Skip to content

Commit bbd9800

Browse files
ynamiteclaude
andcommitted
feat(ci): tag-triggered npm release workflow
Adds .github/workflows/release.yml: pushes of `v*` tags build, test, smoke-test, then `npm publish --provenance` via OIDC + Trusted Publishers (no NPM_TOKEN). Dist-tag auto-derives from the version suffix (alpha/beta/next/latest); a GitHub Release is opened with notes extracted from CHANGELOG.md. README gains a "Releasing" section documenting the flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 640fd58 commit bbd9800

3 files changed

Lines changed: 119 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write
10+
id-token: write
11+
12+
concurrency:
13+
group: release-${{ github.ref }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
release:
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 15
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: pnpm/action-setup@v3
25+
with:
26+
version: 9
27+
28+
- uses: actions/setup-node@v4
29+
with:
30+
node-version: 20
31+
cache: pnpm
32+
registry-url: https://registry.npmjs.org
33+
34+
- name: Install dependencies
35+
run: pnpm install --frozen-lockfile
36+
37+
- name: Verify tag matches package.json
38+
run: |
39+
TAG_VERSION="${GITHUB_REF_NAME#v}"
40+
PKG_VERSION=$(node -p "require('./package.json').version")
41+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
42+
echo "::error::Tag $GITHUB_REF_NAME does not match package.json version $PKG_VERSION"
43+
exit 1
44+
fi
45+
46+
- name: Determine dist-tag
47+
id: disttag
48+
run: |
49+
VERSION=$(node -p "require('./package.json').version")
50+
case "$VERSION" in
51+
*-alpha*) TAG=alpha ;;
52+
*-beta*) TAG=beta ;;
53+
*-rc*) TAG=next ;;
54+
*) TAG=latest ;;
55+
esac
56+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
57+
if [ "$TAG" = "latest" ]; then
58+
echo "prerelease=false" >> "$GITHUB_OUTPUT"
59+
else
60+
echo "prerelease=true" >> "$GITHUB_OUTPUT"
61+
fi
62+
63+
- name: Build
64+
run: pnpm build
65+
66+
- name: Unit tests
67+
run: pnpm test
68+
69+
- name: Dry-run smoke tests
70+
run: bash scripts/test-run.sh
71+
72+
- name: Publish to npm
73+
run: npm publish --provenance --access public --tag ${{ steps.disttag.outputs.tag }}
74+
75+
- name: Extract release notes from CHANGELOG
76+
id: notes
77+
run: |
78+
VERSION="${GITHUB_REF_NAME#v}"
79+
awk -v ver="$VERSION" '
80+
$0 ~ "^## \\[" ver "\\]" { capture=1; next }
81+
capture && /^## \[/ { exit }
82+
capture { print }
83+
' CHANGELOG.md > /tmp/notes.md
84+
{
85+
echo 'body<<NOTES_EOF'
86+
cat /tmp/notes.md
87+
echo 'NOTES_EOF'
88+
} >> "$GITHUB_OUTPUT"
89+
90+
- name: Create GitHub Release
91+
uses: softprops/action-gh-release@v2
92+
with:
93+
name: ${{ github.ref_name }}
94+
body: ${{ steps.notes.outputs.body }}
95+
prerelease: ${{ steps.disttag.outputs.prerelease }}

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pnpm test # vitest
5353

5454
Published usage: `npx create-viterex [project-name] [--flags]`
5555

56+
Releases are tag-triggered: push a `v*` tag and `.github/workflows/release.yml` builds, tests, publishes to npm with provenance (via Trusted Publishers / OIDC — no `NPM_TOKEN`), and opens a GitHub Release. Pre-release versions (`-alpha.N` / `-beta.N` / `-rc.N`) auto-publish under the matching dist-tag; stable goes to `latest`. See the README "Releasing" section.
57+
5658
## CLI flags
5759

5860
- `--skip-db` — skip database creation
@@ -80,7 +82,7 @@ Shipped work has moved to `CHANGELOG.md`. Remaining open items are grouped by th
8082

8183
### Product / roadmap
8284

83-
- [ ] Publish to npm as `create-viterex`
85+
- [x] Publish to npm as `create-viterex`
8486
- [x] Publish `viterex_addon` to the Redaxo installer (separate repo)
8587
- [x] Swap `install-submodule-addons.ts` for a Redaxo-installer install once `viterex_addon` is published — it's the shared frontend logic and should be installed by default
8688
- [x] Ship default `templates/base/package.json.tpl` with Vite + Tailwind deps and scripts (token replacement for project name) – Update: ships with `viterex_addon` stubs instead.

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,27 @@ node dist/index.js # run locally
324324
./scripts/test-run.sh # smoke tests (--dry-run scenarios)
325325
```
326326

327+
## Releasing
328+
329+
Releases publish to npm via the `Release` GitHub Action (`.github/workflows/release.yml`), which fires on every `v*` tag. To cut a release:
330+
331+
1. Move items from `## [Unreleased]` in `CHANGELOG.md` into a new dated section: `## [X.Y.Z] - YYYY-MM-DD`.
332+
2. Bump the version and create the tag:
333+
```bash
334+
pnpm version patch # 3.0.1
335+
pnpm version minor # 3.1.0
336+
pnpm version major # 4.0.0
337+
pnpm version prerelease --preid=alpha # 3.0.0-alpha.2
338+
```
339+
3. Push commits and tags:
340+
```bash
341+
git push --follow-tags
342+
```
343+
344+
The workflow verifies the tag matches `package.json`, builds, runs tests + smoke tests, publishes to npm with [provenance](https://docs.npmjs.com/generating-provenance-statements), and opens a GitHub Release. Pre-release versions (`-alpha.N`, `-beta.N`, `-rc.N`) publish under the `alpha` / `beta` / `next` dist-tags; stable versions go to `latest`.
345+
346+
Auth is handled via npm [Trusted Publishers](https://docs.npmjs.com/trusted-publishers) — no `NPM_TOKEN` secret required. One-time setup on npmjs.com: package page → Settings → Publishing access → "Add trusted publisher" → GitHub Actions, repository `ynamite/create-viterex`, workflow `release.yml`.
347+
327348
## License
328349

329350
MIT

0 commit comments

Comments
 (0)