Files
compras/scripts/sgf-internal-install.php
T
2026-11-02 08:08:05 +02:00

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);
}