Fix bin/publish: use correct .env path for rspade_system Fix bin/publish script: prevent grep exit code 1 from terminating script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
141 lines
5.4 KiB
PHP
Executable File
141 lines
5.4 KiB
PHP
Executable File
<?php
|
||
|
||
namespace App\RSpade\Commands\Migrate;
|
||
|
||
use Illuminate\Console\Command;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Symfony\Component\Process\Process;
|
||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||
|
||
class Migrate_Rollback_Command extends Command
|
||
{
|
||
protected $signature = 'migrate:rollback';
|
||
protected $description = 'Rollback the database to the snapshot taken at migrate:begin';
|
||
|
||
protected $mysql_data_dir = '/var/lib/mysql';
|
||
protected $backup_dir = '/var/lib/mysql_backup';
|
||
protected $flag_file = '/var/www/html/.migrating';
|
||
|
||
public function handle()
|
||
{
|
||
// Check if in migration mode
|
||
if (!file_exists($this->flag_file)) {
|
||
$this->error('⚠️ 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('❌ 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('⚠️ 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...');
|
||
shell_exec('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
|
||
shell_exec("rm -rf {$this->mysql_data_dir}/* 2>/dev/null");
|
||
shell_exec("rm -rf {$this->mysql_data_dir}/.* 2>/dev/null");
|
||
|
||
// Step 3: Restore backup
|
||
$this->info('3️⃣ Restoring database snapshot...');
|
||
shell_exec("cp -r {$this->backup_dir}/* {$this->mysql_data_dir}/");
|
||
shell_exec("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_command(['chown', '-R', 'mysql:mysql', $this->mysql_data_dir]);
|
||
|
||
// Step 5: Start MySQL using supervisorctl
|
||
$this->info('5️⃣ Starting MySQL server...');
|
||
shell_exec('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('✅ Database successfully rolled back to snapshot!');
|
||
$this->info('');
|
||
$this->line('The database has been restored to the snapshot state.');
|
||
$this->warn('⚠️ 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('❌ Rollback failed: ' . $e->getMessage());
|
||
$this->error('Manual intervention may be required!');
|
||
|
||
// Try to restart MySQL
|
||
$this->info('Attempting to restart MySQL...');
|
||
shell_exec('supervisorctl start mysql 2>&1');
|
||
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Run a shell command and check for errors
|
||
*/
|
||
protected function run_command(array $command, bool $throw_on_error = true): string
|
||
{
|
||
$process = new Process($command);
|
||
$process->setTimeout(60);
|
||
$process->run();
|
||
|
||
if ($throw_on_error && !$process->isSuccessful()) {
|
||
throw new ProcessFailedException($process);
|
||
}
|
||
|
||
return $process->getOutput();
|
||
}
|
||
|
||
/**
|
||
* Wait for MySQL to be ready for connections
|
||
*/
|
||
protected function wait_for_mysql_ready(): void
|
||
{
|
||
$max_attempts = 30;
|
||
$attempt = 0;
|
||
|
||
while ($attempt < $max_attempts) {
|
||
// Use mysql client to test connection (similar to user's approach)
|
||
$result = shell_exec("echo \"SELECT 'test';\" | mysql -uroot 2>/dev/null | grep test");
|
||
|
||
if ($result !== null && str_contains($result, 'test')) {
|
||
return;
|
||
}
|
||
|
||
sleep(1);
|
||
$attempt++;
|
||
}
|
||
|
||
throw new \Exception('MySQL did not start within 30 seconds');
|
||
}
|
||
} |