-
-
Notifications
You must be signed in to change notification settings - Fork 0
134 lines (129 loc) · 6.03 KB
/
Copy pathrelease.yml
File metadata and controls
134 lines (129 loc) · 6.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Runs on a v* tag push, or via workflow_dispatch ON A TAG REF (bump.yml uses the
# dispatch path because pushes made with the default GITHUB_TOKEN never trigger
# other workflows). Flow: test -> assert tag == package.json version -> publish to
# public npm (with provenance) -> cut a GitHub Release.
#
# npm publish is SKIPPED with a warning — not failed — when the NPM_TOKEN secret is
# missing, so the GitHub Release is still created. Add an npm "Automation" token as
# the NPM_TOKEN repo secret to enable publishing. See docs/RELEASING.md.
name: Release
on:
push:
tags: ["v*"]
workflow_dispatch: # dispatched by bump.yml with --ref vX.Y.Z; must target a tag
# A tag can arrive twice for the same version — the push (tags: v*) AND bump.yml's
# explicit `gh workflow run release.yml --ref vX.Y.Z` dispatch. Serialize per-ref so
# the two never race a double publish; the loser queues (never cancels a mid-publish).
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
# Publish only after the FULL quality gate passes — not just `npm test` (P0-11).
gate:
uses: ./.github/workflows/reusable-quality-gate.yml
release:
needs: gate
runs-on: ubuntu-latest
permissions:
contents: write # create the GitHub Release
id-token: write # npm provenance (supply-chain attestation)
env:
# secrets are not directly usable in step `if:`, so surface presence here.
HAS_NPM_TOKEN: ${{ secrets.NPM_TOKEN != '' }}
steps:
- name: Assert this run targets a v* tag
if: github.ref_type != 'tag'
run: |
echo "::error::Release must run on a v* tag ref (got '${{ github.ref }}'). Use the 'Bump version' workflow to cut one."
exit 1
- uses: actions/checkout@v7
with:
fetch-depth: 0 # full history so release notes can diff from the last tag
- uses: actions/[email protected]
with:
node-version: 22
registry-url: "https://registry.npmjs.org"
cache: npm
- run: npm ci
- name: Assert tag matches package.json version
env:
TAG: ${{ github.ref_name }}
run: |
PKG="v$(node -p "require('./package.json').version")"
if [ "$TAG" != "$PKG" ]; then
echo "::error::Tag $TAG does not match package.json version $PKG. Re-run the 'Bump version' workflow instead of tagging by hand."
exit 1
fi
- name: Publish to npm (with provenance)
if: env.HAS_NPM_TOKEN == 'true'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# Idempotent: skip when the version is already on the registry, so re-running a
# wedged release (or the serialized second tag event) never dies on "cannot
# publish over the previously published version".
run: |
PKG=$(node -p "require('./package.json').name")
VER=$(node -p "require('./package.json').version")
if npm view "$PKG@$VER" version >/dev/null 2>&1; then
echo "::notice::$PKG@$VER is already on npm — skipping publish (idempotent re-run)."
else
npm publish --provenance --access public
fi
- name: Skip npm publish (NPM_TOKEN not set)
if: env.HAS_NPM_TOKEN != 'true'
run: |
echo "::warning::NPM_TOKEN secret is not set — SKIPPING npm publish. The GitHub Release is still created. To publish, add an npm Automation token as the NPM_TOKEN repo secret (Settings -> Secrets and variables -> Actions) and re-run this workflow. See docs/RELEASING.md."
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
# --generate-notes builds notes from merged PRs/commits since the previous tag,
# so it works with lightweight OR annotated tags (unlike --notes-from-tag).
# Idempotent: skip if the Release already exists (re-run recovery).
run: |
if gh release view "$TAG" >/dev/null 2>&1; then
echo "::notice::GitHub Release $TAG already exists — skipping creation."
else
gh release create "$TAG" --title "$TAG" --generate-notes --verify-tag
fi
- name: Verify the release loop closed (fail loudly on a wedge)
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
# H1 closed-loop check: a tag must produce BOTH a GitHub Release and (when
# publishing is enabled) an npm version. Historically release.yml could wedge
# after the tag landed and nothing alerted — orphan tags v0.22.2/v0.23.2/v0.24.0.
# This step turns that silent wedge into a red, notified job failure; re-running
# the workflow (steps above are idempotent) then completes whatever is missing.
run: |
fail=0
if gh release view "$TAG" >/dev/null 2>&1; then
echo "GitHub Release $TAG: present."
else
echo "::error::No GitHub Release exists for $TAG after the release job."
fail=1
fi
if [ "$HAS_NPM_TOKEN" = "true" ]; then
PKG=$(node -p "require('./package.json').name")
VER=$(node -p "require('./package.json').version")
ok=0
for _ in 1 2 3 4 5 6; do
if npm view "$PKG@$VER" version >/dev/null 2>&1; then ok=1; break; fi
sleep 10 # let the registry reflect a just-published version
done
if [ "$ok" = "1" ]; then
echo "npm $PKG@$VER: present."
else
echo "::error::npm does not serve $PKG@$VER after publish (registry wedge?)."
fail=1
fi
else
echo "::warning::NPM_TOKEN not set — verified the GitHub Release only, not npm."
fi
if [ "$fail" != "0" ]; then
echo "::error::Release loop did NOT close for $TAG — do not treat it as shipped. Re-run this workflow to complete the missing artifact."
exit 1
fi
echo "Release loop verified for $TAG."