1
0
cms11/app/Console/Commands/Bundle/Render.php

300 lines
7.8 KiB
PHP

<?php
namespace App\Console\Commands\Bundle;
use App\Classes\Bundle;
use App\Console\Commands\Bundle\Traits\ReadsBundles;
use App\Console\Commands\Bundle\Traits\SelectsDisks;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Str;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\progress;
class Render extends Command
{
use ReadsBundles;
use SelectsDisks;
/**
* The console command description.
*
* @var string
*/
protected $description = 'Render bundles';
public function __construct()
{
$this->signature = 'bundle:render
{ --a|assets : Rebuild assets - implies <info>-c</info> }
{ --clean-target : Remove all files and directories before rendering (excluding assets and storage) }
{ --c|clear-cache : Clear cache before rendering }
{ --deploy : Deploy without asking }
{ --describe : Ask for missing descriptions }
{ --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 <info>' . env('CONTENT_DISK') . '</info> }
{ --target-disk= : Use specified rendering disk - Defaults to <info>' . env('DIST_DISK') . '</info> }
{ --validate : Perform validations before rendering }
{ path? : Path to a specific bundle to render - Default to <info>/</info> }
';
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->showDisclaimer()
->selectDisk(true, true)
->cleanTarget()
->renderAssets()
->clearCache()
->validate()
->renderFeed()
->selectBundles()
->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;
}
/**
* Remove previously rendered files before rendering new files
*/
private function cleanTarget(): self
{
if (!$this->option('clean-target')) {
return $this;
}
$this->output->write('Cleaning target... ');
$list = $this->targetDisk->listContents($this->argument('path') ?? '/')->toArray();
if (!$this->option('dry-run')) {
foreach ($list as $attrs) {
if ($attrs->isFile()) {
$this->targetDisk->delete($attrs->path());
} else {
$this->targetDisk->deleteDirectory($attrs->path());
}
}
}
$this->info('OK');
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->callSilently('cache:clear');
$this->callSilently('view:clear');
}
$this->info('OK');
return $this;
}
/**
* Validate bundles before rendering
*/
private function validate(): self
{
if (!$this->option('validate')) {
return $this;
}
$options = [
'--recursive' => $this->option('recursive'),
'--source-disk' => $this->option('source-disk'),
'path' => $this->argument('path') ?? '/',
];
if (!$this->option('dry-run')) {
$result = $this->call('bundle:validate', $options);
}
if (!empty($result)) {
if (confirm('Validation errors have occurred. Cancel process?')) {
exit(1);
}
}
return $this;
}
/**
* Render feed
*/
private function renderFeed(): self
{
if ($this->option('dry-run')) {
return $this;
}
$result = Bundle::renderFeed($this->sourceDisk);
foreach ($result as $path => $content) {
$this->output->write(sprintf('Rendering <info>%s</info> 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);
//TODO: Find the right way to check for feed's validity...
Cache::forever('feed_is_valid', true);
$this->info('OK');
}
return $this;
}
/**
* Perform rendering
*/
private function render(): self
{
$bundles = $this->bundles;
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');
}
}