72 lines
3.6 KiB
PHP
72 lines
3.6 KiB
PHP
<?php
|
|
|
|
// Delimited replay of accepted territorial relations, not a new publication.
|
|
declare(strict_types=1);
|
|
use App\Services\ApprovedAnchorTerritories;
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
ini_set('zend.exception_ignore_args', '1');
|
|
require getcwd().'/vendor/autoload.php';
|
|
$app = require getcwd().'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
$args = getopt('', ['qa', 'apply', 'output:', 'preview-sha256:', 'backup:']);
|
|
$qa = isset($args['qa']);
|
|
$apply = isset($args['apply']);
|
|
$expected = $qa ? 'simulator_fiscal_delta_20261021_test' : 'teste_investidor';
|
|
$port = $qa ? 55441 : 5432;
|
|
$identity = DB::selectOne('select current_database() as name,inet_server_port() as port');
|
|
if ($identity->name !== $expected || (int) $identity->port !== $port) {
|
|
throw new RuntimeException('Named isolated QA or homologation required.');
|
|
}
|
|
if (! $qa && (! getenv('SSH_CONNECTION') || ! function_exists('posix_geteuid') || posix_geteuid() !== 0 || rtrim(config('app.url'), '/') !== 'https://teste-investidor.tempo.co.mz')) {
|
|
throw new RuntimeException('Authenticated destination operator required.');
|
|
}
|
|
$output = $args['output'] ?? '';
|
|
if (! $output || is_file($output) || str_contains(str_replace('\\', '/', $output), '/public/')) {
|
|
throw new RuntimeException('New private receipt path required.');
|
|
}
|
|
$service = app(ApprovedAnchorTerritories::class);
|
|
DB::beginTransaction();
|
|
try {
|
|
DB::statement($apply ? 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE' : 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY');
|
|
if ($apply) {
|
|
DB::select('select pg_advisory_xact_lock(231026,28)');
|
|
}
|
|
$plan = $service->plan();
|
|
$hash = hash('sha256', json_encode([$expected, $plan], JSON_THROW_ON_ERROR));
|
|
$result = ['mode' => $apply ? 'applied' : 'preview', 'database' => $expected, 'port' => $port, 'qa' => $qa, 'manifest_sha256' => ApprovedAnchorTerritories::HASH, 'preview_sha256' => $hash, 'relations' => count($plan['links']), 'projects' => count($plan['projects'])];
|
|
if ($apply) {
|
|
if (! hash_equals($hash, $args['preview-sha256'] ?? '')) {
|
|
throw new RuntimeException('Destination changed after preview.');
|
|
}
|
|
if (! $qa) {
|
|
$directory = $args['backup'] ?? '';
|
|
$backup = json_decode(file_get_contents($directory.'/backup-receipt.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
$restore = json_decode(file_get_contents($directory.'/restore-receipt.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
if (! $restore['restore_verified'] || $backup['target']['database'] !== $expected || (int) $backup['target']['port'] !== $port
|
|
|| ! hash_equals($backup['sha256'], $restore['source_dump_sha256']) || ! hash_equals($backup['sha256'], hash_file('sha256', $backup['dump_path']))) {
|
|
throw new RuntimeException('Matching backup and isolated restoration required.');
|
|
}
|
|
}
|
|
file_put_contents($output.'.before.json', json_encode($plan, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
|
|
if (! $qa) {
|
|
chmod($output.'.before.json', 0600);
|
|
}
|
|
$result += $service->apply($plan);
|
|
DB::commit();
|
|
} else {
|
|
DB::rollBack();
|
|
}
|
|
file_put_contents($output, json_encode([...$result, 'plan' => $plan], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
|
|
if (! $qa) {
|
|
chmod($output, 0600);
|
|
}
|
|
echo json_encode($result, JSON_THROW_ON_ERROR);
|
|
} catch (Throwable $error) {
|
|
if (DB::transactionLevel()) {
|
|
DB::rollBack();
|
|
}
|
|
throw $error;
|
|
}
|