Add 100+ automated unit tests from .expect file specifications Add session system test Add rsx:constants:regenerate command test Add rsx:logrotate command test Add rsx:clean command test Add rsx:manifest:stats command test Add model enum system test Add model mass assignment prevention test Add rsx:check command test Add migrate:status command test 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
166 lines
6.1 KiB
PHP
166 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* CODING CONVENTION:
|
|
* This file follows the coding convention where variable_names and function_names
|
|
* use snake_case (underscore_wherever_possible).
|
|
*/
|
|
|
|
namespace App\RSpade\Commands\Rsx;
|
|
|
|
use App\Console\Commands\FrameworkDeveloperCommand;
|
|
use App\RSpade\Core\Manifest\Manifest;
|
|
|
|
class Manifest_Build_Command extends FrameworkDeveloperCommand
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'rsx:manifest:build
|
|
{--force : Execute rsx:clean then rsx:manifest:build via PHP exec}
|
|
{--clean : Clear all caches before building (forces complete rebuild)}
|
|
{--build-debug : Enable verbose build output (shows file-by-file processing)}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Build or rebuild the RSX manifest (incremental by default, use --clean for full rebuild)';
|
|
|
|
/**
|
|
* Create a new command instance
|
|
*/
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
|
|
// Check if --build-debug flag is present in command line args
|
|
// Define BUILD_DEBUG_MODE early so it's set before service providers boot
|
|
if (in_array('--build-debug', $_SERVER['argv'] ?? [])) {
|
|
define('BUILD_DEBUG_MODE', true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function handle()
|
|
{
|
|
// Prevent being called via $this->call() - must use passthru for fresh process
|
|
$this->prevent_call_from_another_command();
|
|
|
|
// Handle --force flag: complete clean and rebuild via exec
|
|
if ($this->option('force')) {
|
|
// Execute rsx:clean
|
|
passthru('php artisan rsx:clean');
|
|
|
|
// Execute rsx:manifest:build without --force to avoid recursion
|
|
passthru('php artisan rsx:manifest:build');
|
|
|
|
// Immediately exit
|
|
die();
|
|
}
|
|
|
|
// Check if in production mode with existing manifest
|
|
if (config('app.env') === 'production') {
|
|
$manifest_file = base_path('storage/rsx-build/manifest_data.php');
|
|
|
|
if (file_exists($manifest_file)) {
|
|
$file_age = time() - filemtime($manifest_file);
|
|
|
|
// If manifest exists and is older than 5 seconds, block rebuild
|
|
if ($file_age > 5) {
|
|
$this->error('Production manifest rebuild blocked');
|
|
$this->line('');
|
|
$this->line('The manifest file exists in production and is ' . round($file_age / 60, 1) . ' minutes old.');
|
|
$this->line('Rebuilding the manifest in production is not permitted without explicit confirmation.');
|
|
$this->line('');
|
|
$this->line('Use --force flag to override this protection and rebuild anyway.');
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
$start_time = microtime(true);
|
|
|
|
// Reset debug options
|
|
Manifest::$_debug_options = [];
|
|
|
|
// Handle --clean flag: clear caches first
|
|
if ($this->option('clean')) {
|
|
$this->info('🧹 Clearing caches before build...');
|
|
// Clear RSX caches (but not persistent directory)
|
|
passthru('php artisan rsx:clean');
|
|
Manifest::clear();
|
|
}
|
|
|
|
$this->info('Building RSX manifest...');
|
|
|
|
// Default behavior: incremental build (don't clear unless --clean was used)
|
|
// Manifest::init() handles everything - loading cache and doing incremental updates
|
|
Manifest::init();
|
|
$stats = Manifest::get_stats();
|
|
|
|
$elapsed = round((microtime(true) - $start_time) * 1000, 2);
|
|
|
|
$this->info('Manifest built successfully!');
|
|
$this->line('');
|
|
$this->line('Summary:');
|
|
$this->line(' Build Hash: ' . Manifest::get_build_key());
|
|
$this->line(' Total Files: ' . $stats['total_files']);
|
|
$this->line(' PHP Classes: ' . $stats['php']);
|
|
$this->line(' JS Classes: ' . $stats['js']);
|
|
$this->line(' Blade Views: ' . $stats['blade']);
|
|
$this->line(' Build Time: ' . $elapsed . 'ms');
|
|
|
|
// Reset debug options
|
|
Manifest::$_debug_options = [];
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Prevent this command from being called via $this->call() from another command
|
|
*
|
|
* This command MUST run in a fresh process to ensure in-memory caches are cleared.
|
|
* Use passthru() instead of $this->call() when calling from other commands.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function prevent_call_from_another_command()
|
|
{
|
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 20);
|
|
|
|
foreach ($trace as $frame) {
|
|
// Check if we're being called from Artisan::call() or Command::call()
|
|
if (isset($frame['class']) && isset($frame['function'])) {
|
|
$class = $frame['class'];
|
|
$function = $frame['function'];
|
|
|
|
// Detect $this->call() from another command
|
|
if ($function === 'call' && str_contains($class, 'Command')) {
|
|
$this->error('');
|
|
$this->error('❌ FATAL ERROR: rsx:manifest:build cannot be called via $this->call()');
|
|
$this->error('');
|
|
$this->error('This command MUST run in a fresh process to properly clear in-memory caches.');
|
|
$this->error('');
|
|
$this->error('FIX: Use passthru() instead of $this->call():');
|
|
$this->error('');
|
|
$this->line(' // ❌ WRONG - runs in same process, caches remain in memory');
|
|
$this->line(' $this->call(\'rsx:manifest:build\');');
|
|
$this->error('');
|
|
$this->line(' // ✅ CORRECT - fresh process, all caches cleared');
|
|
$this->line(' passthru(\'php artisan rsx:manifest:build\');');
|
|
$this->error('');
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|