55 lines
2.8 KiB
PHP
55 lines
2.8 KiB
PHP
<?php
|
|
|
|
// Executes only when explicitly invoked through the destination host's pinned PHP.
|
|
// plan is read-only; migrate prepares schema only, never code/route activation or rights.
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Symfony\Component\Process\Process;
|
|
use Tempo\Purchasing\Delivery\Installation;
|
|
|
|
$root = dirname(__DIR__);
|
|
$action = $argv[1] ?? '';
|
|
$host = realpath($argv[2] ?? '');
|
|
if (! in_array($action, ['plan', 'migrate'], true) || ! $host) {
|
|
fwrite(STDERR, "Usage: plan <host> | migrate <host> <target-sha> <backup-file> <backup-sha> <operator> <manifest-sha>\n");
|
|
exit(1);
|
|
}
|
|
try {
|
|
$manifestText = file_get_contents($root.'/manifest.json');
|
|
$manifest = json_decode($manifestText, true, 32, JSON_THROW_ON_ERROR);
|
|
if ($manifest['format'] !== 'tempo.operational-review.v1') {
|
|
throw new RuntimeException('Manifesto de preparação operacional obrigatório.');
|
|
}
|
|
foreach ($manifest['support_files'] as $file => $hash) {
|
|
if (($file !== 'bootstrap.php' && ! preg_match('~^(app|packages|integrations|scripts)/[A-Za-z0-9_./-]+$~D', $file)) || str_contains($file, '..') || is_link($root.'/'.$file) || ! hash_equals($hash, hash_file('sha256', $root.'/'.$file))) {
|
|
throw new RuntimeException('Integridade da entrega divergente.');
|
|
}
|
|
}
|
|
foreach ($manifest['host_overlays'] as $file => $hashes) {
|
|
$hash = hash_file('sha256', $host.'/'.$file);
|
|
if (! in_array($hash, [$hashes['before_sha256'], $hashes['after_sha256']], true)) {
|
|
throw new RuntimeException('Host alterado; reconciliar antes de preparar schema.');
|
|
}
|
|
}
|
|
require $host.'/vendor/autoload.php';
|
|
$app = require $host.'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
require $root.'/integrations/sgf/operational/bootstrap.php';
|
|
$db = DB::connection();
|
|
if ($action === 'plan') {
|
|
echo json_encode(['target_sha256' => Installation::target($db), 'manifest_sha256' => hash('sha256', $manifestText), 'schema_prepared' => $db->getSchemaBuilder()->hasTable('sgc_installations'), 'activated' => false], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)."\n";
|
|
} else {
|
|
$backup = $argv[4] ?? '';
|
|
$backupHash = $argv[5] ?? '';
|
|
if (! is_file($backup) || ! hash_equals($backupHash, hash_file('sha256', $backup)) || ! hash_equals(hash('sha256', $manifestText), $argv[7] ?? '')) {
|
|
throw new RuntimeException('Backup ou manifesto não conferidos.');
|
|
}
|
|
$index = new Process([getenv('PG_RESTORE_BINARY') ?: 'pg_restore', '--list', $backup], null, null, null, 60);
|
|
$index->mustRun();
|
|
echo json_encode(Installation::migrate($db, $argv[3] ?? '', $backupHash, $argv[6] ?? ''), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)."\n";
|
|
}
|
|
} catch (Throwable $error) {
|
|
fwrite(STDERR, $error->getMessage()."\n");
|
|
exit(1);
|
|
}
|