Sync public tree to 13dc821

This commit is contained in:
Michel Paulissen 2026-07-04 13:10:21 +02:00
parent e2bc145f60
commit 1dad0df5c4
6 changed files with 101 additions and 21 deletions

View file

@ -1,7 +1,7 @@
{ {
"kind": "disasmer-filtered-public-tree", "kind": "disasmer-filtered-public-tree",
"source_commit": "c852e659435428b69cf3991a3fad015f60d121ff", "source_commit": "13dc821c5a4ff2b093b777374ebf510197ab5500",
"release_name": "dryrun-c852e6594354", "release_name": "dryrun-13dc821c5a4f",
"filtered_out": [ "filtered_out": [
"private/**", "private/**",
"experiments/**", "experiments/**",

View file

@ -136,6 +136,8 @@ for (const [name, pattern] of [
["release publisher rejects stale release manifests", /manifest\.source_commit[\s\S]*expectedSourceCommit\(\)/], ["release publisher rejects stale release manifests", /manifest\.source_commit[\s\S]*expectedSourceCommit\(\)/],
["release publisher checks manifest kind", /manifest\.kind !== "disasmer-public-release-dryrun"/], ["release publisher checks manifest kind", /manifest\.kind !== "disasmer-public-release-dryrun"/],
["release publisher requires public tree push", /public tree must be pushed before publishing the Forgejo Release/], ["release publisher requires public tree push", /public tree must be pushed before publishing the Forgejo Release/],
["release publisher accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
["release publisher targets external public tree commit", /function publicTreeCommit\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_COMMIT/],
["release publisher writes evidence report", /public-release-dryrun-forgejo-release\.json/], ["release publisher writes evidence report", /public-release-dryrun-forgejo-release\.json/],
["release publisher records reused assets", /reused_assets:/], ["release publisher records reused assets", /reused_assets:/],
]) { ]) {
@ -144,8 +146,10 @@ for (const [name, pattern] of [
for (const [name, pattern] of [ for (const [name, pattern] of [
["preflight rejects stale release manifests", /manifest\.source_commit[\s\S]*currentSourceCommit/], ["preflight rejects stale release manifests", /manifest\.source_commit[\s\S]*currentSourceCommit/],
["preflight verifies public branch commit", /remoteMain[\s\S]*manifest\.public_tree_publish\.commit/], ["preflight accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
["preflight verifies public branch commit", /remoteMain[\s\S]*publicTreeCommit/],
["preflight verifies local asset checksums", /parseSha256Sums[\s\S]*checksum mismatch/], ["preflight verifies local asset checksums", /parseSha256Sums[\s\S]*checksum mismatch/],
["preflight records public tree push source", /public_tree_push_source/],
["preflight records pending external gates", /external_gates:[\s\S]*forgejo_release_publication[\s\S]*public_release_e2e/], ["preflight records pending external gates", /external_gates:[\s\S]*forgejo_release_publication[\s\S]*public_release_e2e/],
["preflight writes evidence report", /public-release-dryrun-preflight\.json/], ["preflight writes evidence report", /public-release-dryrun-preflight\.json/],
]) { ]) {
@ -155,6 +159,7 @@ for (const [name, pattern] of [
for (const [name, pattern] of [ for (const [name, pattern] of [
["e2e runner requires explicit opt-in", /DISASMER_PUBLIC_RELEASE_DRYRUN_E2E=1/], ["e2e runner requires explicit opt-in", /DISASMER_PUBLIC_RELEASE_DRYRUN_E2E=1/],
["e2e runner rejects stale release manifests", /manifest\.source_commit[\s\S]*expectedSourceCommit\(\)/], ["e2e runner rejects stale release manifests", /manifest\.source_commit[\s\S]*expectedSourceCommit\(\)/],
["e2e runner accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
["e2e runner downloads Forgejo Release assets", /downloadReleaseAssets/], ["e2e runner downloads Forgejo Release assets", /downloadReleaseAssets/],
["e2e runner verifies SHA256SUMS", /verifyChecksums/], ["e2e runner verifies SHA256SUMS", /verifyChecksums/],
["e2e runner clones public repo", /"git", \["clone", "--depth", "1"/], ["e2e runner clones public repo", /"git", \["clone", "--depth", "1"/],
@ -195,6 +200,7 @@ for (const [name, pattern] of [
["final evidence requires public e2e report", /public-release-dryrun-e2e\.json/], ["final evidence requires public e2e report", /public-release-dryrun-e2e\.json/],
["final evidence verifies Forgejo host", /forgejoHost = "git\.michelpaulissen\.com"/], ["final evidence verifies Forgejo host", /forgejoHost = "git\.michelpaulissen\.com"/],
["final evidence verifies default operator", /serviceEndpoint = "https:\/\/disasmer\.michelpaulissen\.com:9443"/], ["final evidence verifies default operator", /serviceEndpoint = "https:\/\/disasmer\.michelpaulissen\.com:9443"/],
["final evidence accepts external public tree push", /function publicTreeAlreadyPushed\(manifest\)[\s\S]*DISASMER_PUBLIC_TREE_ALREADY_PUSHED/],
["final evidence requires private hosted coordinator", /operator_implementation[\s\S]*"private-hosted-coordinator"/], ["final evidence requires private hosted coordinator", /operator_implementation[\s\S]*"private-hosted-coordinator"/],
["final evidence requires service private coordinator marker", /service\.operator_implementation[\s\S]*"private-hosted-coordinator"/], ["final evidence requires service private coordinator marker", /service\.operator_implementation[\s\S]*"private-hosted-coordinator"/],
["final evidence requires current service smoke source", /service\.source_commit[\s\S]*manifest\.source_commit/], ["final evidence requires current service smoke source", /service\.source_commit[\s\S]*manifest\.source_commit/],
@ -209,7 +215,7 @@ for (const [name, pattern] of [
["final evidence requires public coordinator validation", /public_coordinator_validated/], ["final evidence requires public coordinator validation", /public_coordinator_validated/],
["final evidence requires standalone public coordinator", /standalone-public-coordinator/], ["final evidence requires standalone public coordinator", /standalone-public-coordinator/],
["final evidence requires released live DAP", /released_live_dap_verified/], ["final evidence requires released live DAP", /released_live_dap_verified/],
["final evidence requires pushed public tree", /public_tree_publish[\s\S]*pushed/], ["final evidence requires pushed public tree", /publicTreeAlreadyPushed\(manifest\)/],
["final evidence requires release assets", /downloaded_release_assets/], ["final evidence requires release assets", /downloaded_release_assets/],
["final evidence requires VS Code debugger", /vscode_debugger_verified/], ["final evidence requires VS Code debugger", /vscode_debugger_verified/],
["final evidence writes final report", /public-release-dryrun-final\.json/], ["final evidence writes final report", /public-release-dryrun-final\.json/],

View file

@ -110,6 +110,13 @@ function expectedSourceCommit() {
); );
} }
function publicTreeAlreadyPushed(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) ||
process.env.DISASMER_PUBLIC_TREE_ALREADY_PUSHED === "1"
);
}
function sha256File(file) { function sha256File(file) {
return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); return crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
} }
@ -844,7 +851,7 @@ async function main() {
"public release e2e must use release assets prepared from the current acceptance commit" "public release e2e must use release assets prepared from the current acceptance commit"
); );
assert.strictEqual(manifest.default_operator_endpoint, serviceEndpoint); assert.strictEqual(manifest.default_operator_endpoint, serviceEndpoint);
assert.strictEqual(manifest.public_tree_publish.pushed, true); assert.strictEqual(publicTreeAlreadyPushed(manifest), true);
assert.strictEqual(forgejoReport.release_name, manifest.release_name); assert.strictEqual(forgejoReport.release_name, manifest.release_name);
const addr = parseAddr(serviceAddr); const addr = parseAddr(serviceAddr);

View file

@ -105,6 +105,23 @@ function assertFilteredOut(manifest) {
} }
} }
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) { function commandOutput(command, args) {
try { try {
return require("child_process") return require("child_process")
@ -140,12 +157,12 @@ assertDnsState(manifest.dns_publication_state, "manifest");
assert(manifest.resolver_override, "manifest must record resolver override"); assert(manifest.resolver_override, "manifest must record resolver override");
assertFilteredOut(manifest); assertFilteredOut(manifest);
assert.strictEqual( assert.strictEqual(
manifest.public_tree_publish && manifest.public_tree_publish.pushed, publicTreeAlreadyPushed(manifest),
true, true,
"filtered public tree must be pushed to Forgejo" "filtered public tree must be pushed to Forgejo"
); );
assertIncludes( assertIncludes(
manifest.public_repo_url || manifest.public_repo_remote || "", publicRepoRemote(manifest),
forgejoHost, forgejoHost,
"manifest public repository must point at Forgejo" "manifest public repository must point at Forgejo"
); );

View file

@ -69,6 +69,38 @@ function remoteHead(remote) {
return head && head.split(/\s+/)[0]; return head && head.split(/\s+/)[0];
} }
function publicTreeAlreadyPushed(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) ||
process.env.DISASMER_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.DISASMER_PUBLIC_REPO_REMOTE ||
process.env.DISASMER_PUBLIC_REPO_URL ||
null
);
}
function publicTreeCommitForManifest(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.commit) ||
process.env.DISASMER_PUBLIC_TREE_COMMIT ||
process.env.DISASMER_PUBLIC_RELEASE_TARGET ||
null
);
}
function envState(name) { function envState(name) {
return process.env[name] ? "set" : "unset"; return process.env[name] ? "set" : "unset";
} }
@ -111,19 +143,23 @@ assert.strictEqual(
); );
assert.strictEqual(manifest.source_tree_clean, true, "public release prep must start clean"); assert.strictEqual(manifest.source_tree_clean, true, "public release prep must start clean");
assert.strictEqual( assert.strictEqual(
manifest.public_tree_publish && manifest.public_tree_publish.pushed, publicTreeAlreadyPushed(manifest),
true, true,
"filtered public tree must be pushed to Forgejo before release publication" "filtered public tree must be pushed to Forgejo before release publication"
); );
const publicRepoRemote = manifest.public_repo_url || manifest.public_repo_remote; const publicRepoRemote = publicRepoRemoteForManifest(manifest);
assert(publicRepoRemote, "manifest must record public repository URL or remote"); assert(publicRepoRemote, "manifest must record public repository URL or remote");
const publicTreeCommit = publicTreeCommitForManifest(manifest);
const remoteMain = remoteHead(publicRepoRemote); const remoteMain = remoteHead(publicRepoRemote);
assert.strictEqual( assert(remoteMain, "Forgejo public repository main branch must be readable");
remoteMain, if (publicTreeCommit) {
manifest.public_tree_publish.commit, assert.strictEqual(
"Forgejo public repository main branch must match the prepared public tree commit" 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"); assert(Array.isArray(manifest.assets) && manifest.assets.length > 0, "manifest assets missing");
const checksumAsset = manifest.assets.find((asset) => asset.name === "SHA256SUMS"); const checksumAsset = manifest.assets.find((asset) => asset.name === "SHA256SUMS");
@ -176,7 +212,8 @@ const report = {
kind: "disasmer-public-release-dryrun-preflight", kind: "disasmer-public-release-dryrun-preflight",
source_commit: currentSourceCommit, source_commit: currentSourceCommit,
release_name: manifest.release_name, release_name: manifest.release_name,
public_tree_commit: manifest.public_tree_publish.commit, public_tree_commit: publicTreeCommit || remoteMain,
public_tree_push_source: publicTreePushSource(manifest),
public_repo_url: publicRepoRemote, public_repo_url: publicRepoRemote,
public_repo_remote_head: remoteMain, public_repo_remote_head: remoteMain,
source_tree_clean: currentTreeStatus === "", source_tree_clean: currentTreeStatus === "",

View file

@ -98,6 +98,22 @@ function resolveRepoIdentity(manifest) {
} }
} }
function publicTreeAlreadyPushed(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.pushed === true) ||
process.env.DISASMER_PUBLIC_TREE_ALREADY_PUSHED === "1"
);
}
function publicTreeCommit(manifest) {
return (
(manifest.public_tree_publish && manifest.public_tree_publish.commit) ||
process.env.DISASMER_PUBLIC_TREE_COMMIT ||
process.env.DISASMER_PUBLIC_RELEASE_TARGET ||
null
);
}
function apiPath(pathname) { function apiPath(pathname) {
return `/api/v1${pathname}`; return `/api/v1${pathname}`;
} }
@ -190,8 +206,7 @@ async function createOrReuseRelease(manifest) {
return { release: found, created: false }; return { release: found, created: false };
} }
const targetCommitish = const targetCommitish =
(manifest.public_tree_publish && manifest.public_tree_publish.commit) || publicTreeCommit(manifest) ||
process.env.DISASMER_PUBLIC_RELEASE_TARGET ||
"main"; "main";
const body = [ const body = [
"Disasmer public release dry run.", "Disasmer public release dry run.",
@ -264,11 +279,8 @@ async function main() {
"public release manifest is stale; regenerate it for the current acceptance commit before publishing the Forgejo Release" "public release manifest is stale; regenerate it for the current acceptance commit before publishing the Forgejo Release"
); );
} }
const publicTreeAlreadyPushed =
process.env.DISASMER_PUBLIC_TREE_ALREADY_PUSHED === "1";
if ( if (
!publicTreeAlreadyPushed && !publicTreeAlreadyPushed(manifest)
(!manifest.public_tree_publish || manifest.public_tree_publish.pushed !== true)
) { ) {
throw new Error( throw new Error(
"public tree must be pushed before publishing the Forgejo Release; run prepare-public-release-dryrun.js with DISASMER_PUBLISH_PUBLIC_TREE=1" "public tree must be pushed before publishing the Forgejo Release; run prepare-public-release-dryrun.js with DISASMER_PUBLISH_PUBLIC_TREE=1"
@ -306,6 +318,7 @@ async function main() {
release_created: created, release_created: created,
default_operator_endpoint: manifest.default_operator_endpoint, default_operator_endpoint: manifest.default_operator_endpoint,
public_tree_identity: manifest.public_tree_identity, public_tree_identity: manifest.public_tree_identity,
public_tree_commit: publicTreeCommit(manifest),
source_commit: manifest.source_commit, source_commit: manifest.source_commit,
uploaded_assets: uploaded.map((asset) => ({ uploaded_assets: uploaded.map((asset) => ({
id: asset.id, id: asset.id,