Repository navigation
302 lines (269 loc) · 11.2 KB
/
Copy pathrelease.yml
File metadata and controls
302 lines (269 loc) · 11.2 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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
name: Release
on:
push:
tags:
- 'v*.*.*'
# Tag `vX.Y.Z` → publishes `@codeam/ide-core`, `@codeam/ide-web`, and
# `@codeam/ide-native` to npm under a single version line, then drafts a
# GitHub Release with git-cliff-generated notes drawn from the conventional
# commits since the previous tag. Same shape as the codeagent-mobile-clients
# release workflow so anyone reading either repo's changelog sees an
# identical structure.
#
# Topology: `setup` validates the tag and exposes the version as a job
# output. `publish-core` runs first because `web` and `native` depend on
# core at the same version, then `publish-web` and `publish-native` fan
# out in parallel. The final `release` job runs git-cliff against the
# commits between the previous and current tag, prepends the notes to
# each package's CHANGELOG.md, commits that back to main with
# `[skip ci]`, and creates the GitHub Release with the generated notes.
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
setup:
name: Validate tag and expose version
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
steps:
- name: Extract version from tag
id: version
run: |
VERSION="${GITHUB_REF_NAME#v}"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Tag ${GITHUB_REF_NAME} does not match vMAJOR.MINOR.PATCH[-prerelease]"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
if [[ "$VERSION" == *-* ]]; then
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
fi
echo "Publishing version: $VERSION"
publish-core:
name: Publish @codeam/ide-core to npm
needs: setup
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
registry-url: https://registry.npmjs.org
- name: Patch package version
working-directory: packages/core
run: npm version "${{ needs.setup.outputs.version }}" --no-git-tag-version --allow-same-version
- name: Install workspace deps
run: npm ci
- name: Build
working-directory: packages/core
run: npm run build
- name: Publish
working-directory: packages/core
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.setup.outputs.version }}
run: |
# Idempotent publish — skip if this exact version is
# already in the registry. npm itself returns 403 on
# republish, which is correct semantics but causes
# spurious red runs whenever a tag is retried for any
# reason (e.g. an earlier job partially failed and the
# operator force-pushed the tag). With this guard the
# downstream changelog + GitHub Release jobs still run.
if npm view "@codeam/ide-core@$VERSION" version >/dev/null 2>&1; then
echo "::notice::@codeam/ide-core@$VERSION already published — skipping npm publish"
exit 0
fi
npm publish --access public
publish-web:
name: Publish @codeam/ide-web to npm
needs: [setup, publish-core]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
registry-url: https://registry.npmjs.org
- name: Patch package versions (web depends on core at the same version)
run: |
VERSION="${{ needs.setup.outputs.version }}"
npm --workspace @codeam/ide-core version "$VERSION" --no-git-tag-version --allow-same-version
npm --workspace @codeam/ide-web version "$VERSION" --no-git-tag-version --allow-same-version
# Pin the @codeam/ide-core dependency in @codeam/ide-web to the same version.
node -e "
const fs = require('fs');
const p = 'packages/web/package.json';
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
if (j.dependencies && j.dependencies['@codeam/ide-core']) {
j.dependencies['@codeam/ide-core'] = '${{ needs.setup.outputs.version }}';
fs.writeFileSync(p, JSON.stringify(j, null, 2) + '\n');
}
"
- name: Install workspace deps
# `npm install` rather than `npm ci` because the patch step
# bumped versions in package.json but didn't touch the
# lockfile — `npm ci` would reject the mismatch.
run: npm install --no-audit --no-fund
- name: Build core (needed for @codeam/ide-web dts resolution)
working-directory: packages/core
run: npm run build
- name: Purge nested @codeam/ide-core copy
# npm install was hoisting a registry copy of @codeam/ide-core
# (an OLDER 0.1.0 picked up from the lockfile) into
# packages/web/node_modules/@codeam/ide-core ALONGSIDE the
# workspace symlink at the root. TypeScript's nearest-node_modules
# resolver found the nested 0.1.0 first and ignored the symlinked
# 0.2.x — the dts step failed claiming the new exports didn't
# exist. Delete the nested directory so resolution falls back to
# the workspace symlink at the repo root.
run: rm -rf packages/web/node_modules/@codeam
- name: Build web
working-directory: packages/web
run: npm run build
- name: Publish
working-directory: packages/web
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.setup.outputs.version }}
run: |
if npm view "@codeam/ide-web@$VERSION" version >/dev/null 2>&1; then
echo "::notice::@codeam/ide-web@$VERSION already published — skipping npm publish"
exit 0
fi
npm publish --access public
publish-native:
name: Publish @codeam/ide-native to npm
needs: [setup, publish-core]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
registry-url: https://registry.npmjs.org
- name: Patch package versions (native depends on core at the same version)
run: |
VERSION="${{ needs.setup.outputs.version }}"
npm --workspace @codeam/ide-core version "$VERSION" --no-git-tag-version --allow-same-version
npm --workspace @codeam/ide-native version "$VERSION" --no-git-tag-version --allow-same-version
node -e "
const fs = require('fs');
const p = 'packages/native/package.json';
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
if (j.dependencies && j.dependencies['@codeam/ide-core']) {
j.dependencies['@codeam/ide-core'] = '${{ needs.setup.outputs.version }}';
fs.writeFileSync(p, JSON.stringify(j, null, 2) + '\n');
}
"
- name: Install workspace deps
# See the equivalent step in publish-web for the rationale.
run: npm install --no-audit --no-fund
- name: Build core (needed for @codeam/ide-native dts resolution)
working-directory: packages/core
run: npm run build
- name: Purge nested @codeam/ide-core copy
# Same root cause as publish-web's equivalent step: npm
# hoists a stale registry 0.1.0 into the per-package
# node_modules alongside the workspace symlink, and
# TypeScript's nearest-folder resolver picks the wrong one.
run: rm -rf packages/native/node_modules/@codeam
- name: Build native
working-directory: packages/native
run: npm run build
- name: Publish
working-directory: packages/native
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.setup.outputs.version }}
run: |
if npm view "@codeam/ide-native@$VERSION" version >/dev/null 2>&1; then
echo "::notice::@codeam/ide-native@$VERSION already published — skipping npm publish"
exit 0
fi
npm publish --access public
release:
name: Draft GitHub Release + commit changelog
needs: [setup, publish-core, publish-web, publish-native]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout (full history for git-cliff)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate release notes (git-cliff)
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --current --strip all
env:
OUTPUT: RELEASE_NOTES.md
- name: Prepend notes to per-package CHANGELOGs + commit back to main
run: |
set -euo pipefail
if [ ! -s RELEASE_NOTES.md ]; then
echo "git-cliff produced no release notes (no conventional commits between tags). Nothing to commit."
exit 0
fi
# The tag checkout has no in-flight version bumps in this job —
# those happen in publish-* and aren't pushed. Switch to main
# cleanly so the changelog commit lands there.
git fetch origin main
git checkout main
for PKG in core web native; do
FILE="packages/$PKG/CHANGELOG.md"
# Insert the new section right above the first existing "## ["
# heading, preserving any prior history. If the file is just
# the header (first release), append.
awk -v notes_file="RELEASE_NOTES.md" '
BEGIN { inserted = 0 }
/^## \[/ && !inserted {
while ((getline line < notes_file) > 0) print line
print ""
inserted = 1
}
{ print }
END {
if (!inserted) {
while ((getline line < notes_file) > 0) print line
}
}
' "$FILE" > "$FILE.new"
mv "$FILE.new" "$FILE"
done
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add packages/*/CHANGELOG.md
if git diff --cached --quiet; then
echo "Nothing new to add to CHANGELOGs."
exit 0
fi
git commit -m "chore(changelog): notes for v${{ needs.setup.outputs.version }} [skip ci]"
git push origin main
- name: Create GitHub Release
if: always() && needs.setup.outputs.version != ''
uses: softprops/action-gh-release@v2
with:
name: v${{ needs.setup.outputs.version }}
body_path: RELEASE_NOTES.md
prerelease: ${{ needs.setup.outputs.is_prerelease == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}