fix(ci): make npm bootstrap retries idempotent

This commit is contained in:
Peter Steinberger
2026-07-17 06:54:52 +01:00
parent f4f5150557
commit d8dd59bf8f
2 changed files with 65 additions and 2 deletions
+54 -2
View File
@@ -1241,8 +1241,55 @@ jobs:
PACKAGE_VERSION: ${{ matrix.plugin.version }}
run: node scripts/verify-plugin-npm-published-runtime.mjs "${PACKAGE_NAME}@${PACKAGE_VERSION}"
- name: Publish approved bootstrap tarball
- name: Check bootstrap npm package version
id: bootstrap_npm_package_version
if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap'
env:
PACKAGE_NAME: ${{ steps.publication_evidence.outputs.package_name }}
PACKAGE_VERSION: ${{ steps.publication_evidence.outputs.package_version }}
TARBALL_PATH: ${{ steps.publication_evidence.outputs.tarball_path }}
run: |
set -euo pipefail
node --input-type=module <<'NODE' >> "$GITHUB_OUTPUT"
import crypto from "node:crypto";
import fs from "node:fs";
import { fetchNpmRegistryPackumentWithRetry } from "./scripts/lib/npm-publish-plan.mjs";
const packageName = process.env.PACKAGE_NAME;
const packageVersion = process.env.PACKAGE_VERSION;
const tarball = fs.readFileSync(process.env.TARBALL_PATH);
const expectedIntegrity = `sha512-${crypto.createHash("sha512").update(tarball).digest("base64")}`;
const expectedShasum = crypto.createHash("sha1").update(tarball).digest("hex");
const registryFetch = await fetchNpmRegistryPackumentWithRetry({
packageName,
packageUrl: `https://registry.npmjs.org/${encodeURIComponent(packageName)}`,
});
if (registryFetch.status === 404) {
console.log("already_published=false");
process.exit(0);
}
if (!registryFetch.ok) {
throw new Error(`${packageName}: npm registry returned HTTP ${registryFetch.status}.`);
}
const publishedDist = registryFetch.packument?.versions?.[packageVersion]?.dist;
if (!publishedDist) {
console.log("already_published=false");
process.exit(0);
}
if (
publishedDist.integrity !== expectedIntegrity ||
publishedDist.shasum !== expectedShasum
) {
throw new Error(
`${packageName}@${packageVersion}: npm registry tarball identity does not match the approved bootstrap artifact.`,
);
}
console.error(`${packageName}@${packageVersion} already matches the approved bootstrap artifact.`);
console.log("already_published=true");
NODE
- name: Publish approved bootstrap tarball
if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap' && steps.bootstrap_npm_package_version.outputs.already_published != 'true'
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
PACKAGE_DIR: ${{ matrix.plugin.packageDir }}
@@ -1301,12 +1348,17 @@ jobs:
- name: Record Meta trusted publisher checkpoint
if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap'
env:
ALREADY_PUBLISHED: ${{ steps.bootstrap_npm_package_version.outputs.already_published }}
PACKAGE_NAME: ${{ steps.publication_evidence.outputs.package_name }}
run: |
{
echo "## Meta npm bootstrap follow-up"
echo
echo "- Published \`${PACKAGE_NAME}\` from the verified immutable tarball."
if [[ "$ALREADY_PUBLISHED" == "true" ]]; then
echo "- Verified existing \`${PACKAGE_NAME}\` against the approved immutable tarball."
else
echo "- Published \`${PACKAGE_NAME}\` from the verified immutable tarball."
fi
echo "- Configure the GitHub trusted publisher for \`plugin-npm-release.yml\` and environment \`npm-release\` before the next OIDC publish."
} >> "$GITHUB_STEP_SUMMARY"
@@ -329,8 +329,19 @@ describe("plugin npm extended-stable workflow", () => {
});
expect(publish.env?.NODE_AUTH_TOKEN).toBeUndefined();
expect(publish.env?.NPM_TOKEN).toBeUndefined();
const bootstrapCheck = step(
parsed.jobs?.publish_plugins_npm,
"Check bootstrap npm package version",
);
expect(bootstrapCheck.if).toContain("npm-token-bootstrap");
expect(bootstrapCheck.run).toContain("fetchNpmRegistryPackumentWithRetry");
expect(bootstrapCheck.run).toContain("publishedDist.integrity !== expectedIntegrity");
expect(bootstrapCheck.run).toContain("already_published=true");
const bootstrap = step(parsed.jobs?.publish_plugins_npm, "Publish approved bootstrap tarball");
expect(bootstrap.if).toContain("npm-token-bootstrap");
expect(bootstrap.if).toContain(
"steps.bootstrap_npm_package_version.outputs.already_published != 'true'",
);
expect(bootstrap.env?.NPM_TOKEN).toBe("${{ secrets.NPM_TOKEN }}");
expect(bootstrap.env?.PACKAGE_NAME).toContain("publication_evidence.outputs.package_name");
expect(bootstrap.run).not.toContain("@openclaw/meta-provider");