'testing', 'DB_CONNECTION' => 'pgsql', 'DB_HOST' => '127.0.0.1', 'DB_PORT' => '55441', 'DB_DATABASE' => $target['database'], 'DB_USERNAME' => 'postgres', 'DB_PASSWORD' => '', 'DB_URL' => '', 'CACHE_STORE' => 'array', 'SESSION_DRIVER' => 'array', 'QUEUE_CONNECTION' => 'sync'] as $key => $value) { putenv($key.'='.$value); $_ENV[$key] = $_SERVER[$key] = $value; } require $root.'/vendor/autoload.php'; $app = require $root.'/bootstrap/app.php'; $app->make(Kernel::class)->bootstrap(); $db = DB::connection(); $identity = $db->selectOne('select current_database() as database, inet_server_port() as port'); if ($identity->database !== $target['database'] || (int) $identity->port !== 55441) { throw new RuntimeException('Actual QA identity rejected.'); } $check = function (bool $extended) use ($db, $proof) { foreach ($proof['table_hashes'] as $name => $hash) { // Extension adds nullable columns to existing tasks; compare original columns explicitly. $columns = '*'; if ($extended && $name === '"public"."tasks"') { $columns = implode(',', array_map(fn ($r) => '"'.$r->column_name.'"', $db->select("select column_name from information_schema.columns where table_schema='public' and table_name='tasks' and column_name not in ('desk_case_id','desk_request_id','guidance_key','accepted_by','accepted_at','external_sync_status') order by ordinal_position"))); } $rows = array_map(fn ($r) => $r->value, $db->select('select row_to_json(t)::text as value from (select '.$columns.' from '.$name.') t order by row_to_json(t)::text')); if (hash('sha256', implode("\n", $rows)) !== $hash) { throw new RuntimeException('Original contents changed: '.$name); } } }; $check(false); $migrations = array_map(fn ($name) => require $root.'/database/migrations/'.$name.'.php', VirtualDeskInstallation::MIGRATIONS); $db->transaction(function () use ($migrations, $check) { foreach ($migrations as $migration) { $migration->up(); } $check(true); foreach (array_reverse($migrations) as $migration) { $migration->down(); } $check(false); }); $archive = new ZipArchive; $archive->open($proof['files_path']); $restoreRoot = dirname($proof['files_path']).'/restored-files'; foreach ($proof['file_hashes'] as $name => $hash) { if (str_contains($name, '..') || str_starts_with($name, '/')) { throw new RuntimeException('Invalid archive entry.'); } $path = $restoreRoot.'/'.$name; if (! is_dir(dirname($path))) { mkdir(dirname($path), 0700, true); } file_put_contents($path, $archive->getFromName($name)); if (hash_file('sha256', $path) !== $hash) { throw new RuntimeException('Restored private bytes differ.'); } } $archive->close(); $result = ['passed' => true, 'verified_at' => gmdate('c'), 'database' => $target['database'], 'port' => 55441, 'migrations' => VirtualDeskInstallation::MIGRATIONS, 'original_tables_preserved' => count($proof['table_hashes']), 'empty_rollback_verified' => true, 'private_files_restored' => count($proof['file_hashes']), 'dump_sha256' => $proof['sha256'], 'code_hash' => app(VirtualDeskInstallation::class)->fingerprint()]; file_put_contents($root.'/storage/app/private/virtual-desk/activation-compatibility.json', json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); echo json_encode($result, JSON_THROW_ON_ERROR);