43 lines
2.0 KiB
PHP
43 lines
2.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Explicit migration runner. It never drops a database and does not run from
|
|
* HTTP. Usage: php backend/scripts/run_migrations.php [--dry-run|--status].
|
|
*/
|
|
require_once dirname(__DIR__) . '/app/bootstrap.php';
|
|
|
|
use App\Core\Database;
|
|
|
|
$mode = in_array('--status', $argv, true) ? 'status' : (in_array('--dry-run', $argv, true) ? 'dry_run' : 'apply');
|
|
$dir = dirname(__DIR__) . '/migrations';
|
|
$files = glob($dir . '/*.sql') ?: [];
|
|
sort($files, SORT_STRING);
|
|
$pdo = Database::getConnection();
|
|
$pdo->exec("CREATE TABLE IF NOT EXISTS schema_migrations (filename VARCHAR(255) NOT NULL PRIMARY KEY, sha256 CHAR(64) NOT NULL, applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
$applied = Database::select('SELECT filename, sha256, applied_at FROM schema_migrations ORDER BY filename');
|
|
$known = array_column($applied, null, 'filename');
|
|
|
|
foreach ($files as $file) {
|
|
$name = basename($file); $sha = hash_file('sha256', $file);
|
|
if (isset($known[$name])) {
|
|
if (!hash_equals((string)$known[$name]['sha256'], $sha)) { fwrite(STDERR, "REFUSE {$name}: applied migration checksum changed.\n"); exit(2); }
|
|
echo "APPLIED {$name} {$known[$name]['applied_at']}\n"; continue;
|
|
}
|
|
if ($mode === 'status') { echo "PENDING {$name}\n"; continue; }
|
|
if ($mode === 'dry_run') { echo "WOULD APPLY {$name}\n"; continue; }
|
|
$sql = file_get_contents($file);
|
|
if ($sql === false || trim($sql) === '') { fwrite(STDERR, "REFUSE {$name}: unreadable or empty.\n"); exit(2); }
|
|
echo "APPLYING {$name}\n";
|
|
try {
|
|
// MySQL DDL may commit implicitly. The migration is recorded only after
|
|
// its complete SQL succeeds; repair any partial DDL manually before retry.
|
|
$pdo->exec($sql);
|
|
Database::insert('INSERT INTO schema_migrations (filename, sha256) VALUES (?, ?)', [$name, $sha]);
|
|
echo "APPLIED {$name}\n";
|
|
} catch (Throwable $e) {
|
|
fwrite(STDERR, "FAILED {$name}: {$e->getMessage()}\n");
|
|
exit(1);
|
|
}
|
|
}
|