mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-07 16:35:48 +01:00
fix(release): recover duplicate provenance canary publishes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
05cb18cf28
commit
2ad29da807
4 files changed
+191
-2
No files matched your search
+1
-1
@@ -42,7 +42,7 @@
|
||||
"smoke:openclaw-docker-ui": "./scripts/smoke/openclaw-docker-ui.sh",
|
||||
"smoke:openclaw-sse-standalone": "./scripts/smoke/openclaw-sse-standalone.sh",
|
||||
"smoke:terminal-bench-loop-skill": "node scripts/smoke/terminal-bench-loop-skill-smoke.mjs",
|
||||
"test:release-registry": "node --test scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs",
|
||||
"test:release-registry": "node --test scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs scripts/release-lib.test.mjs",
|
||||
"test:e2e": "npx playwright test --config tests/e2e/playwright.config.ts",
|
||||
"test:e2e:headed": "npx playwright test --config tests/e2e/playwright.config.ts --headed",
|
||||
"test:e2e:multiuser-authenticated": "npx playwright test --config tests/e2e/playwright-multiuser-authenticated.config.ts",
|
||||
|
||||
@@ -263,6 +263,49 @@ wait_for_npm_package_version() {
|
||||
return 1
|
||||
}
|
||||
|
||||
is_npm_tlog_duplicate_error() {
|
||||
local output="$1"
|
||||
|
||||
grep -q "TLOG_CREATE_ENTRY_ERROR" <<< "$output" &&
|
||||
grep -q "equivalent entry already exists in the transparency log" <<< "$output"
|
||||
}
|
||||
|
||||
publish_package_to_npm() {
|
||||
local dist_tag="$1"
|
||||
local package_name="$2"
|
||||
local package_version="$3"
|
||||
local publish_log
|
||||
|
||||
publish_log="$(mktemp "${TMPDIR:-/tmp}/paperclip-npm-publish.XXXXXX")"
|
||||
|
||||
if pnpm publish --no-git-checks --tag "$dist_tag" --access public 2>&1 | tee "$publish_log"; then
|
||||
rm -f "$publish_log"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! is_npm_tlog_duplicate_error "$(cat "$publish_log")"; then
|
||||
rm -f "$publish_log"
|
||||
return 1
|
||||
fi
|
||||
|
||||
release_warn "npm publish hit a duplicate Sigstore transparency-log entry for ${package_name}@${package_version}."
|
||||
|
||||
if npm_package_version_exists "$package_name" "$package_version"; then
|
||||
release_warn "npm already exposes ${package_name}@${package_version}; continuing to registry verification."
|
||||
rm -f "$publish_log"
|
||||
return 0
|
||||
fi
|
||||
|
||||
release_warn "Retrying ${package_name}@${package_version} once with npm provenance disabled."
|
||||
if pnpm publish --no-git-checks --tag "$dist_tag" --access public --provenance=false; then
|
||||
rm -f "$publish_log"
|
||||
return 0
|
||||
fi
|
||||
|
||||
rm -f "$publish_log"
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_release_registry_state() {
|
||||
local attempts="${1:-12}"
|
||||
local delay_seconds="${2:-5}"
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const repoRoot = new URL("..", import.meta.url).pathname.replace(/\/$/, "");
|
||||
|
||||
function writeExecutable(path, body) {
|
||||
writeFileSync(path, body, { mode: 0o755 });
|
||||
}
|
||||
|
||||
function runPublishHelper({ pnpmMode, npmVersionExists = false }) {
|
||||
const fixtureDir = mkdtempSync(join(tmpdir(), "paperclip-release-lib-"));
|
||||
const binDir = join(fixtureDir, "bin");
|
||||
const stateDir = join(fixtureDir, "state");
|
||||
const callLog = join(fixtureDir, "calls.log");
|
||||
mkdirSync(binDir);
|
||||
mkdirSync(stateDir);
|
||||
|
||||
writeExecutable(
|
||||
join(binDir, "pnpm"),
|
||||
`#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf 'pnpm %s\\n' "$*" >> "$FAKE_CALL_LOG"
|
||||
case "$PNPM_MODE" in
|
||||
success)
|
||||
echo "published"
|
||||
exit 0
|
||||
;;
|
||||
tlog-then-success)
|
||||
if [ ! -f "$FAKE_STATE_DIR/pnpm-called" ]; then
|
||||
touch "$FAKE_STATE_DIR/pnpm-called"
|
||||
echo "npm error code TLOG_CREATE_ENTRY_ERROR"
|
||||
echo "npm error error creating tlog entry - (409) an equivalent entry already exists in the transparency log with UUID abc"
|
||||
exit 1
|
||||
fi
|
||||
case " $* " in
|
||||
*" --provenance=false "*)
|
||||
echo "published without provenance"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "retry did not disable provenance"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
tlog-always-fails)
|
||||
echo "npm error code TLOG_CREATE_ENTRY_ERROR"
|
||||
echo "npm error error creating tlog entry - (409) an equivalent entry already exists in the transparency log with UUID abc"
|
||||
exit 1
|
||||
;;
|
||||
non-tlog-failure)
|
||||
echo "npm error code E500"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
`,
|
||||
);
|
||||
|
||||
writeExecutable(
|
||||
join(binDir, "npm"),
|
||||
`#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
printf 'npm %s\\n' "$*" >> "$FAKE_CALL_LOG"
|
||||
if [ "$1" = "view" ] && [ "$NPM_VERSION_EXISTS" = "true" ]; then
|
||||
echo "1.2.3"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
`,
|
||||
);
|
||||
|
||||
const script = `
|
||||
set -euo pipefail
|
||||
source "${repoRoot}/scripts/release-lib.sh"
|
||||
publish_package_to_npm canary @paperclipai/example 1.2.3
|
||||
`;
|
||||
|
||||
let status = 0;
|
||||
let output = "";
|
||||
try {
|
||||
output = execFileSync("bash", ["-lc", script], {
|
||||
cwd: fixtureDir,
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: `${binDir}:${process.env.PATH}`,
|
||||
FAKE_CALL_LOG: callLog,
|
||||
FAKE_STATE_DIR: stateDir,
|
||||
NPM_VERSION_EXISTS: npmVersionExists ? "true" : "false",
|
||||
PNPM_MODE: pnpmMode,
|
||||
REPO_ROOT: fixtureDir,
|
||||
},
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
} catch (error) {
|
||||
status = error.status ?? 1;
|
||||
output = `${error.stdout ?? ""}${error.stderr ?? ""}`;
|
||||
}
|
||||
|
||||
return {
|
||||
calls: readFileSync(callLog, "utf8"),
|
||||
output,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
test("publish_package_to_npm returns after a successful pnpm publish", () => {
|
||||
const result = runPublishHelper({ pnpmMode: "success" });
|
||||
|
||||
assert.equal(result.status, 0);
|
||||
assert.match(result.calls, /^pnpm publish --no-git-checks --tag canary --access public$/m);
|
||||
assert.doesNotMatch(result.calls, /npm view/);
|
||||
assert.doesNotMatch(result.calls, /--provenance=false/);
|
||||
});
|
||||
|
||||
test("publish_package_to_npm retries duplicate tlog failures without provenance", () => {
|
||||
const result = runPublishHelper({ pnpmMode: "tlog-then-success" });
|
||||
|
||||
assert.equal(result.status, 0);
|
||||
assert.match(result.calls, /^npm view @paperclipai\/example@1\.2\.3 version$/m);
|
||||
assert.match(
|
||||
result.calls,
|
||||
/^pnpm publish --no-git-checks --tag canary --access public --provenance=false$/m,
|
||||
);
|
||||
});
|
||||
|
||||
test("publish_package_to_npm treats a duplicate tlog failure as complete when npm exposes the version", () => {
|
||||
const result = runPublishHelper({ pnpmMode: "tlog-always-fails", npmVersionExists: true });
|
||||
|
||||
assert.equal(result.status, 0);
|
||||
assert.match(result.calls, /^npm view @paperclipai\/example@1\.2\.3 version$/m);
|
||||
assert.doesNotMatch(result.calls, /--provenance=false/);
|
||||
});
|
||||
|
||||
test("publish_package_to_npm does not retry unrelated publish failures", () => {
|
||||
const result = runPublishHelper({ pnpmMode: "non-tlog-failure" });
|
||||
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.doesNotMatch(result.calls, /npm view/);
|
||||
assert.doesNotMatch(result.calls, /--provenance=false/);
|
||||
});
|
||||
+1
-1
@@ -258,7 +258,7 @@ else
|
||||
[ -z "$pkg_dir" ] && continue
|
||||
release_info " Publishing $pkg_name@$pkg_version"
|
||||
cd "$REPO_ROOT/$pkg_dir"
|
||||
pnpm publish --no-git-checks --tag "$DIST_TAG" --access public
|
||||
publish_package_to_npm "$DIST_TAG" "$pkg_name" "$pkg_version"
|
||||
done <<< "$VERSIONED_PACKAGE_INFO"
|
||||
release_info " ✓ Published all packages under dist-tag $DIST_TAG"
|
||||
fi
|
||||
|
||||
Reference in new issue
Block a user