info('๐Ÿš€ Starting NNTmux update process...'); // Initialize progress tracking $totalSteps = $this->calculateTotalSteps(); $this->progressBar = $this->output->createProgressBar($totalSteps); $this->progressBar->start(); try { // Prepare environment $this->prepareEnvironment(); // Execute update steps $this->executeUpdateSteps(); // Finalize $this->finalizeUpdate(); $this->progressBar->finish(); $this->newLine(2); $this->info('โœ… NNTmux update completed successfully!'); return Command::SUCCESS; } catch (\Exception $e) { $this->progressBar->finish(); $this->newLine(2); $this->error('โŒ Update failed: '.$e->getMessage()); $this->restoreEnvironment(); return Command::FAILURE; } } /** * Calculate total steps for progress tracking */ private function calculateTotalSteps(): int { $steps = 3; // prepare, finalize, cleanup if (! $this->option('skip-git')) { $steps++; } if (! $this->option('skip-composer')) { $steps++; } if (! $this->option('skip-npm')) { $steps += 2; } // install + build if (! $this->option('skip-db')) { $steps++; } $steps++; // smarty cache clear $steps++; // env merge return $steps; } /** * Prepare the environment for updates */ private function prepareEnvironment(): void { $this->info('๐Ÿ”ง Preparing environment...'); // Check if app is in maintenance mode $this->wasInMaintenance = App::isDownForMaintenance(); if (! $this->wasInMaintenance) { $this->call('down', [ '--render' => 'errors::maintenance', '--retry' => 120, '--secret' => config('app.key'), ]); } // Check if tmux is running $tmux = new Tmux; $this->tmuxWasRunning = $tmux->isRunning(); if ($this->tmuxWasRunning) { $this->call('tmux-ui:stop', ['--kill' => true]); } $this->progressBar->advance(); } /** * Execute the main update steps */ private function executeUpdateSteps(): void { // Git operations if (! $this->option('skip-git')) { $this->performGitUpdate(); } // Composer operations if (! $this->option('skip-composer')) { $this->performComposerUpdate(); } // Database migrations if (! $this->option('skip-db')) { $this->performDatabaseUpdate(); } // NPM operations if (! $this->option('skip-npm')) { $this->performNpmOperations(); } // Clear caches and perform maintenance $this->performMaintenanceTasks(); } /** * Perform git update with better error handling */ private function performGitUpdate(): void { $this->info('๐Ÿ“ฅ Updating from git repository...'); $gitResult = $this->call('nntmux:git'); if ($gitResult !== 0) { throw new \Exception('Git update failed'); } $this->progressBar->advance(); } /** * Perform composer update with optimization */ private function performComposerUpdate(): void { $this->info('๐Ÿ“ฆ Updating composer dependencies...'); $composerResult = $this->call('nntmux:composer'); if ($composerResult !== 0) { throw new \Exception('Composer update failed'); } $this->progressBar->advance(); } /** * Perform database updates */ private function performDatabaseUpdate(): void { $this->info('๐Ÿ—„๏ธ Updating database...'); $dbResult = $this->call('nntmux:db'); if ($dbResult !== 0) { throw new \Exception('Database update failed'); } $this->progressBar->advance(); } /** * Perform NPM operations with parallel processing where possible */ private function performNpmOperations(): void { // Check if package.json has changed $packageLockExists = File::exists(base_path('package-lock.json')); $shouldInstall = ! $packageLockExists || $this->option('force'); if ($shouldInstall) { $this->info('๐Ÿ“ฆ Installing npm packages...'); $process = Process::timeout(600) ->path(base_path()) ->run('npm ci --silent'); if (! $process->successful()) { // Fallback to npm install if ci fails $process = Process::timeout(600) ->path(base_path()) ->run('npm install --silent'); if (! $process->successful()) { throw new \Exception('NPM install failed: '.$process->errorOutput()); } } } $this->progressBar->advance(); // Build assets $this->info('๐Ÿ”จ Building assets...'); $buildProcess = Process::timeout(600) ->path(base_path()) ->run('npm run build'); if (! $buildProcess->successful()) { throw new \Exception('Asset build failed: '.$buildProcess->errorOutput()); } $this->progressBar->advance(); } /** * Perform maintenance tasks */ private function performMaintenanceTasks(): void { // Clear Smarty cache $this->info('๐Ÿงน Clearing Smarty cache...'); $this->clearSmartyCache(); $this->progressBar->advance(); // Merge environment variables $this->info('โš™๏ธ Merging environment configuration...'); $this->mergeEnvironmentConfig(); $this->progressBar->advance(); } /** * Clear Smarty compiled templates */ private function clearSmartyCache(): void { try { $smarty = new Smarty; $smarty->setCompileDir(config('ytake-laravel-smarty.compile_path')); if ($smarty->clearCompiledTemplate()) { $this->line(' โœ“ Smarty compiled template cache cleared'); } else { $this->warn(' โš  Could not clear Smarty cache automatically'); $this->line(' Please clear manually: '.config('ytake-laravel-smarty.compile_path')); } } catch (\Exception $e) { $this->warn(' โš  Smarty cache clear failed: '.$e->getMessage()); } } /** * Merge environment configuration with improved error handling */ private function mergeEnvironmentConfig(): void { try { $envExamplePath = base_path('.env.example'); $envPath = base_path('.env'); if (! File::exists($envExamplePath)) { $this->warn(' โš  .env.example not found, skipping environment merge'); return; } if (! File::exists($envPath)) { $this->warn(' โš  .env not found, skipping environment merge'); return; } $envExampleVars = $this->parseEnvFile($envExamplePath); $envVars = $this->parseEnvFile($envPath); $missingKeys = array_diff_key($envExampleVars, $envVars); if (empty($missingKeys)) { $this->line(' โœ“ No new environment variables to merge'); return; } $this->addMissingEnvVars($envPath, $missingKeys); $this->line(' โœ“ Merged '.count($missingKeys).' new environment variables'); } catch (\Exception $e) { $this->warn(' โš  Environment merge failed: '.$e->getMessage()); } } /** * Parse environment file into key-value pairs */ private function parseEnvFile(string $path): array { $content = File::get($path); $vars = []; foreach (preg_split("/\r\n|\n|\r/", $content) as $line) { $line = trim($line); if (empty($line) || str_starts_with($line, '#')) { continue; } if (preg_match('/^([^=]+)=(.*)$/', $line, $matches)) { $key = trim($matches[1]); $value = $matches[2]; $vars[$key] = $value; } } return $vars; } /** * Add missing environment variables to .env file */ private function addMissingEnvVars(string $envPath, array $missingKeys): void { $content = File::get($envPath); if (! str_ends_with($content, "\n")) { $content .= "\n"; } $content .= "\n# New settings added from .env.example on ".now()->toDateTimeString()."\n"; foreach ($missingKeys as $key => $value) { $content .= "$key=$value\n"; } File::put($envPath, $content); } /** * Finalize the update process */ private function finalizeUpdate(): void { $this->info('๐Ÿ Finalizing update...'); // Clear application caches Cache::flush(); // Restore application state $this->restoreEnvironment(); $this->progressBar->advance(); } /** * Restore the original environment state */ private function restoreEnvironment(): void { // Restore maintenance mode state if (! $this->wasInMaintenance && App::isDownForMaintenance()) { $this->call('up'); } // Restore tmux state if ($this->tmuxWasRunning) { $this->call('tmux-ui:start'); } } }