Skip to content

Commit cda8ae3

Browse files
committed
ci: keep npm publish and GitHub releases coupled, self-heal release gaps
GitHub releases are created only by the changesets/action step, and that step has not completed cleanly on any release-triggering run since mid-June (last release: @tanstack/ai-react@0.15.5 on 2026-06-15; npm is now at 0.15.12). The "Version Packages" merge runs (#773, #778, #787, #792, #808, #813) passed the test gate and then FAILED at the "Run Changesets" step: CI ran `changeset publish` and npm advanced, but the step died before the tag-push / GitHub-release phase, so no tags (0.15.6-0.15.12 don't exist) and no releases were created. More recent runs (#814, #825) now fail even earlier, at the test gate (test:kiira), so the changesets step is never reached. Either way npm and GitHub drift apart. (The exact in-step error is no longer recoverable - those runs' logs have expired.) Changes: - Split into a `test` gate job and a `release` job (needs: test) so a flaky run blocks BOTH npm and GitHub releases together, never one without the other. - Add workflow_dispatch so a maintainer recovers a blocked release by re-running the workflow (publish is idempotent) instead of intervening by hand. - Add a self-heal step that enforces "published to npm => GitHub release exists": for any package version on npm without a release it creates the tag + release from the CHANGELOG. Runs even when the changesets step fails mid-way and on manual re-runs, so gaps self-heal - directly covering the failure mode above. - Set GITHUB_TOKEN explicitly on the changesets step and tighten permissions (top-level contents:read; write scoped to the release job).
1 parent f3144a6 commit cda8ae3

1 file changed

Lines changed: 59 additions & 6 deletions

File tree

.github/workflows/release.yml

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ name: Release
33
on:
44
push:
55
branches: [main, alpha, beta, rc]
6+
workflow_dispatch: {}
67

78
concurrency:
89
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
@@ -12,25 +13,41 @@ env:
1213
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
1314

1415
permissions:
15-
contents: write
16-
id-token: write
17-
pull-requests: write
16+
contents: read
1817

1918
jobs:
20-
release:
21-
name: Release
19+
test:
20+
name: Test
2221
if: github.repository_owner == 'TanStack'
2322
runs-on: ubuntu-latest
2423
steps:
2524
- name: Checkout
2625
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2726
with:
2827
fetch-depth: 0
29-
persist-credentials: true # release job pushes version/docs changes
28+
persist-credentials: false
3029
- name: Setup Tools
3130
uses: TanStack/config/.github/setup@190f659075ff0845850e330883eb26d7ffd0671f # main
3231
- name: Run Tests
3332
run: pnpm run test:ci
33+
34+
release:
35+
name: Release
36+
needs: test
37+
if: github.repository_owner == 'TanStack'
38+
runs-on: ubuntu-latest
39+
permissions:
40+
contents: write
41+
id-token: write
42+
pull-requests: write
43+
steps:
44+
- name: Checkout
45+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
46+
with:
47+
fetch-depth: 0
48+
persist-credentials: true
49+
- name: Setup Tools
50+
uses: TanStack/config/.github/setup@190f659075ff0845850e330883eb26d7ffd0671f # main
3451
- name: Run Changesets (version or publish)
3552
id: changesets
3653
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1.7.0
@@ -39,6 +56,42 @@ jobs:
3956
publish: pnpm run changeset:publish
4057
commit: 'ci: Version Packages'
4158
title: 'ci: Version Packages'
59+
env:
60+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61+
- name: Ensure GitHub releases for published versions
62+
if: ${{ !cancelled() && (steps.changesets.outputs.published == 'true' || steps.changesets.outcome == 'failure' || github.event_name == 'workflow_dispatch') }}
63+
env:
64+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65+
run: |
66+
set -euo pipefail
67+
for pkg in packages/*/package.json; do
68+
[ "$(jq -r '.private // false' "$pkg")" = "true" ] && continue
69+
name=$(jq -r '.name' "$pkg")
70+
version=$(jq -r '.version' "$pkg")
71+
tag="${name}@${version}"
72+
73+
if gh release view "$tag" >/dev/null 2>&1; then
74+
continue
75+
fi
76+
if ! npm view "${name}@${version}" version >/dev/null 2>&1; then
77+
continue
78+
fi
79+
80+
echo "Backfilling GitHub release for $tag"
81+
if ! git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then
82+
git tag "$tag"
83+
git push origin "refs/tags/${tag}"
84+
fi
85+
86+
notes=$(mktemp)
87+
changelog="$(dirname "$pkg")/CHANGELOG.md"
88+
if [ -f "$changelog" ]; then
89+
awk '/^## /{n++} n==1{print} n==2{exit}' "$changelog" > "$notes" || true
90+
fi
91+
[ -s "$notes" ] || echo "Release ${tag}" > "$notes"
92+
gh release create "$tag" --title "$tag" --notes-file "$notes"
93+
rm -f "$notes"
94+
done
4295
- name: Generate Docs
4396
if: steps.changesets.outputs.published == 'true'
4497
run: pnpm generate-docs

0 commit comments

Comments
 (0)