Public dry run dryrun-20b59dc46b72
Source commit: 20b59dc46b72103c2f8a516c692b5cc3d54fab19 Public tree identity: sha256:aaa5ac7b58b2a0d23c6b11e5f76324bf3839ca1f62653a83deb736932adcfbd9
This commit is contained in:
commit
2bef715211
221 changed files with 79792 additions and 0 deletions
515
scripts/public-release-dryrun-final-evidence.js
Executable file
515
scripts/public-release-dryrun-final-evidence.js
Executable file
|
|
@ -0,0 +1,515 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const {
|
||||
assertFinalLedger,
|
||||
readPhase3Ledger,
|
||||
} = require("./phase3-ledger");
|
||||
|
||||
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";
|
||||
const serviceHost = "disasmer.michelpaulissen.com";
|
||||
const serviceAddr = `${serviceHost}:443`;
|
||||
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"),
|
||||
hostedClientCompat: process.env.DISASMER_HOSTED_CLIENT_COMPAT_REPORT ||
|
||||
path.join(acceptanceRoot, "hosted-client-compat.json"),
|
||||
coreCoordinatorCompat: process.env.DISASMER_CORE_COORDINATOR_COMPAT_REPORT ||
|
||||
path.join(acceptanceRoot, "core-coordinator-compat.json"),
|
||||
e2e: process.env.DISASMER_PUBLIC_RELEASE_E2E_REPORT ||
|
||||
path.join(acceptanceRoot, "public-release-dryrun-e2e.json"),
|
||||
};
|
||||
|
||||
assertFinalLedger(readPhase3Ledger(repo));
|
||||
|
||||
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",
|
||||
"root/*.md except README.md and SECURITY.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_tree_clean,
|
||||
true,
|
||||
"final release evidence cannot be produced from a dirty source tree"
|
||||
);
|
||||
for (const binary of [
|
||||
"disasmer",
|
||||
"disasmer-coordinator",
|
||||
"disasmer-node",
|
||||
"disasmer-debug-dap",
|
||||
]) {
|
||||
const platformName = process.platform === "win32" ? `${binary}.exe` : binary;
|
||||
assert.match(
|
||||
manifest.binary_digests && manifest.binary_digests[platformName],
|
||||
/^sha256:[a-f0-9]{64}$/,
|
||||
`manifest omitted release binary digest for ${platformName}`
|
||||
);
|
||||
}
|
||||
assert.strictEqual(
|
||||
manifest.source_commit,
|
||||
expectedSourceCommit(),
|
||||
"final public release evidence must be generated from the current acceptance commit"
|
||||
);
|
||||
assert.strictEqual(manifest.default_hosted_coordinator_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"
|
||||
);
|
||||
const resolvedPublicRepoRemote = publicRepoRemote(manifest);
|
||||
assertIncludes(
|
||||
resolvedPublicRepoRemote,
|
||||
forgejoHost,
|
||||
"manifest public repository must point at Forgejo"
|
||||
);
|
||||
|
||||
const manifestAssets = assetNames(manifest);
|
||||
for (const pattern of [
|
||||
/^disasmer-public-source-/,
|
||||
/^disasmer-public-binaries-/,
|
||||
/^disasmer-public-evidence-/,
|
||||
/^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_hosted_coordinator_endpoint,
|
||||
manifest.default_hosted_coordinator_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_hosted_coordinator_endpoint, serviceEndpoint);
|
||||
assert.strictEqual(deployment.coordinator_implementation, "hosted-policy-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.coordinator_implementation, "hosted-policy-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
|
||||
);
|
||||
assert(service.deployment_config_commit, "service smoke must record deployment config commit");
|
||||
assert(
|
||||
service.deployment_system_generation,
|
||||
"service smoke must record the activated NixOS system generation"
|
||||
);
|
||||
assert(service.service_unit, "service smoke must record the active service unit fragment");
|
||||
for (const key of [
|
||||
"ping",
|
||||
"login",
|
||||
"projects",
|
||||
"forged_unsigned_project",
|
||||
"session_scope",
|
||||
"server_generated_enrollment_grant",
|
||||
"node_credential",
|
||||
"signed_node_heartbeat",
|
||||
"signed_node_capabilities",
|
||||
"scoped_node_listing",
|
||||
"process_start",
|
||||
"debug_attach",
|
||||
"process_abort",
|
||||
]) {
|
||||
assert(service.evidence && service.evidence[key], `service smoke missing ${key}`);
|
||||
}
|
||||
assert.strictEqual(service.evidence.forged_unsigned_project, "error");
|
||||
assert.strictEqual(service.evidence.session_scope, "derived_from_server_identity");
|
||||
if (service.acceptance_result) {
|
||||
assert.strictEqual(service.acceptance_result, "passed");
|
||||
}
|
||||
|
||||
const compat = readJson("hosted Client compatibility report", inputs.hostedClientCompat);
|
||||
assert.strictEqual(compat.kind, "disasmer-hosted-client-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.server_owned_browser_login.response, "oidc_browser_session");
|
||||
assert(compat.server_owned_browser_login.coordinator_requests >= 2);
|
||||
assert.strictEqual(
|
||||
compat.server_owned_browser_login.provider_tokens_exposed_to_cli,
|
||||
false
|
||||
);
|
||||
assert.strictEqual(compat.server_owned_browser_login.session_scope_derived, true);
|
||||
assert.strictEqual(compat.authority_denials.forged_unsigned_project, "error");
|
||||
assert.strictEqual(compat.authority_denials.cross_tenant_task_events, "error");
|
||||
assert.strictEqual(compat.session_lifecycle.logout_revoked, true);
|
||||
assert.strictEqual(compat.session_lifecycle.revoked_reuse, "error");
|
||||
assert.strictEqual(compat.session_lifecycle.expired_reuse, "error");
|
||||
|
||||
const coreCoordinator = readJson(
|
||||
"Core coordinator compatibility report",
|
||||
inputs.coreCoordinatorCompat
|
||||
);
|
||||
assert.strictEqual(coreCoordinator.kind, "disasmer-core-coordinator-compatibility");
|
||||
assert.strictEqual(coreCoordinator.source_commit, manifest.source_commit);
|
||||
assert.strictEqual(coreCoordinator.release_name, manifest.release_name);
|
||||
assert.strictEqual(
|
||||
coreCoordinator.coordinator_implementation,
|
||||
"standalone-core-coordinator"
|
||||
);
|
||||
assert.strictEqual(coreCoordinator.client_authority, "strict");
|
||||
assert.strictEqual(coreCoordinator.authenticated_session, true);
|
||||
assert.strictEqual(coreCoordinator.forged_body_authority_denied, "error");
|
||||
assert.strictEqual(coreCoordinator.wrong_session_denied, "error");
|
||||
assert.strictEqual(coreCoordinator.self_hosted_cli.connected, "connected");
|
||||
assert.strictEqual(coreCoordinator.self_hosted_cli.secret_read_from_stdin, true);
|
||||
assert.strictEqual(coreCoordinator.self_hosted_cli.secret_exposed_in_report, false);
|
||||
assert.strictEqual(coreCoordinator.self_hosted_cli.authenticated_status, "auth_status");
|
||||
assert.strictEqual(
|
||||
coreCoordinator.self_hosted_admin.nonce_bound_proof_succeeded,
|
||||
"admin_status"
|
||||
);
|
||||
assert.strictEqual(coreCoordinator.self_hosted_admin.replay_denied, "error");
|
||||
if (process.platform !== "win32") {
|
||||
assert.strictEqual(coreCoordinator.self_hosted_cli.session_file_mode, "600");
|
||||
}
|
||||
assert.strictEqual(coreCoordinator.task_placement, "task_placement");
|
||||
assert.strictEqual(coreCoordinator.task_completion, "task_recorded");
|
||||
assert(coreCoordinator.task_events >= 1, "Core coordinator compat must record task events");
|
||||
assert.strictEqual(coreCoordinator.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_hosted_coordinator_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, "main_launched");
|
||||
assert.strictEqual(e2e.worker_assignment_process, e2e.process);
|
||||
assert.strictEqual(e2e.core_coordinator_validated, true);
|
||||
assert.strictEqual(
|
||||
e2e.core_coordinator_implementation,
|
||||
"standalone-core-coordinator"
|
||||
);
|
||||
assert.strictEqual(e2e.core_coordinator_client_authority, "strict");
|
||||
assert.strictEqual(e2e.core_coordinator_authenticated_session, true);
|
||||
assert.strictEqual(e2e.core_coordinator_forged_body_authority_denied, "error");
|
||||
assert.strictEqual(e2e.core_coordinator_wrong_session_denied, "error");
|
||||
assert.strictEqual(e2e.core_coordinator_launch_task_response, "task_launched");
|
||||
assert.strictEqual(e2e.core_coordinator_assignment_response, "task_assignment");
|
||||
assert(e2e.core_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_coordinator_implementation,
|
||||
"hosted-policy-coordinator"
|
||||
);
|
||||
assert.strictEqual(e2e.released_live_dap_attached_to_client_process, true);
|
||||
assert.strictEqual(e2e.released_live_dap_authenticated_with_cli_session, 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.attached_to_existing_client_process === true,
|
||||
"e2e must prove released DAP attaches to the existing Client-authorized process"
|
||||
);
|
||||
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",
|
||||
"core_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_hosted_coordinator_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: {
|
||||
coordinator_implementation: service.coordinator_implementation,
|
||||
service_addr: service.service_addr,
|
||||
launch_task: service.evidence.launch_task,
|
||||
assignment_poll: service.evidence.assignment_poll,
|
||||
released_live_dap: e2e.evidence.released_live_dap,
|
||||
},
|
||||
standalone_core_coordinator: {
|
||||
coordinator_implementation: coreCoordinator.coordinator_implementation,
|
||||
client_authority: coreCoordinator.client_authority,
|
||||
authenticated_session: coreCoordinator.authenticated_session,
|
||||
forged_body_authority_denied:
|
||||
coreCoordinator.forged_body_authority_denied,
|
||||
wrong_session_denied: coreCoordinator.wrong_session_denied,
|
||||
self_hosted_cli: coreCoordinator.self_hosted_cli,
|
||||
task_placement: coreCoordinator.task_placement,
|
||||
task_events: coreCoordinator.task_events,
|
||||
release_binary_e2e: {
|
||||
client_authority: e2e.core_coordinator_client_authority,
|
||||
authenticated_session: e2e.core_coordinator_authenticated_session,
|
||||
forged_body_authority_denied:
|
||||
e2e.core_coordinator_forged_body_authority_denied,
|
||||
wrong_session_denied: e2e.core_coordinator_wrong_session_denied,
|
||||
launch_task: e2e.core_coordinator_launch_task_response,
|
||||
assignment_poll: e2e.core_coordinator_assignment_response,
|
||||
task_events: e2e.core_coordinator_task_events,
|
||||
},
|
||||
},
|
||||
},
|
||||
forgejo_host: forgejoHost,
|
||||
public_repository_url: resolvedPublicRepoRemote,
|
||||
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,
|
||||
},
|
||||
core_coordinator_compatibility: {
|
||||
result: "passed",
|
||||
evidence: coreCoordinator,
|
||||
},
|
||||
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}`);
|
||||
Loading…
Add table
Add a link
Reference in a new issue