mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
fix(release): publish exact-source cloud migrators on merge (#13188)
Publish exact-source shared and database migrator packages for each master merge through the existing trusted Release workflow, independently of the full release and image build. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
6a7025ebe3
commit
5cc51fad06
5 files changed
+135
-11
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { planArtifacts } from "./preview-artifacts.mjs";
|
||||
import { readFileSync, mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
@@ -24,6 +25,35 @@ test("preview request requires immutable SHA and correlation UUID", () => {
|
||||
assert.throws(() => validateRequest(sha, "not-a-request"));
|
||||
});
|
||||
|
||||
test("migrator-only planning never waits for GHCR and reuses complete exact-source packages", async () => {
|
||||
for (const available of [[], ["@paperclipai/shared"], ["@paperclipai/shared", "@paperclipai/db"]]) {
|
||||
const calls = [];
|
||||
const result = await planArtifacts(sha, { image: false, migrator: true, fetchImpl: async (url) => {
|
||||
assert.equal(new URL(url).hostname, "registry.npmjs.org");
|
||||
const name = decodeURIComponent(new URL(url).pathname.split("/")[1]);
|
||||
calls.push(name);
|
||||
return available.includes(name) ? json({ ...manifest(name), dist: { integrity: "test-integrity", tarball: "https://registry.npmjs.org/package.tgz" } }) : json({}, 404);
|
||||
} });
|
||||
assert.deepEqual(result, { image: false, packages: available.length !== 2 });
|
||||
assert.ok(calls.includes("@paperclipai/shared"));
|
||||
if (available.length) assert.ok(calls.includes("@paperclipai/db"));
|
||||
}
|
||||
});
|
||||
|
||||
test("migrator-only planning rejects registry outages and mismatched source identity", async () => {
|
||||
for (const response of [json({}, 403), json({}, 503), json({ ...manifest("@paperclipai/shared"), gitHead: "b".repeat(40) })]) {
|
||||
await assert.rejects(planArtifacts(sha, { image: false, migrator: true, fetchImpl: async () => response }));
|
||||
}
|
||||
});
|
||||
|
||||
test("ordinary preview planning still requests a missing image without publishing unsolicited packages", async () => {
|
||||
const result = await planArtifacts(sha, { fetchImpl: async (url) => {
|
||||
assert.equal(new URL(url).hostname, "ghcr.io");
|
||||
return url.includes("/token?") ? json({ token: "test-pull-token" }) : json({}, 404);
|
||||
} });
|
||||
assert.deepEqual(result, { image: true, packages: false });
|
||||
});
|
||||
|
||||
test("preview manifests carry exact source, isolated versions and shared dependency", () => {
|
||||
const pkg = manifest("@paperclipai/db");
|
||||
assert.equal(pkg.version, `0.0.0-preview.g${sha}`);
|
||||
@@ -86,6 +116,24 @@ test("preview workflow separates branch compilation from trusted publishing", ()
|
||||
assert.match(workflow, /Stack deploy \{0\} build/);
|
||||
});
|
||||
|
||||
test("merge dispatch uses the existing publisher outside full-release concurrency without claiming image readiness", () => {
|
||||
const dispatcher = readFileSync(new URL("../.github/workflows/cloud-artifacts.yml", import.meta.url), "utf8");
|
||||
const release = readFileSync(new URL("../.github/workflows/release.yml", import.meta.url), "utf8");
|
||||
assert.match(dispatcher, /branches: \[master\]/);
|
||||
assert.match(dispatcher, /github.ref == 'refs\/heads\/master'/);
|
||||
assert.match(dispatcher, /SOURCE_SHA: \$\{\{ github.sha \}\}/);
|
||||
assert.match(dispatcher, /gh workflow run release.yml .*--ref master/);
|
||||
assert.match(dispatcher, /--field channel=cloud-migrator/);
|
||||
assert.doesNotMatch(dispatcher, /actions\/checkout|id-token: write|packages: write|secrets\./);
|
||||
assert.match(release, /\(inputs.channel == 'preview' \|\| inputs.channel == 'cloud-migrator'\) && format\('\{0\}-\{1\}', inputs.channel, inputs.source_ref\)/);
|
||||
const publisher = release.split(" publish_preview:")[1].split(" image_preview:")[0];
|
||||
assert.match(publisher, /group: preview-package-publish-\$\{\{ inputs.source_ref \}\}/);
|
||||
assert.match(publisher, /cancel-in-progress: false/);
|
||||
assert.match(release, /PLAN_COMMAND: \$\{\{ inputs.channel == 'cloud-migrator' && 'plan-migrator' \|\| 'plan' \}\}/);
|
||||
const result = release.split(" result_preview:")[1].split(" verify_canary:")[0];
|
||||
assert.match(result, /always\(\) && inputs.channel == 'preview'/);
|
||||
});
|
||||
|
||||
|
||||
test("existing image reuse verifies the full revision behind the immutable tag", async () => {
|
||||
const digest = "sha256:" + "b".repeat(64);
|
||||
|
||||
Reference in new issue
Block a user