From b013655ff50c86144549d0a67ea1bfde4a3e7a8b Mon Sep 17 00:00:00 2001 From: mohamed-elkholy95 Date: Mon, 1 Jun 2026 10:05:36 -0400 Subject: [PATCH] ci: add release concurrency guards + document release gotchas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concurrency: add a per-ref concurrency group (cancel-in-progress: false — a publish must never be cancelled mid-flight) to the four v*-tag publishers that lacked one (release-pythinker-cli, linux-installer, windows-installer, homebrew-tap), so the whole v* pipeline is consistently guarded against overlapping same-tag runs. scoop-bucket, docker, and promote-release already had it. Resolves the concurrency nit deferred from #46. Docs: add a 'Release pipeline gotchas' section to AGENTS.md capturing the traps hit while shipping 0.28.0 — GitHub tag filters are glob (not regex/extglob, so ( ) are literal and v+([0-9])... matches nothing), a pushed tag runs the workflow at the tagged commit (re-tag onto the fixed commit), and main's required_conversation_resolution makes all-green checks insufficient to merge when a review thread is unresolved. [skip changelog] — CI/docs hardening, no user-facing product change. --- .github/workflows/homebrew-tap.yml | 3 +++ .github/workflows/linux-installer.yml | 3 +++ .github/workflows/release-pythinker-cli.yml | 4 ++++ .github/workflows/windows-installer.yml | 3 +++ AGENTS.md | 23 +++++++++++++++++++++ 5 files changed, 36 insertions(+) diff --git a/.github/workflows/homebrew-tap.yml b/.github/workflows/homebrew-tap.yml index 40ba7015..8697477d 100644 --- a/.github/workflows/homebrew-tap.yml +++ b/.github/workflows/homebrew-tap.yml @@ -11,6 +11,9 @@ on: required: true type: string +concurrency: + group: homebrew-tap-${{ github.ref }} + cancel-in-progress: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" diff --git a/.github/workflows/linux-installer.yml b/.github/workflows/linux-installer.yml index cfab2ab8..5ceaee3a 100644 --- a/.github/workflows/linux-installer.yml +++ b/.github/workflows/linux-installer.yml @@ -12,6 +12,9 @@ on: required: true type: string +concurrency: + group: linux-installer-${{ github.ref }} + cancel-in-progress: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" diff --git a/.github/workflows/release-pythinker-cli.yml b/.github/workflows/release-pythinker-cli.yml index 270dc98f..177d93ac 100644 --- a/.github/workflows/release-pythinker-cli.yml +++ b/.github/workflows/release-pythinker-cli.yml @@ -5,6 +5,10 @@ on: tags: - "v[0-9]+.[0-9]+.[0-9]+" +concurrency: + group: release-pythinker-cli-${{ github.ref }} + cancel-in-progress: false + permissions: contents: write diff --git a/.github/workflows/windows-installer.yml b/.github/workflows/windows-installer.yml index 106dfd42..f86fd212 100644 --- a/.github/workflows/windows-installer.yml +++ b/.github/workflows/windows-installer.yml @@ -12,6 +12,9 @@ on: required: true type: string +concurrency: + group: windows-installer-${{ github.ref }} + cancel-in-progress: false env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" diff --git a/AGENTS.md b/AGENTS.md index be30bf44..70f8e651 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -422,3 +422,26 @@ user or release workflow explicitly asks for that package. resolvable by the install scripts and the in-app updater. If a builder is re-run after promotion it flips the Release back to prerelease; recover by running `promote-release.yml` via `workflow_dispatch` for that tag. + +### Release pipeline gotchas + +Hard-won traps — re-check these before and during a release: + +- **Tag triggers use GitHub's glob filter, which is NOT regex or ksh extglob.** Every `v*`-triggered + workflow (`release-pythinker-cli`, `promote-release`, `linux-installer`, `windows-installer`, + `homebrew-tap`, `scoop-bucket`, `docker`) must filter on `"v[0-9]+.[0-9]+.[0-9]+"`. In a GitHub tag + filter `(` and `)` are literal characters, so an extglob-style pattern such as + `"v+([0-9]).+([0-9]).+([0-9])"` matches no real tag and silently fires **nothing** — the release looks + like it "did nothing" with no error anywhere. Do not "modernize" these patterns into extglob/regex. +- **A pushed tag runs the workflow definition that exists AT the tagged commit.** If you fix a release + workflow or its trigger, re-create the tag on the post-fix commit — re-pushing a tag that still points + at the pre-fix commit just re-runs the broken definition. Use an annotated tag + (`git tag -a vX.Y.Z -m ...`). Pre-flight before re-tagging: confirm nothing shipped yet + (`gh release view vX.Y.Z` is "not found" and `https://pypi.org/pypi/pythinker-code/X.Y.Z/json` is 404), + then delete the old tag locally and on origin and re-push. +- **`main` requires conversation resolution, so all-green checks are not sufficient to merge.** If + `gh pr view --json mergeStateStatus` shows `BLOCKED` while every required check is `SUCCESS`, look + for an unresolved review thread — CodeRabbit can open one even when its commit status reads "Review + skipped". Inspect via the `reviewThreads` GraphQL field, verify the finding, reply in-thread, then + `resolveReviewThread`. `main` is also squash-only (linear history, `enforce_admins` on), so merge with + `gh pr merge --squash`.