forked from colbymchenry/codegraph
-
Notifications
You must be signed in to change notification settings - Fork 0
204 lines (190 loc) · 9.78 KB
/
release.yml
File metadata and controls
204 lines (190 loc) · 9.78 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
name: Release
# Manually triggered ("Run workflow"). On trigger it:
# 1. reads the version from package.json,
# 2. promotes `## [Unreleased]` content into `## [<version>]` in
# CHANGELOG.md (and commits + pushes that change back to main), so
# the published release notes are never sparse just because the
# maintainer didn't pre-stage the [<version>] block by hand,
# 3. builds a self-contained bundle for every platform (one runner — there's no
# native compilation, so cross-packaging is fine),
# 4. creates the GitHub Release (tag v<version>) with all archives, using the
# release notes from CHANGELOG.md,
# 5. publishes the npm thin-installer (shim + per-platform packages).
#
# Before triggering: bump package.json. CHANGELOG.md entries can live under
# `## [Unreleased]` — step 2 takes care of moving them. Set the NPM_TOKEN secret.
on:
workflow_dispatch: {}
permissions:
contents: write # create the GitHub Release + tag, push the CHANGELOG promote
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
# Default checkout is detached at a SHA; we need an actual branch
# so the CHANGELOG-promote commit knows where to push.
ref: ${{ github.ref }}
# Authenticate as the maintainer (admin), not as github-actions[bot].
# The "Require PR approval for main branch" ruleset only lets the
# Admin repo role bypass — and GitHub blocks adding the GitHub
# Actions integration to bypass_actors on user-owned (non-org)
# repos with "Actor GitHub Actions integration must be part of
# the ruleset source or owner organization." So the auto-promote
# and auto-sync `git push origin HEAD:main` steps below both fail
# under the default GITHUB_TOKEN. Using a fine-grained PAT owned
# by the admin makes the push go through cleanly. Set the
# RELEASE_PAT secret with: contents:write on this repo, no other
# scopes. Rotate per your token policy; the workflow only runs
# on manual dispatch so the blast radius is small.
token: ${{ secrets.RELEASE_PAT }}
- uses: actions/setup-node@v6
with:
node-version: 22
registry-url: https://registry.npmjs.org
- name: Sync package-lock.json if version drifted
# When the maintainer bumps the version on package.json only — for
# example via a GitHub web-UI edit — `npm ci` would refuse to run
# with `EUSAGE: npm ci can only install packages when your
# package.json and package-lock.json … are in sync`. This step
# rewrites just the lock-file's version fields (top-level + the
# `packages.""` entry) to match package.json, then auto-commits
# and pushes the result so on-disk truth on `main` stays
# consistent. Idempotent: if the lock file already matches, no
# commit is made.
run: |
set -euo pipefail
PKG_V=$(node -p "require('./package.json').version")
LOCK_V=$(node -p "require('./package-lock.json').version")
if [ "$PKG_V" = "$LOCK_V" ]; then
echo "package-lock.json already at $PKG_V — nothing to sync."
exit 0
fi
echo "Lock-file version drift: lock=$LOCK_V, package=$PKG_V. Syncing."
# `--package-lock-only` rewrites only the lock file, doesn't
# touch node_modules or actually install anything. Cheap.
npm install --package-lock-only --ignore-scripts
# Sanity: lockfile should now report the package version.
NEW_LOCK_V=$(node -p "require('./package-lock.json').version")
if [ "$NEW_LOCK_V" != "$PKG_V" ]; then
echo "::error::lock-file still at $NEW_LOCK_V after sync attempt; expected $PKG_V"; exit 1
fi
if git diff --quiet -- package-lock.json; then
echo "lock file unchanged after sync? bailing"; exit 1
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add package-lock.json
git commit -m "release: sync package-lock.json to ${PKG_V}" -m "[skip ci] Auto-generated by Release workflow."
git push origin "HEAD:${GITHUB_REF#refs/heads/}"
- run: npm ci
- name: Ensure zip/unzip
run: sudo apt-get update -qq && sudo apt-get install -y -qq zip unzip
- name: Resolve version
id: ver
run: echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
- name: Promote [Unreleased] → [<version>] in CHANGELOG.md
# Idempotent: a no-op if [Unreleased] is empty OR if the previous
# run already moved everything. Auto-commit + push the change back
# so the version block on main is the source of truth going
# forward (and so subsequent extract-release-notes.mjs calls
# surface the full content even if this run is re-triggered).
run: |
set -euo pipefail
V="${{ steps.ver.outputs.version }}"
before=$(git rev-parse HEAD)
node scripts/prepare-release.mjs "$V"
if git diff --quiet -- CHANGELOG.md; then
echo "CHANGELOG.md unchanged — nothing to commit."
else
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
git commit -m "docs(changelog): promote [Unreleased] into [${V}]" -m "[skip ci] Auto-generated by Release workflow."
# Push to the branch the workflow was triggered on (main).
git push origin "HEAD:${GITHUB_REF#refs/heads/}"
fi
- name: Build all platform bundles
run: |
for t in darwin-arm64 darwin-x64 linux-x64 linux-arm64 win32-x64 win32-arm64; do
bash scripts/build-bundle.sh "$t"
done
ls -lh release
- name: Generate SHA256SUMS
# Published as a release asset; the npm launcher verifies downloaded
# bundles against it (basenames only, so its path.basename match works).
run: |
( cd release && sha256sum codegraph-* > SHA256SUMS )
cat release/SHA256SUMS
- name: Release notes from CHANGELOG.md
# The [<version>] block was guaranteed-populated by the
# "Promote" step above, so the [Unreleased] fallback should
# never be needed in practice. Kept for defense-in-depth.
run: |
V="${{ steps.ver.outputs.version }}"
node scripts/extract-release-notes.mjs "$V" > notes.md 2>/dev/null \
|| node scripts/extract-release-notes.mjs Unreleased > notes.md 2>/dev/null || true
if [ ! -s notes.md ]; then
echo "::error::No release notes in CHANGELOG.md for [$V] or [Unreleased]."
exit 1
fi
echo "----- release notes -----"; cat notes.md
- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="v${{ steps.ver.outputs.version }}"
# Idempotent: create the release once, otherwise (re-run) refresh assets.
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" release/codegraph-* release/SHA256SUMS --clobber
else
gh release create "$TAG" release/codegraph-* release/SHA256SUMS --title "$TAG" --notes-file notes.md
fi
- name: Publish to npm
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
V="${{ steps.ver.outputs.version }}"
bash scripts/pack-npm.sh "$V"
# Platform packages first, then the main shim (which depends on them).
# Skip any already on the registry so a re-run only fills in gaps.
for dir in release/npm/codegraph-* release/npm/main; do
name=$(node -p "require('./$dir/package.json').name")
if npm view "$name@$V" version >/dev/null 2>&1; then
echo "skip $name@$V (already published)"
else
echo "publishing $name@$V"
( cd "$dir" && npm publish --access public )
fi
done
- name: Verify every package is actually on the registry
run: |
V="${{ steps.ver.outputs.version }}"
# npm publish can print success without persisting; confirm against the
# registry (with retries for propagation) so green means really shipped.
for dir in release/npm/codegraph-* release/npm/main; do
name=$(node -p "require('./$dir/package.json').name")
ok=
for i in 1 2 3 4 5 6; do
if npm view "$name@$V" version >/dev/null 2>&1; then ok=1; break; fi
echo "waiting for $name@$V to appear ($i)…"; sleep 10
done
[ -n "$ok" ] || { echo "::error::$name@$V never appeared on the registry"; exit 1; }
echo "verified $name@$V"
done
- name: Sync packages to npmmirror
# npmmirror/cnpm mirror lazily and frequently never pull the per-platform
# optionalDependencies on their own, so `npm i` there fails with
# "no prebuilt bundle" (issue #303). Nudge a sync now so mirror users get
# the bundle without waiting. Best-effort — the launcher also self-heals
# from GitHub Releases — so a mirror hiccup never fails the release.
continue-on-error: true
run: |
for dir in release/npm/codegraph-* release/npm/main; do
name=$(node -p "require('./$dir/package.json').name")
enc=$(node -p "encodeURIComponent(require('./$dir/package.json').name)")
echo "sync $name"
curl -s -X PUT "https://registry.npmmirror.com/-/package/$enc/syncs" || true
echo
done