signature = 'bundle:render { --a|assets : Rebuild assets - implies -c } { --c|clear-cache : Clear cache before rendering } { --deploy : Deploy without asking } { --d|dry-run : Show what would be done but do nothing } { --no-deploy : Do not deploy, do not ask to deploy } { --r|recursive : Also render sub-bundles } { --source-disk= : Use specified content disk - Defaults to ' . env('CONTENT_DISK') . ' } { --target-disk= : Use specified rendering disk - Defaults to ' . env('DIST_DISK') . ' } { --validate : Perform validations before rendering } { path? : Path to a specific bundle to render - Default to / } '; parent::__construct(); } /** * Execute the console command. */ public function handle() { $this->showDisclaimer() ->selectDisk() ->renderAssets() ->clearCache() ->validate() ->renderFeed() ->render() ->deploy(); } /** * Show a "disclaimer" for running dry-mode */ private function showDisclaimer(): self { if (!$this->option('dry-run')) { return $this; } $this->newLine(); $this->warn('Running dry-mode: no modification will be done'); $this->newLine(); return $this; } /** * Select the disk we will be working on */ private function selectDisk(): self { $sourceDisk = $this->option('source-disk') ?? env('CONTENT_DISK'); $targetDisk = $this->option('target-disk') ?? env('DIST_DISK'); $this->sourceDisk = Storage::disk($sourceDisk); $this->targetDisk = Storage::disk($targetDisk); $this->line( sprintf( 'Using %s as source disk and %s as target disk for generated content', $sourceDisk, $targetDisk ) ); return $this; } /** * Build assets, and clear cache afterwise */ private function renderAssets(): self { // Do not re-generate assets if we didn't explicitely asked for it if (!$this->option('assets')) { return $this; } $this->output->write('Building assets... '); if (!$this->option('dry-run')) { Process::run('npm run build')->throw(); } $this->info('OK'); return $this; } /** * Clear application cache */ private function clearCache(): self { // Do not clear cache if we didn't explicitely asked for it if (!$this->option('clear-cache') && !$this->option('assets')) { return $this; } // For now, we need to clear the cache, otherwise rendered HTML will // still reference old assets $this->output->write('Clearing cache... '); if (!$this->option('dry-run')) { $this->call('cache:clear', ['--quiet']); } $this->info('OK'); return $this; } private function validate(): self { if (!$this->option('validate')) { return $this; } $result = $this->call('bundle:validate', ['--recursive' => true]); if (!empty($result)) { if (confirm('Validation errors have occurred. Cancel process?')) { exit(1); } } return $this; } /** * Collect a list of bundles to render */ private function getBundles() { $path = $this->argument('path') ?? '/'; $comment = sprintf('Rendering %s', $path); if ($this->option('recursive')) { $comment .= ' and all sub-bundles'; } $this->line($comment); $this->output->write('Collecting bundles... '); if ($this->option('recursive')) { $bundles = Bundle::findBundles($this->sourceDisk, $path, true, false); } else { $bundles = [new Bundle($path, $this->sourceDisk)]; } $this->info('OK'); return $bundles; } private function renderFeed(): self { $result = Bundle::renderFeed($this->sourceDisk); foreach ($result as $path => $content) { $this->output->write(sprintf('Rendering %s as feed... ', $path)); if ($this->targetDisk->exists($path)) { $fileChecksum = $this->targetDisk->checksum($path); $contentChecksum = md5($content); if ($fileChecksum === $contentChecksum) { $this->info('Not changed'); continue; } } $this->targetDisk->put($path, $content); $this->info('OK'); } return $this; } /** * Perform rendering */ private function render(): self { $bundles = $this->getBundles(); progress( label: 'Rendering... ', steps: $bundles, callback: fn (Bundle $bundle, $progress) => $this->handleBundle($bundle, $progress) ); return $this; } /** * Handle specific bundle */ private function handleBundle(Bundle $bundle, $progress) { $progress->label(sprintf('Rendering %s ...', $bundle->getPath())); if ($this->option('dry-run')) { return; } $result = $bundle->render(); foreach ($result as $path => $content) { if (!Str::contains($path, '.')) { $path = Str::finish($path, '/') . 'index.html'; } if ($this->targetDisk->exists($path)) { $fileChecksum = $this->targetDisk->checksum($path); $contentChecksum = md5($content); if ($fileChecksum !== $contentChecksum) { continue; } } $this->targetDisk->put($path, $content); } } /** * Deploy to remote server */ private function deploy() { // Do not even ask if --no-deploy was used if ($this->option('no-deploy')) { return; } if (!$this->option('deploy')) { // Ask for confirmation if (!confirm('Ready to deploy?')) { $this->comment('OK, not deploying!'); return; } } $source = Str::finish($this->targetDisk->path('/'), '/'); $target = env('DEPLOY_TARGET'); $command = __(env('DEPLOY_COMMAND'), [ 'source' => $source, 'target' => $target, ]); $this->output->write(sprintf('Deploying using `%s` ... ', $command)); if (!$this->option('dry-run')) { Process::run($command)->throw(); } $this->info('OK'); } }