Public release release-55b5a563d642

Source commit: 55b5a563d6421f49b5f5e77bc3c1f29752da8801

Public tree identity: sha256:52790e23e2b1806b00cc220c92edcfeed445600dcde620342af2b3369e7883fa
This commit is contained in:
Clusterflux release 2026-07-16 17:13:43 +02:00
commit 6acd2d6eb7
210 changed files with 76963 additions and 0 deletions

View file

@ -0,0 +1,294 @@
#!/usr/bin/env node
const assert = require("assert");
const cp = require("child_process");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const repo = path.resolve(__dirname, "..");
const releaseRoot = path.resolve(
process.env.CLUSTERFLUX_PUBLIC_RELEASE_DIR ||
path.join(repo, "target/public-release")
);
const acceptanceRoot = path.join(repo, "target/acceptance");
const manifestPath =
process.env.CLUSTERFLUX_PUBLIC_RELEASE_MANIFEST ||
path.join(releaseRoot, "public-release-manifest.json");
const reportPath =
process.env.CLUSTERFLUX_PUBLIC_RELEASE_PREFLIGHT_REPORT ||
path.join(acceptanceRoot, "public-release-preflight.json");
function commandOutput(command, args, options = {}) {
try {
return cp
.execFileSync(command, args, {
cwd: repo,
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
...options,
})
.trim();
} catch (_) {
return null;
}
}
function nonInteractiveEnv(extra = {}) {
return {
...process.env,
GIT_TERMINAL_PROMPT: "0",
GIT_ASKPASS: process.env.GIT_ASKPASS || "/bin/false",
SSH_ASKPASS: process.env.SSH_ASKPASS || "/bin/false",
GIT_SSH_COMMAND:
process.env.GIT_SSH_COMMAND ||
"ssh -o BatchMode=yes -o NumberOfPasswordPrompts=0",
...extra,
};
}
function expectedSourceCommit() {
const head = commandOutput("git", ["rev-parse", "HEAD"]);
assert(head && /^[0-9a-f]{40}$/u.test(head), "preflight requires an exact Git HEAD");
const asserted = process.env.CLUSTERFLUX_ACCEPTANCE_COMMIT;
if (asserted) {
assert.strictEqual(asserted, head, "acceptance commit must match Git HEAD");
}
return head;
}
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function sha256File(file) {
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
}
function parseSha256Sums(file) {
const sums = new Map();
for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
if (!line.trim()) continue;
const match = /^([0-9a-f]{64})\s+(.+)$/.exec(line.trim());
assert(match, `malformed SHA256SUMS line: ${line}`);
sums.set(path.basename(match[2]), match[1]);
}
return sums;
}
function remoteHead(remote) {
const output = commandOutput("git", ["ls-remote", remote, "HEAD", "refs/heads/main"], {
env: nonInteractiveEnv(),
timeout: Number(process.env.CLUSTERFLUX_PUBLIC_REPO_REMOTE_TIMEOUT_MS || 30000),
});
if (!output) return null;
const lines = output.split(/\r?\n/).filter(Boolean);
const head = lines.find((line) => line.endsWith("\tHEAD")) || lines[0];
return head && head.split(/\s+/)[0];
}
function publicTreeAlreadyPushed(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) ||
process.env.CLUSTERFLUX_PUBLIC_TREE_ALREADY_PUSHED === "1"
);
}
function publicTreePushSource(manifest) {
return manifest.public_tree_publish && manifest.public_tree_publish.pushed === true
? "manifest"
: "external-env";
}
function publicRepoRemoteForManifest(manifest) {
return (
manifest.public_repo_url ||
manifest.public_repo_remote ||
process.env.CLUSTERFLUX_PUBLIC_REPO_REMOTE ||
process.env.CLUSTERFLUX_PUBLIC_REPO_URL ||
null
);
}
function publicTreeCommitForManifest(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.commit) ||
process.env.CLUSTERFLUX_PUBLIC_TREE_COMMIT ||
process.env.CLUSTERFLUX_PUBLIC_RELEASE_TARGET ||
null
);
}
function envState(name) {
return process.env[name] ? "set" : "unset";
}
function staleEvidence(file, currentSourceCommit) {
if (!fs.existsSync(file)) {
return { file, status: "missing", source_commit: null, release_name: null };
}
const evidence = readJson(file);
if (!evidence.source_commit) {
return {
file,
status: "unversioned",
source_commit: null,
release_name: evidence.release_name || null,
};
}
return {
file,
status: evidence.source_commit === currentSourceCommit ? "current" : "stale",
source_commit: evidence.source_commit,
release_name: evidence.release_name || null,
};
}
assert(fs.existsSync(manifestPath), `missing public release manifest: ${manifestPath}`);
const manifest = readJson(manifestPath);
const currentSourceCommit = expectedSourceCommit();
const currentTreeStatus = commandOutput("git", ["status", "--short"]) || "";
assert.strictEqual(
currentTreeStatus,
"",
"public release preflight requires a clean source tree"
);
assert.strictEqual(manifest.kind, "clusterflux-public-release");
assert.strictEqual(
manifest.source_commit,
currentSourceCommit,
"public release manifest must be regenerated for the current acceptance commit"
);
assert.strictEqual(manifest.source_tree_clean, true, "public release prep must start clean");
assert.strictEqual(
publicTreeAlreadyPushed(manifest),
true,
"filtered public tree must be pushed to Forgejo before release publication"
);
const publicRepoRemote = publicRepoRemoteForManifest(manifest);
assert(publicRepoRemote, "manifest must record public repository URL or remote");
const publicTreeCommit = publicTreeCommitForManifest(manifest);
const remoteMain = remoteHead(publicRepoRemote);
assert(remoteMain, "Forgejo public repository main branch must be readable");
if (publicTreeCommit) {
assert.strictEqual(
remoteMain,
publicTreeCommit,
"Forgejo public repository main branch must match the prepared public tree commit"
);
}
assert(Array.isArray(manifest.assets) && manifest.assets.length > 0, "manifest assets missing");
const checksumAsset = manifest.assets.find((asset) => asset.name === "SHA256SUMS");
assert(checksumAsset, "manifest must include SHA256SUMS");
assert(fs.existsSync(checksumAsset.file), `missing checksum asset: ${checksumAsset.file}`);
const checksums = parseSha256Sums(checksumAsset.file);
const assets = manifest.assets.map((asset) => {
assert(fs.existsSync(asset.file), `missing release asset: ${asset.file}`);
const actual = sha256File(asset.file);
const expected = checksums.get(asset.name);
if (asset.name !== "SHA256SUMS") {
assert.strictEqual(actual, expected, `checksum mismatch for ${asset.name}`);
}
return {
name: asset.name,
file: asset.file,
bytes: fs.statSync(asset.file).size,
sha256: actual,
};
});
const evidence = [
staleEvidence(
path.join(acceptanceRoot, "public-release-forgejo-release.json"),
currentSourceCommit
),
staleEvidence(
path.join(acceptanceRoot, "public-release-service.json"),
currentSourceCommit
),
staleEvidence(
path.join(acceptanceRoot, "hosted-client-compat.json"),
currentSourceCommit
),
staleEvidence(
path.join(acceptanceRoot, "core-coordinator-compat.json"),
currentSourceCommit
),
staleEvidence(
path.join(acceptanceRoot, "public-release-e2e.json"),
currentSourceCommit
),
staleEvidence(
path.join(acceptanceRoot, "public-release-final.json"),
currentSourceCommit
),
];
const report = {
kind: "clusterflux-public-release-preflight",
source_commit: currentSourceCommit,
release_name: manifest.release_name,
public_tree_commit: publicTreeCommit || remoteMain,
public_tree_push_source: publicTreePushSource(manifest),
public_repo_url: publicRepoRemote,
public_repo_remote_head: remoteMain,
source_tree_clean: currentTreeStatus === "",
local_assets_ready: true,
assets,
evidence,
external_gates: {
forgejo_release_publication: {
status: envState("CLUSTERFLUX_FORGEJO_TOKEN") === "set" ? "ready" : "pending",
env: {
CLUSTERFLUX_FORGEJO_TOKEN: envState("CLUSTERFLUX_FORGEJO_TOKEN"),
},
},
live_service_smoke: {
status:
envState("CLUSTERFLUX_PUBLIC_RELEASE_SERVICE_ADDR") === "set" &&
envState("CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND") === "set"
? "ready"
: "pending",
env: {
CLUSTERFLUX_PUBLIC_RELEASE_SERVICE_ADDR: envState(
"CLUSTERFLUX_PUBLIC_RELEASE_SERVICE_ADDR"
),
CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND: envState(
"CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND"
),
},
},
public_release_e2e: {
status:
envState("CLUSTERFLUX_PUBLIC_RELEASE_E2E") === "set" &&
process.env.CLUSTERFLUX_PUBLIC_RELEASE_E2E === "1" &&
envState("CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND") === "set"
? "ready"
: "pending",
env: {
CLUSTERFLUX_PUBLIC_RELEASE_E2E:
process.env.CLUSTERFLUX_PUBLIC_RELEASE_E2E || "unset",
CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND: envState(
"CLUSTERFLUX_PUBLIC_RELEASE_BROWSER_OPEN_COMMAND"
),
},
},
final_evidence: {
status:
envState("CLUSTERFLUX_PUBLIC_RELEASE_FINAL") === "set" &&
process.env.CLUSTERFLUX_PUBLIC_RELEASE_FINAL === "1"
? "ready"
: "pending",
env: {
CLUSTERFLUX_PUBLIC_RELEASE_FINAL:
process.env.CLUSTERFLUX_PUBLIC_RELEASE_FINAL || "unset",
},
},
},
};
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
console.log(JSON.stringify({ report: reportPath, release_name: report.release_name }, null, 2));