#!/usr/bin/env node const assert = require("assert"); const fs = require("fs"); const path = require("path"); const repo = path.resolve(__dirname, ".."); const releaseRoot = path.resolve( process.env.DISASMER_PUBLIC_RELEASE_DIR || path.join(repo, "target/public-release-dryrun") ); const acceptanceRoot = path.join(repo, "target/acceptance"); const serviceEndpoint = "https://disasmer.michelpaulissen.com:9443"; const serviceHost = "disasmer.michelpaulissen.com"; const serviceAddr = `${serviceHost}:9443`; const forgejoHost = "git.michelpaulissen.com"; const inputs = { manifest: process.env.DISASMER_PUBLIC_RELEASE_MANIFEST || path.join(releaseRoot, "public-release-manifest.json"), forgejoRelease: process.env.DISASMER_PUBLIC_RELEASE_FORGEJO_REPORT || path.join(acceptanceRoot, "public-release-dryrun-forgejo-release.json"), deployment: process.env.DISASMER_PUBLIC_RELEASE_DEPLOYMENT_MANIFEST || path.join(releaseRoot, "deployment/stage/deployment-manifest.json"), service: process.env.DISASMER_PUBLIC_RELEASE_SERVICE_REPORT || path.join(acceptanceRoot, "public-release-dryrun-service.json"), publicOperatorCompat: process.env.DISASMER_PUBLIC_OPERATOR_COMPAT_REPORT || path.join(acceptanceRoot, "public-operator-compat.json"), publicCoordinatorCompat: process.env.DISASMER_PUBLIC_COORDINATOR_COMPAT_REPORT || path.join(acceptanceRoot, "public-coordinator-compat.json"), e2e: process.env.DISASMER_PUBLIC_RELEASE_E2E_REPORT || path.join(acceptanceRoot, "public-release-dryrun-e2e.json"), }; function readJson(name, file) { assert(fs.existsSync(file), `missing ${name} evidence: ${file}`); return JSON.parse(fs.readFileSync(file, "utf8")); } function assertIncludes(value, expected, message) { assert( typeof value === "string" && value.includes(expected), `${message}: expected ${JSON.stringify(value)} to include ${expected}` ); } function assetNames(manifest) { assert(Array.isArray(manifest.assets), "manifest assets must be an array"); return new Set(manifest.assets.map((asset) => asset.name)); } function uploadedAssetNames(report) { return new Set( [...(report.uploaded_assets || []), ...(report.reused_assets || [])].map( (asset) => asset.name ) ); } function assertEvidenceBooleans(report, fields) { for (const field of fields) { assert.strictEqual(report[field], true, `e2e report must set ${field}=true`); } } function compactToolVersions(...reports) { const versions = {}; for (const [name, report] of reports) { if (report && report.tool_versions) { versions[name] = report.tool_versions; } } return versions; } function assertDnsState(value, name) { assert( ["not-published", "published"].includes(value), `${name} has unexpected DNS publication state: ${value}` ); } function assertFilteredOut(manifest) { assert(Array.isArray(manifest.filtered_out), "manifest filtered_out must be an array"); for (const expected of [ "private/**", "experiments/**", ".git", "target", "/*.md", "**/.disasmer/**", ]) { assert( manifest.filtered_out.includes(expected), `manifest filtered_out must include ${expected}` ); } const includeForgejoWorkflows = manifest.public_export && manifest.public_export.include_forgejo_workflows; if (!includeForgejoWorkflows) { assert( manifest.filtered_out.includes(".forgejo/**"), "manifest filtered_out must include .forgejo/** for the host-neutral export" ); } } function publicTreeAlreadyPushed(manifest) { return ( (manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) || process.env.DISASMER_PUBLIC_TREE_ALREADY_PUSHED === "1" ); } function publicRepoRemote(manifest) { return ( manifest.public_repo_url || manifest.public_repo_remote || process.env.DISASMER_PUBLIC_REPO_REMOTE || process.env.DISASMER_PUBLIC_REPO_URL || "" ); } function commandOutput(command, args) { try { return require("child_process") .execFileSync(command, args, { cwd: repo, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], }) .trim(); } catch (_) { return null; } } function expectedSourceCommit() { return ( process.env.DISASMER_ACCEPTANCE_COMMIT || commandOutput("git", ["rev-parse", "HEAD"]) || "unknown" ); } const manifest = readJson("public release manifest", inputs.manifest); assert.strictEqual(manifest.kind, "disasmer-public-release-dryrun"); assert.strictEqual( manifest.source_commit, expectedSourceCommit(), "final public release evidence must be generated from the current acceptance commit" ); assert.strictEqual(manifest.default_operator_endpoint, serviceEndpoint); assert.strictEqual(manifest.forgejo_host, forgejoHost); assertDnsState(manifest.dns_publication_state, "manifest"); assert(manifest.resolver_override, "manifest must record resolver override"); assertFilteredOut(manifest); assert.strictEqual( publicTreeAlreadyPushed(manifest), true, "filtered public tree must be pushed to Forgejo" ); assertIncludes( publicRepoRemote(manifest), forgejoHost, "manifest public repository must point at Forgejo" ); const manifestAssets = assetNames(manifest); for (const pattern of [ /^disasmer-public-source-/, /^disasmer-public-binaries-/, /^disasmer-vscode-/, /^DISASMER_PUBLIC_DRYRUN_GETTING_STARTED-/, /^DISASMER_PUBLIC_DRYRUN_INVITE-/, /^SHA256SUMS$/, ]) { assert( [...manifestAssets].some((name) => pattern.test(name)), `manifest is missing asset matching ${pattern}` ); } const forgejoRelease = readJson("Forgejo Release report", inputs.forgejoRelease); assert.strictEqual( forgejoRelease.kind, "disasmer-public-release-dryrun-forgejo-release" ); assertIncludes(forgejoRelease.forgejo_url, forgejoHost, "Forgejo Release host"); assert.strictEqual(forgejoRelease.release_name, manifest.release_name); assert.strictEqual( forgejoRelease.default_operator_endpoint, manifest.default_operator_endpoint ); assert.strictEqual( forgejoRelease.public_tree_identity, manifest.public_tree_identity ); assert.strictEqual(forgejoRelease.source_commit, manifest.source_commit); const releaseAssets = uploadedAssetNames(forgejoRelease); for (const asset of manifestAssets) { assert(releaseAssets.has(asset), `Forgejo Release is missing asset ${asset}`); } const deployment = readJson("deployment manifest", inputs.deployment); assert.strictEqual(deployment.kind, "disasmer-public-release-dryrun-deployment"); assert.strictEqual(deployment.default_operator_endpoint, serviceEndpoint); assert.strictEqual(deployment.operator_implementation, "private-hosted-coordinator"); assert.match( deployment.public_release_meaning || "", /public repository[\s\S]*public release assets[\s\S]*public client protocol/ ); assert.strictEqual(deployment.service_host, serviceHost); assert.strictEqual(deployment.service_addr, serviceAddr); assertDnsState(deployment.dns_publication_state, "deployment"); assert(deployment.resolver_override, "deployment manifest must record resolver override"); assert(deployment.deployed_service_commit, "deployment must record deployed service commit"); const service = readJson("live service smoke report", inputs.service); assert.strictEqual(service.kind, "disasmer-public-release-dryrun-service"); assert.strictEqual(service.source_commit, manifest.source_commit); assert.strictEqual(service.release_name, manifest.release_name); assert.strictEqual(service.endpoint, serviceEndpoint); assert.strictEqual(service.service_addr, serviceAddr); assert.strictEqual(service.operator_implementation, "private-hosted-coordinator"); assertDnsState(service.dns_publication_state, "service smoke"); assert(service.resolver_override, "service smoke must record resolver override"); assert.strictEqual( service.public_tree_identity || manifest.public_tree_identity, manifest.public_tree_identity ); assert.strictEqual( service.deployed_service_commit || deployment.deployed_service_commit, deployment.deployed_service_commit ); for (const key of [ "ping", "login", "project", "node_credential", "heartbeat", "public_worker_credential", "public_worker_capabilities", "public_launch_task", "public_assignment_poll", "public_worker_completion", "process", "task", "debug", "artifact_metadata", "download_action", "download_link", "observability", ]) { assert(service.evidence && service.evidence[key], `service smoke missing ${key}`); } assert.strictEqual(service.evidence.public_launch_task, "task_launched"); assert.strictEqual(service.evidence.public_assignment_poll, "task_assignment"); assert.strictEqual(service.evidence.public_worker_completion, "task_recorded"); if (service.acceptance_result) { assert.strictEqual(service.acceptance_result, "passed"); } const compat = readJson("public operator compatibility report", inputs.publicOperatorCompat); assert.strictEqual(compat.kind, "disasmer-public-operator-compatibility"); assert.strictEqual(compat.source_commit, manifest.source_commit); assert.strictEqual(compat.release_name, manifest.release_name); assert.strictEqual( compat.public_cli_attach.coordinator_response, "node_enrollment_exchanged" ); assert.strictEqual(compat.public_cli_attach.heartbeat_response, "node_heartbeat"); assert.strictEqual(compat.public_launch_task.process_started_response, "process_started"); assert.strictEqual(compat.public_launch_task.launch_response, "task_launched"); assert.strictEqual(compat.public_node_runtime.node_status, "completed"); assert.strictEqual(compat.public_node_runtime.task_assignment_response, "task_assignment"); assert.strictEqual( compat.public_node_runtime.task_assignment_node, compat.public_launch_task.assignment_node ); assert.strictEqual( compat.public_node_runtime.coordinator_response, "task_recorded" ); assert(compat.public_task_events >= 1, "public operator compat must record task events"); const publicCoordinator = readJson( "public coordinator compatibility report", inputs.publicCoordinatorCompat ); assert.strictEqual(publicCoordinator.kind, "disasmer-public-coordinator-compatibility"); assert.strictEqual(publicCoordinator.source_commit, manifest.source_commit); assert.strictEqual(publicCoordinator.release_name, manifest.release_name); assert.strictEqual( publicCoordinator.operator_implementation, "standalone-public-coordinator" ); assert.strictEqual(publicCoordinator.task_placement, "task_placement"); assert.strictEqual(publicCoordinator.task_completion, "task_recorded"); assert(publicCoordinator.task_events >= 1, "public coordinator compat must record task events"); assert.strictEqual(publicCoordinator.artifact_export_plan, "artifact_export_plan"); const e2e = readJson("public repository e2e report", inputs.e2e); assert.strictEqual(e2e.kind, "disasmer-public-release-dryrun-e2e"); assert.strictEqual(e2e.default_operator_endpoint, serviceEndpoint); assert.strictEqual(e2e.service_addr, serviceAddr); assert.strictEqual(e2e.launch_task_verified, true); assert.strictEqual(e2e.worker_assignment_poll_verified, true); assert.strictEqual(e2e.worker_assignment_poll_protocol, "poll_task_assignment"); assert.strictEqual(e2e.launch_task_response, "task_launched"); assert.strictEqual(e2e.worker_assignment_process, e2e.process); assert.strictEqual(e2e.public_coordinator_validated, true); assert.strictEqual( e2e.public_coordinator_operator_implementation, "standalone-public-coordinator" ); assert.strictEqual(e2e.public_coordinator_launch_task_response, "task_launched"); assert.strictEqual(e2e.public_coordinator_assignment_response, "task_assignment"); assert(e2e.public_coordinator_task_events >= 1, "e2e must record public coordinator task events"); assert.strictEqual(e2e.released_live_dap_verified, true); assert.strictEqual( e2e.released_live_dap_operator_implementation, "private-hosted-coordinator" ); assert.strictEqual(e2e.released_live_dap_worker_after_launch, true); assert(e2e.released_live_dap_task_events >= 1, "e2e must record released live-DAP task events"); assert( e2e.evidence && e2e.evidence.released_live_dap && e2e.evidence.released_live_dap.coordinator_side_started_without_worker === true, "e2e must prove released DAP starts the coordinator-side process before a worker is attached" ); assert( e2e.evidence && e2e.evidence.released_live_dap && e2e.evidence.released_live_dap.variables_verified === true, "e2e must prove released live-DAP variables are inspectable" ); if (e2e.dns_publication_state) { assertDnsState(e2e.dns_publication_state, "public repository e2e"); } if (e2e.acceptance_result) { assert.strictEqual(e2e.acceptance_result, "passed"); } assertIncludes(e2e.public_repository_url, forgejoHost, "e2e public repo URL"); assert.strictEqual(e2e.release_name, manifest.release_name); assert.strictEqual(e2e.public_tree_identity, manifest.public_tree_identity); assert.strictEqual(e2e.source_commit, manifest.source_commit); assertEvidenceBooleans(e2e, [ "downloaded_release_assets", "verified_checksums", "clean_public_checkout", "public_repo_build_or_install", "default_operator_selected", "browser_or_cli_login", "attached_user_node", "public_coordinator_validated", "released_live_dap_verified", "ran_flagship_workflow", "vscode_debugger_verified", "logs_verified", "artifact_metadata_verified", "artifact_download_or_export_verified", ]); assert(Array.isArray(e2e.commands) && e2e.commands.length > 0); assert(Array.isArray(e2e.tool_versions) || typeof e2e.tool_versions === "object"); const finalReport = { kind: "disasmer-public-release-dryrun-final-evidence", release_name: manifest.release_name, source_commit: manifest.source_commit, public_tree_identity: manifest.public_tree_identity, deployed_service_commit: deployment.deployed_service_commit, default_operator_endpoint: serviceEndpoint, service_addr: serviceAddr, dns_publication_state: { manifest: manifest.dns_publication_state, deployment: deployment.dns_publication_state, service: service.dns_publication_state, public_repository_e2e: e2e.dns_publication_state || null, }, resolver_override: { manifest: manifest.resolver_override, deployment: deployment.resolver_override, service: service.resolver_override, public_repository_e2e: e2e.resolver_override || null, }, deployment_config: { repo: service.deployment_config_repo || null, commit: service.deployment_config_commit || null, system_generation: service.deployment_system_generation || null, service_unit: service.service_unit || null, }, coordinator_validation: { private_hosted_default_operator: { operator_implementation: service.operator_implementation, service_addr: service.service_addr, launch_task: service.evidence.public_launch_task, assignment_poll: service.evidence.public_assignment_poll, released_live_dap: e2e.evidence.released_live_dap, }, standalone_public_coordinator: { operator_implementation: publicCoordinator.operator_implementation, task_placement: publicCoordinator.task_placement, task_events: publicCoordinator.task_events, release_binary_e2e: { launch_task: e2e.public_coordinator_launch_task_response, assignment_poll: e2e.public_coordinator_assignment_response, task_events: e2e.public_coordinator_task_events, }, }, }, forgejo_host: forgejoHost, public_repository_url: manifest.public_repo_url, forgejo_release: { owner: forgejoRelease.owner, repo: forgejoRelease.repo, release_id: forgejoRelease.release_id, asset_count: releaseAssets.size, }, tool_versions: compactToolVersions( ["manifest", manifest], ["service_smoke", service], ["public_repository_e2e", e2e] ), acceptance_results: { public_release_preparation: { commands: manifest.commands, result: "passed", }, deployment_bundle: { commands: deployment.commands, result: "passed", }, live_service_smoke: { command: service.acceptance_command || null, result: service.acceptance_result || "passed", evidence: service.evidence, }, public_coordinator_compatibility: { result: "passed", evidence: publicCoordinator, }, public_repository_e2e: { commands: e2e.commands, result: "passed", evidence: e2e.evidence, }, }, evidence_files: inputs, }; const output = path.join(acceptanceRoot, "public-release-dryrun-final.json"); fs.mkdirSync(path.dirname(output), { recursive: true }); fs.writeFileSync(output, `${JSON.stringify(finalReport, null, 2)}\n`); console.log(`Public release dry-run final evidence passed: ${output}`);