From 0ecf4047418d7af5fd0737489ba99270d3140ed7 Mon Sep 17 00:00:00 2001 From: "t3-code[bot]" <269035359+t3-code[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 23:33:50 +0000 Subject: [PATCH] test(release): prevent duplicate stable release runs --- docs/operations/release.md | 4 +++- scripts/release-workflow.test.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 scripts/release-workflow.test.ts diff --git a/docs/operations/release.md b/docs/operations/release.md index 1d8768f59d0..37ba2f3eb46 100644 --- a/docs/operations/release.md +++ b/docs/operations/release.md @@ -42,7 +42,9 @@ credentials documented below: The finalize job uses them to commit and push aligned package versions to `main` as the Release App. GitHub Release publication uses the repository-scoped workflow token so it has a rate-limit quota -independent from the shared Release App installation. +independent from the shared Release App installation. Tags created with this token do not start a +second workflow run, which keeps a manually dispatched stable release from publishing twice. Keep +release publication on the workflow token unless the tag trigger strategy changes with it. ## T3 Connect relay deployment diff --git a/scripts/release-workflow.test.ts b/scripts/release-workflow.test.ts new file mode 100644 index 00000000000..cd71cec9a18 --- /dev/null +++ b/scripts/release-workflow.test.ts @@ -0,0 +1,32 @@ +// @effect-diagnostics nodeBuiltinImport:off - reads the workflow file under test. +import * as NodeFS from "node:fs"; +import * as NodePath from "node:path"; +import * as NodeURL from "node:url"; +import { assert, it } from "@effect/vitest"; +import { parse as parseYaml } from "yaml"; + +const repoRoot = NodePath.resolve(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), ".."); + +type WorkflowStep = { + readonly uses?: string; + readonly with?: Readonly>; +}; + +type Workflow = { + readonly jobs?: Readonly }>>; +}; + +it("publishes releases with GITHUB_TOKEN so generated tags do not retrigger the workflow", () => { + const workflow = parseYaml( + NodeFS.readFileSync(NodePath.join(repoRoot, ".github/workflows/release.yml"), "utf8"), + ) as Workflow; + const releaseSteps = workflow.jobs?.release?.steps ?? []; + const publishSteps = releaseSteps.filter((step) => + step.uses?.startsWith("softprops/action-gh-release@"), + ); + + assert.lengthOf(publishSteps, 2); + for (const step of publishSteps) { + assert.equal(step.with?.token, "${{ github.token }}"); + } +});