Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
release_tag:
description: Existing draft release tag to recover (for example, v1.2.0)
description: Existing draft or assetless release tag to recover (for example, v1.8.0)
required: true
type: string

Expand Down Expand Up @@ -95,15 +95,23 @@ jobs:
fi

is_draft="$(jq -r '.[0].draft' <<<"${matching_releases}")"
asset_count="$(jq -r '.[0].assets | length' <<<"${matching_releases}")"
release_ref="$(jq -r '.[0].target_commitish' <<<"${matching_releases}")"
if [[ "${is_draft}" != "true" ]]; then
echo "::error::Release ${RECOVERY_TAG} is already published"
if [[ "${is_draft}" != "true" && "${asset_count}" != "0" ]]; then
echo "::error::Published release ${RECOVERY_TAG} already has assets"
exit 1
fi
if [[ ! "${release_ref}" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::Draft ${RECOVERY_TAG} is not pinned to an immutable commit SHA"
echo "::error::Release ${RECOVERY_TAG} is not pinned to an immutable commit SHA"
exit 1
fi
if [[ "${is_draft}" != "true" ]]; then
tag_ref="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RECOVERY_TAG}" --jq '.object.sha')"
if [[ "${tag_ref}" != "${release_ref}" ]]; then
echo "::error::Published release ${RECOVERY_TAG} does not match its Git tag"
exit 1
fi
fi

{
echo "should_release=true"
Expand Down Expand Up @@ -297,7 +305,7 @@ jobs:
bun-version: 1.1.0

- name: Smoke test exact npm bundle on minimum runtime
run: bun run scripts/smoke-npm-bundle.ts --expected-bun=1.1.0
run: bun run scripts/smoke-npm-bundle.ts --expected-bun=1.1.0 --scaffold-only
working-directory: cli

- name: Restore release build toolchain
Expand Down
13 changes: 10 additions & 3 deletions cli/scripts/release.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ describe("release infrastructure wiring", () => {
}
});

test("gates ordered draft publication on canonical and native verification", async () => {
test("gates ordered release publication on canonical and native verification", async () => {
const workflow = await readWorkflow("release-please.yml");
const releasePleaseConfig = JSON.parse(
await readFile(join(repositoryRoot, "release-please-config.json"), "utf8"),
Expand All @@ -519,7 +519,7 @@ describe("release infrastructure wiring", () => {

expect(releasePleaseConfig.packages["."]?.draft).toBe(true);
expect(workflow.on?.workflow_dispatch?.inputs?.release_tag).toEqual({
description: "Existing draft release tag to recover (for example, v1.2.0)",
description: "Existing draft or assetless release tag to recover (for example, v1.8.0)",
required: true,
type: "string",
});
Expand All @@ -535,7 +535,11 @@ describe("release infrastructure wiring", () => {
expect(contextScript).toContain("gh api --paginate --slurp");
expect(contextScript).toContain("releases?per_page=100");
expect(contextScript).toContain("select(.tag_name == $tag)");
expect(contextScript).toContain("already published");
expect(contextScript).toContain("asset_count=\"$(jq -r '.[0].assets | length'");
expect(contextScript).toContain('"${is_draft}" != "true" && "${asset_count}" != "0"');
expect(contextScript).toContain("already has assets");
expect(contextScript).toContain("git/ref/tags/${RECOVERY_TAG}");
expect(contextScript).toContain('"${tag_ref}" != "${release_ref}"');
expect(contextScript).not.toContain("gh release view");
expect(contextScript).toContain("immutable commit SHA");
expect(contextScript).toContain("Release recovery must run from refs/heads/main");
Expand Down Expand Up @@ -589,6 +593,9 @@ describe("release infrastructure wiring", () => {
].map((name) => workflowStepIndex(publication, name));
expect(orderedSteps.every((index) => index >= 0)).toBe(true);
expect(orderedSteps).toEqual([...orderedSteps].sort((left, right) => left - right));
expect(publication.steps?.[orderedSteps[5] ?? -1]?.run).toBe(
"bun run scripts/smoke-npm-bundle.ts --expected-bun=1.1.0 --scaffold-only",
);
const publishScript = "bun run .release-orchestration/cli/scripts/publish-release.ts";
expect(publication.steps?.[orderedSteps[9] ?? -1]?.run).toBe(`${publishScript} upload-github`);
expect(publication.steps?.[orderedSteps[10] ?? -1]?.run).toBe(`${publishScript} publish-npm`);
Expand Down
Loading