flag_file)) { $this->error('[WARNING] No migration session in progress!'); $this->info('Run "php artisan migrate:begin" to start a migration session.'); return 1; } // Check if backup exists if (!is_dir($this->backup_dir)) { $this->error('[ERROR] Backup directory not found!'); $this->info('The backup may have been corrupted or deleted.'); $this->warn('You may need to manually restore from a different backup.'); // Remove flag file since backup is gone unlink($this->flag_file); return 1; } $this->warn('[WARNING] Restoring database to the snapshot taken at migrate:begin.'); $this->info(' Starting database rollback...'); try { // Step 1: Stop MySQL using supervisorctl $this->info('[1] Stopping MySQL server...'); $this->shell_exec_privileged('supervisorctl stop mysql 2>&1'); // Wait a moment for process to die sleep(3); // Step 2: Clear current MySQL data $this->info('[2] Clearing current database data...'); // Use shell_exec to clear directory contents instead of removing directory $this->shell_exec_privileged("rm -rf {$this->mysql_data_dir}/* 2>/dev/null"); $this->shell_exec_privileged("rm -rf {$this->mysql_data_dir}/.* 2>/dev/null"); // Step 3: Restore backup $this->info('[3] Restoring database snapshot...'); $this->shell_exec_privileged("cp -r {$this->backup_dir}/* {$this->mysql_data_dir}/"); $this->shell_exec_privileged("cp -r {$this->backup_dir}/.[^.]* {$this->mysql_data_dir}/ 2>/dev/null"); // Step 4: Fix permissions (MySQL needs to own the files) $this->info('[4] Setting correct permissions...'); $this->run_privileged_command(['chown', '-R', 'mysql:mysql', $this->mysql_data_dir]); // Step 5: Start MySQL using supervisorctl $this->info('[5] Starting MySQL server...'); $this->shell_exec_privileged('mkdir -p /var/run/mysqld'); $this->shell_exec_privileged('supervisorctl start mysql 2>&1'); // Step 6: Wait for MySQL to be ready $this->info('[6] Waiting for MySQL to be ready...'); $this->wait_for_mysql_ready(); // Step 7: Keep backup and flag - stay in migration mode $this->info('[7] Rollback complete...'); // Success $this->info(''); $this->info('[OK] Database successfully rolled back to snapshot!'); $this->info(''); $this->line('The database has been restored to the snapshot state.'); $this->warn('[WARNING] You are still in migration mode with the same snapshot.'); $this->info(''); $this->line('Your options:'); $this->line(' • Fix your migration files and run "php artisan migrate" to try again'); $this->line(' • Run "php artisan migrate:commit" to exit migration mode'); $this->info(''); $this->line('The snapshot remains available for additional rollbacks if needed.'); return 0; } catch (\Exception $e) { $this->error('[ERROR] Rollback failed: ' . $e->getMessage()); $this->error('Manual intervention may be required!'); // Try to restart MySQL $this->info('Attempting to restart MySQL...'); $this->shell_exec_privileged('supervisorctl start mysql 2>&1'); return 1; } } /** * Wait for MySQL to be ready for connections */ protected function wait_for_mysql_ready(): void { $max_attempts = 120; $attempt = 0; while ($attempt < $max_attempts) { // Use mysql client to test connection (similar to user's approach) $result = shell_exec("echo \"SELECT 'test';\" | mysql -urspade -prspadepass 2>/dev/null | grep test"); if ($result !== null && str_contains($result, 'test')) { return; } sleep(1); $attempt++; } throw new \Exception('MySQL did not start within 120 seconds'); } }