Source commit: b468a859465ae17a287d83b7ca1c5a86c5f1bdc6 Public tree identity: sha256:efc0a1cf2eb1044109ac7d5064062d83a63d7a54fe6aca62033efa6f74af14de
224 lines
8.2 KiB
JavaScript
Executable file
224 lines
8.2 KiB
JavaScript
Executable file
#!/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"),
|
|
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`);
|
|
}
|
|
}
|
|
|
|
const manifest = readJson("public release manifest", inputs.manifest);
|
|
assert.strictEqual(manifest.kind, "disasmer-public-release-dryrun");
|
|
assert.strictEqual(manifest.default_operator_endpoint, serviceEndpoint);
|
|
assert.strictEqual(manifest.forgejo_host, forgejoHost);
|
|
assert.strictEqual(manifest.dns_publication_state, "not-published");
|
|
assert(manifest.resolver_override, "manifest must record resolver override");
|
|
assert.deepStrictEqual(manifest.filtered_out, [
|
|
"private/**",
|
|
"experiments/**",
|
|
".git",
|
|
"target",
|
|
]);
|
|
assert.strictEqual(
|
|
manifest.public_tree_publish && manifest.public_tree_publish.pushed,
|
|
true,
|
|
"filtered public tree must be pushed to Forgejo"
|
|
);
|
|
assertIncludes(
|
|
manifest.public_repo_url || manifest.public_repo_remote || "",
|
|
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.service_host, serviceHost);
|
|
assert.strictEqual(deployment.service_addr, serviceAddr);
|
|
assert.strictEqual(deployment.dns_publication_state, "not-published");
|
|
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.endpoint, serviceEndpoint);
|
|
assert.strictEqual(service.service_addr, serviceAddr);
|
|
assert.strictEqual(service.dns_publication_state, "not-published");
|
|
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",
|
|
"process",
|
|
"task",
|
|
"debug",
|
|
"artifact_metadata",
|
|
"download_action",
|
|
"download_link",
|
|
"observability",
|
|
]) {
|
|
assert(service.evidence && service.evidence[key], `service smoke missing ${key}`);
|
|
}
|
|
|
|
const compat = readJson("public operator compatibility report", inputs.publicOperatorCompat);
|
|
assert.strictEqual(compat.kind, "disasmer-public-operator-compatibility");
|
|
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_node_runtime.node_status, "completed");
|
|
assert.strictEqual(
|
|
compat.public_node_runtime.coordinator_response,
|
|
"task_recorded"
|
|
);
|
|
assert(compat.public_task_events >= 1, "public operator compat must record task events");
|
|
|
|
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);
|
|
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",
|
|
"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,
|
|
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,
|
|
},
|
|
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}`);
|