getMessage(), $text), $text); } }; $command = escapeshellarg(PHP_BINARY).' '.escapeshellarg($root.'/scripts/prepare-sgf-operational.php').' '.escapeshellarg($host); exec($command, $output, $exit); if ($exit !== 0) { throw new RuntimeException('Prepare failed'); } $bundle = json_decode(implode("\n", $output), true, 16, JSON_THROW_ON_ERROR)['directory']; $manifest = json_decode(file_get_contents($bundle.'/manifest.json'), true, 32, JSON_THROW_ON_ERROR); $hash = hash_file('sha256', $bundle.'/manifest.json'); $make = function () use ($host, $manifest) { $copy = sys_get_temp_dir().'/sgf-code-deploy-'.bin2hex(random_bytes(6)); mkdir($copy, 0700); foreach ($manifest['host_overlays'] as $file => $values) { if (! is_dir(dirname($copy.'/'.$file))) { mkdir(dirname($copy.'/'.$file), 0700, true); }copy($host.'/'.$file, $copy.'/'.$file); } mkdir($copy.'/packages/purchasing', 0700, true); copy($host.'/packages/purchasing/current.json', $copy.'/packages/purchasing/current.json'); mkdir($copy.'/bootstrap/cache', 0700, true); mkdir($copy.'/storage/framework', 0700, true); file_put_contents($copy.'/bootstrap/cache/config.php', "true];"); file_put_contents($copy.'/bootstrap/cache/routes-v7.php', ' new CodeDeployment(''), 'Host explícito obrigatório'); $deny(fn () => $deploy->plan('', $hash), 'Bundle explícito obrigatório'); $plan = $deploy->plan($bundle, $hash); $check(! $plan['writes'] && ! is_dir($copy.'/storage/app'), 'plan is read-only'); $deny(fn () => $deploy->plan($bundle, str_repeat('0', 64)), 'Manifesto divergente'); file_put_contents($copy.'/.env', 'APP_CONFIG_CACHE=storage/framework/custom.php'); $deny(fn () => $deploy->plan($bundle, $hash), 'Cache Laravel personalizado'); unlink($copy.'/.env'); $deny(fn () => $deploy->apply($bundle, $hash, $plan['target_sha256'], 'Synthetic operator', true), 'manutenção activa'); file_put_contents($copy.'/storage/framework/down', '{"status":503}'); $deny(fn () => $deploy->apply($bundle, $hash, $plan['target_sha256'], 'Synthetic operator', false), 'serviços de escrita parados'); $deny(fn () => $deploy->apply($bundle, $hash, str_repeat('0', 64), 'Synthetic operator', true), 'Destino de código divergente'); $beforeCache = file_get_contents($copy.'/bootstrap/cache/config.php'); $result = $deploy->apply($bundle, $hash, $plan['target_sha256'], 'Synthetic operator', true); $id = $result['deployment_id']; $check($result['state'] === 'applied' && ! $result['activation_performed'] && is_file($copy.'/storage/framework/down'), 'applied offline without activation'); $check(! is_file($copy.'/bootstrap/cache/config.php') && ! is_file($copy.'/bootstrap/cache/routes-v7.php') && is_file($copy.'/bootstrap/cache/unrelated.php'), 'invalidates known Laravel caches without touching unrelated files'); foreach ($manifest['host_overlays'] as $file => $values) { $check(hash_file('sha256', $copy.'/'.$file) === $values['after_sha256'], 'applied verified overlay '.$file); } $check($deploy->apply($bundle, $hash, $plan['target_sha256'], 'Synthetic operator', true)['replayed'], 'apply retry does not duplicate installation'); $file = array_key_first($manifest['host_overlays']); $applied = file_get_contents($copy.'/'.$file); file_put_contents($copy.'/'.$file, $applied."\n// local change\n"); $deny(fn () => $deploy->rollback($id, 'Synthetic operator', true), 'Código local alterado'); $check(is_dir($copy.'/packages/internal-requisitions'), 'refused rollback preserves bundle and local code'); file_put_contents($copy.'/'.$file, $applied); file_put_contents($copy.'/bootstrap/cache/config.php', 'true];'); $deny(fn () => $deploy->rollback($id, 'Synthetic operator', true), 'Cache recriado'); unlink($copy.'/bootstrap/cache/config.php'); $rollback = $deploy->rollback($id, 'Synthetic operator', true); $check($rollback['state'] === 'rolled-back' && ! $rollback['data_schema_changed'] && is_file($copy.'/storage/framework/down'), 'rollback preserves maintenance and never touches data schema'); $check(file_get_contents($copy.'/bootstrap/cache/config.php') === $beforeCache, 'rollback restores exact previous cache'); foreach ($manifest['host_overlays'] as $file => $values) { $check(hash_file('sha256', $copy.'/'.$file) === $values['before_sha256'], 'restored original overlay '.$file); } $check(! is_dir($copy.'/packages/internal-requisitions') && is_dir($copy.'/storage/app/private/module-deployments/'.$id.'/withdrawn'), 'withdrawn bundle preserved for diagnosis'); $check(file_get_contents($copy.'/packages/purchasing/current.json') === $pointer, 'clone remains on original package pointer'); $failed = $make(); file_put_contents($failed.'/storage/framework/down', '{"status":503}'); $recover = new CodeDeployment($failed); $plan2 = $recover->plan($bundle, $hash); $deny(fn () => $recover->apply($bundle, $hash, $plan2['target_sha256'], 'Synthetic failure', true, function ($step) { if (str_starts_with($step, 'file:')) { throw new RuntimeException('Injected deployment interruption'); } }), 'Injected deployment interruption'); $check(is_file($failed.'/storage/framework/down'), 'interruption retains maintenance'); $recover->rollback($id, 'Synthetic recovery', true); foreach ($manifest['host_overlays'] as $file => $values) { $check(hash_file('sha256', $failed.'/'.$file) === $values['before_sha256'], 'interrupted deployment recovers '.$file); } $check(file_get_contents($host.'/packages/purchasing/current.json') === $pointer, 'destination pointer untouched'); $switched = $make(); file_put_contents($switched.'/storage/framework/down', '{"status":503}'); $switchDeployment = new CodeDeployment($switched); $switchPlan = $switchDeployment->plan($bundle, $hash); $deny(fn () => $switchDeployment->apply($bundle, $hash, $switchPlan['target_sha256'], 'Synthetic commit failure', true, function ($step) { if ($step === 'bundle-switched') { throw new RuntimeException('Injected post-switch interruption'); } }), 'Injected post-switch interruption'); $switchDeployment->rollback($id, 'Synthetic post-switch recovery', true); $check(! is_dir($switched.'/packages/internal-requisitions') && is_file($switched.'/storage/framework/down'), 'post-switch interruption recovers without exposing partial code'); $lockPath = $switched.'/storage/app/private/module-deployments/deployment.lock'; $lock = fopen($lockPath, 'c+'); flock($lock, LOCK_EX); $deny(fn () => $switchDeployment->rollback($id, 'Synthetic concurrent recovery', true), 'Outra implantação em curso'); flock($lock, LOCK_UN); fclose($lock); $cliCopy = $make(); file_put_contents($cliCopy.'/storage/framework/down', '{"status":503}'); $cli = function (array $args) use ($bundle) { $command = implode(' ', array_map('escapeshellarg', [PHP_BINARY, $bundle.'/scripts/sgf-code-deploy.php', ...$args])); $rows = []; exec($command, $rows, $exit); if ($exit !== 0) { throw new RuntimeException('Code deployment CLI failed.'); } return json_decode(implode("\n", $rows), true, 32, JSON_THROW_ON_ERROR); }; $cliPlan = $cli(['plan', $cliCopy, $bundle, $hash]); $check(! $cliPlan['writes'], 'CLI plan verifies bundle without writes'); $cliApplied = $cli(['apply', $cliCopy, $bundle, $hash, $cliPlan['target_sha256'], 'Synthetic CLI operator', '--offline']); $check($cliApplied['state'] === 'applied' && $cliApplied['maintenance_preserved'], 'CLI apply leaves services and maintenance under operator control'); $cliRolled = $cli(['rollback', $cliCopy, $cliApplied['deployment_id'], 'Synthetic CLI recovery', '--offline']); $check($cliRolled['state'] === 'rolled-back' && file_get_contents($cliCopy.'/packages/purchasing/current.json') === $pointer, 'CLI rollback preserves 0.1.1 pointer'); $proof = ['passed' => true, 'checks' => $checks, 'candidate' => $bundle, 'clones' => [$copy, $failed, $switched, $cliCopy], 'checked_at' => gmdate('c'), 'destination_version' => '0.1.1', 'destination_applied' => false, 'old_suites_repeated' => false, 'database_opened' => false, 'services_restarted' => false]; file_put_contents($root.'/docs/proofs/sgf-code-deploy-delivery.json', json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); echo json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)."\n";