Skip to content

Commit b68801b

Browse files
authored
feat: enhance git-cliff GitHub integration and npm trusted publishing (#2)
- Update cliff.toml to support filtering commits by PR labels and include new contributor recognition in changelogs. - Add validation step in CI workflow to ensure proper changelog format and structure. - Improve publish-release.yml and version-bump.yml workflows for trusted npm publishing with OIDC. - Enhance README with detailed explanations of GitHub integration features and squash merge support. This update aims to streamline changelog generation and improve the overall release process. Co-authored-by: JSONbored <49853598+gh0stdotexe@users.noreply.github.com>
1 parent 30739ec commit b68801b

5 files changed

Lines changed: 399 additions & 44 deletions

File tree

.github/workflows/README.md

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,56 @@ Changelogs are generated using git-cliff with the following approach:
118118
3. **Format:** Follows [Keep a Changelog](https://keepachangelog.com/) format
119119
4. **Grouping:** Commits are grouped by type (Added, Fixed, Changed, etc.)
120120

121+
### GitHub Integration Features
122+
123+
When `GITHUB_TOKEN` is provided, git-cliff enhances changelogs with:
124+
125+
- **PR Links**: Automatic links to pull requests in commit messages
126+
- Format: `- commit message by @username in [#123](url)`
127+
- Works with squash merges and regular merges
128+
129+
- **Contributor Attribution**: Shows who made each change
130+
- Format: `by @username` after each commit message
131+
- Retrieved from GitHub API
132+
133+
- **First-Time Contributors**: Special recognition section
134+
- Shows new contributors separately: `### New Contributors`
135+
- Format: `- @username made their first contribution in [#123](url)`
136+
137+
- **Statistics**: Release metrics automatically calculated
138+
- Commit count (total and conventional)
139+
- Linked issues/PRs count
140+
- Days since last release
141+
142+
- **PR Labels**: Can be used for filtering
143+
- Add `skip-release-notes` label to exclude commits from changelog
144+
- Supports label-based grouping (advanced feature)
145+
146+
**Example changelog entry with GitHub integration:**
147+
148+
```markdown
149+
## [0.2.0] - 2025-12-27
150+
151+
### Added
152+
- feat: add search functionality by @username in [#42](https://github.com/JSONbored/safemocker/pull/42)
153+
- feat: improve performance by @contributor in [#43](https://github.com/JSONbored/safemocker/pull/43)
154+
155+
### New Contributors
156+
- @newuser made their first contribution in [#42](https://github.com/JSONbored/safemocker/pull/42)
157+
158+
### Statistics
159+
- 5 commits in this release
160+
- 4 conventional commits
161+
- 2 linked issues/PRs
162+
- 3 days since last release
163+
```
164+
165+
**Configuration:**
166+
167+
- `GITHUB_TOKEN` is automatically provided in workflows via `secrets.GITHUB_TOKEN`
168+
- `[remote.github]` section in `cliff.toml` configures repository details
169+
- GitHub API is used to fetch PR metadata, contributor info, and labels
170+
121171
## Manual Operations
122172

123173
### Trigger Version Bump Manually
@@ -165,18 +215,34 @@ Changelogs are generated using git-cliff with the following approach:
165215
**Solutions:**
166216
- Verify commits since last tag exist
167217
- Check git-cliff output in workflow logs
168-
- Ensure `GITHUB_TOKEN` is set (for PR links)
218+
- Ensure `GITHUB_TOKEN` is set (for PR links and GitHub integration)
169219
- Verify `cliff.toml` configuration is correct
220+
- Check that commits follow conventional commit format
221+
222+
### GitHub Integration Not Working
223+
224+
**Issue:** PR links, usernames, or contributor info not appearing in changelog.
225+
226+
**Solutions:**
227+
- Verify `GITHUB_TOKEN` is set in workflow (automatically provided via `secrets.GITHUB_TOKEN`)
228+
- Check that commits are associated with PRs (squash merges work fine)
229+
- Ensure `[remote.github]` is configured in `cliff.toml` with correct owner/repo
230+
- Verify GitHub API rate limits haven't been exceeded
231+
- Check workflow logs for GitHub API errors
232+
- Note: GitHub integration requires commits to be associated with PRs - direct commits to main won't have PR links
170233

171234
### npm Publish Fails
172235

173236
**Issue:** npm publish fails with authentication error.
174237

175238
**Solutions:**
176-
- Verify trusted publishing is configured in npm
239+
- Verify trusted publishing is configured in npm for repository and environment
177240
- Check `production` environment is set in workflow
178241
- Ensure `id-token: write` permission is set
242+
- Verify `setup-node` is configured **before** `pnpm/action-setup`
243+
- Check `registry-url: 'https://registry.npmjs.org'` is set
179244
- Verify package name and version in `package.json`
245+
- See "npm Trusted Publishing" section above for detailed troubleshooting
180246

181247
### Tag Already Exists
182248

@@ -204,13 +270,103 @@ Contains:
204270
- npm scripts (optional, for local use)
205271
- Package metadata
206272

273+
## Squash Merge Support
274+
275+
### How It Works
276+
277+
When you **squash merge** a PR, GitHub creates a **single commit** on `main` with:
278+
- **Commit message**: The PR title (should follow conventional commits: `feat:`, `fix:`, etc.)
279+
- **Commit body**: The PR description (optional)
280+
281+
**git-cliff automatically processes all commits**, including squash merge commits. The workflow:
282+
283+
1. ✅ Squash merge creates a single commit on `main`
284+
2. ✅ git-cliff processes this commit just like any other commit
285+
3. ✅ Commit message is parsed using conventional commit rules
286+
4. ✅ Changelog entry is generated based on commit type (feat → Added, fix → Fixed, etc.)
287+
288+
### Best Practices for Squash Merges
289+
290+
- **Use conventional commits in PR titles**: `feat: add feature`, `fix: resolve bug`, etc.
291+
- **Include PR references**: The commit message can include `(#123)` which will be converted to a PR link
292+
- **PR descriptions are optional**: git-cliff primarily uses the commit message (PR title)
293+
294+
### Example
295+
296+
**PR Title**: `feat: add search functionality (#42)`
297+
298+
**After squash merge**:
299+
- Creates commit: `feat: add search functionality (#42)`
300+
- git-cliff processes it as a `feat:` commit
301+
- Appears in changelog under "### Added"
302+
- PR link is automatically added: `([#42](https://github.com/JSONbored/safemocker/pull/42))`
303+
304+
### Configuration
305+
306+
The `commit_preprocessors` in `cliff.toml` handle:
307+
- Standard GitHub PR merge messages: `Merge pull request #123`
308+
- PR references in commit messages: `(#123)`
309+
- Squash merge commits (processed automatically)
310+
311+
## npm Trusted Publishing
312+
313+
### Overview
314+
315+
The release workflow uses **npm Trusted Publishing** with OIDC (OpenID Connect) for secure, tokenless authentication. This eliminates the need for `NODE_AUTH_TOKEN` secrets.
316+
317+
### Requirements
318+
319+
1. **Environment name**: Must match npm trusted publisher configuration (currently `production`)
320+
2. **Permissions**: `id-token: write` is required (already set in workflow)
321+
3. **setup-node order**: Must be configured **before** pnpm setup for OIDC to work
322+
4. **registry-url**: Must be set to `https://registry.npmjs.org`
323+
5. **--provenance flag**: Creates signed provenance statements
324+
325+
### How It Works
326+
327+
1. GitHub Actions generates an OIDC token automatically
328+
2. `setup-node@v4` with `registry-url` configures npm to use OIDC
329+
3. `npm publish --provenance` uses the OIDC token for authentication
330+
4. npm verifies the token against your trusted publisher configuration
331+
5. Package is published with signed provenance
332+
333+
### Verification
334+
335+
After publishing, the workflow verifies the package is available on npm:
336+
337+
```bash
338+
npm view "@jsonbored/safemocker@$VERSION" version
339+
```
340+
341+
If this fails, the workflow will report an error.
342+
343+
### Troubleshooting npm Trusted Publishing
344+
345+
**Issue**: npm publish fails with authentication error
346+
347+
**Solutions**:
348+
1. Verify environment name matches npm trusted publisher config (`production`)
349+
2. Check that `id-token: write` permission is set in workflow
350+
3. Ensure `setup-node` is configured before `pnpm/action-setup`
351+
4. Verify `registry-url: 'https://registry.npmjs.org'` is set
352+
5. Check npm trusted publisher configuration: https://docs.npmjs.com/trusted-publishers/
353+
354+
**Issue**: Package not found after publish
355+
356+
**Solutions**:
357+
1. Wait a few seconds - npm registry may need time to update
358+
2. Check npm registry directly: https://www.npmjs.com/package/@jsonbored/safemocker
359+
3. Verify the version number matches exactly
360+
4. Check workflow logs for publish errors
361+
207362
## Best Practices
208363

209364
1. **Use Conventional Commits:** Always use conventional commit format (`feat:`, `fix:`, etc.)
210-
2. **Squash Merges:** Use squash merges for PRs to maintain clean history
365+
2. **Squash Merges:** Use squash merges for PRs to maintain clean history (fully supported)
211366
3. **Don't Commit Changelog Manually:** Let workflows handle changelog updates
212367
4. **Test Locally:** Use `pnpm exec git-cliff --bumped-version` to preview version
213368
5. **Review Before Merging:** Check PR commits to ensure correct version bump
369+
6. **Verify npm Publishing:** Check workflow logs to ensure package was published successfully
214370

215371
## Local Development
216372

.github/workflows/ci.yml

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121

2222
steps:
2323
- name: Checkout repository
24-
uses: actions/checkout@v4
24+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
2525

2626
- name: Setup pnpm
27-
uses: pnpm/action-setup@v4
27+
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
2828
with:
2929
version: 10
3030

3131
- name: Setup Node.js
32-
uses: actions/setup-node@v4
32+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
3333
with:
3434
node-version: '20'
3535
cache: 'pnpm'
@@ -47,3 +47,27 @@ jobs:
4747
run: pnpm test:coverage
4848
continue-on-error: true
4949

50+
- name: Validate changelog format
51+
run: |
52+
if [ -f CHANGELOG.md ]; then
53+
# Verify changelog has proper structure
54+
if ! grep -q "^# Changelog" CHANGELOG.md; then
55+
echo "❌ Error: Invalid changelog header - expected '# Changelog'"
56+
exit 1
57+
fi
58+
59+
# Verify changelog follows Keep a Changelog format
60+
if ! grep -q "Keep a Changelog" CHANGELOG.md; then
61+
echo "⚠️ Warning: Changelog may not follow Keep a Changelog format"
62+
fi
63+
64+
# Verify changelog has at least one version entry or [Unreleased] section
65+
if ! grep -qE "^## \[" CHANGELOG.md; then
66+
echo "⚠️ Warning: Changelog has no version entries"
67+
fi
68+
69+
echo "✅ Changelog format validation passed"
70+
else
71+
echo "ℹ️ CHANGELOG.md not found (this is okay for new repositories)"
72+
fi
73+

.github/workflows/publish-release.yml

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@
1313
# **Triggers:**
1414
# - Tag push matching v*.*.* pattern (automatic, triggered by version-bump.yml)
1515
# - Manual workflow_dispatch (with tag input for re-publishing)
16+
#
17+
# **npm Trusted Publishing Requirements:**
18+
# CRITICAL: This workflow filename (publish-release.yml) MUST exactly match the
19+
# workflow filename configured in npm trusted publisher settings.
20+
#
21+
# Required npm trusted publisher configuration:
22+
# - Organization/User: JSONbored
23+
# - Repository: safemocker (or JSONbored/safemocker)
24+
# - Workflow filename: publish-release.yml (must match this file name exactly)
25+
# - Environment: production (must match the environment name below)
26+
#
27+
# See: https://docs.npmjs.com/trusted-publishers/
28+
# Documentation: .cursor/npm-trusted-publishing-setup.md
1629
name: Publish Release
1730

1831
on:
@@ -32,7 +45,13 @@ jobs:
3245
runs-on: ubuntu-latest
3346
timeout-minutes: 10
3447

35-
# CRITICAL: Must match npm trusted publisher environment configuration
48+
# CRITICAL: Environment name must exactly match npm trusted publisher configuration
49+
# This workflow filename (publish-release.yml) must also match npm config
50+
# Required npm settings:
51+
# - Workflow filename: publish-release.yml
52+
# - Environment: production
53+
# - Repository: JSONbored/safemocker
54+
# - Organization: JSONbored
3655
environment: production
3756

3857
permissions:
@@ -41,7 +60,7 @@ jobs:
4160

4261
steps:
4362
- name: Checkout repository
44-
uses: actions/checkout@v4
63+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
4564
with:
4665
# Full history needed for changelog extraction
4766
fetch-depth: 0
@@ -79,20 +98,28 @@ jobs:
7998
echo "TAG=$TAG" >> $GITHUB_OUTPUT
8099
echo "✅ Extracted version: $VERSION from tag: $TAG"
81100
82-
- name: Setup Node.js
83-
uses: actions/setup-node@v4
101+
- name: Setup Node.js (with OIDC for trusted publishing)
102+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
84103
with:
85104
node-version: '20'
86105
registry-url: 'https://registry.npmjs.org'
87-
cache: 'pnpm'
88-
# OIDC is automatically used when id-token: write permission is set
106+
# CRITICAL: setup-node must be configured BEFORE pnpm setup for OIDC to work correctly
107+
# OIDC token is automatically used when id-token: write permission is set
89108
# No NODE_AUTH_TOKEN needed for trusted publishing
109+
# Note: cache is configured after pnpm is installed
90110

91111
- name: Setup pnpm
92-
uses: pnpm/action-setup@v4
112+
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
93113
with:
94114
version: 10
95115

116+
- name: Configure Node.js cache
117+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
118+
with:
119+
node-version: '20'
120+
cache: 'pnpm'
121+
# Configure caching after pnpm is available
122+
96123
- name: Install dependencies
97124
run: pnpm install --frozen-lockfile
98125

@@ -128,14 +155,53 @@ jobs:
128155
129156
echo "🔍 DEBUG: Publishing @jsonbored/safemocker@$VERSION to npm..."
130157
echo "🔍 DEBUG: Using trusted publishing (OIDC) - no NODE_AUTH_TOKEN needed"
158+
echo "🔍 DEBUG: Environment: ${{ github.environment }}"
159+
echo "🔍 DEBUG: Repository: ${{ github.repository }}"
131160
132161
# For trusted publishing, npm automatically uses OIDC token from GitHub Actions
133162
# The --provenance flag creates a signed provenance statement
134163
# Ensure trusted publishing is configured in npm: https://docs.npmjs.com/trusted-publishers/
135-
npm publish --access public --provenance
164+
# Trusted publishing requires:
165+
# 1. Environment name matches npm trusted publisher config (currently: production)
166+
# 2. id-token: write permission (already set in permissions)
167+
# 3. registry-url set in setup-node (already configured)
168+
169+
if ! npm publish --access public --provenance; then
170+
echo "❌ Error: npm publish failed" >&2
171+
echo "🔍 DEBUG: Check that trusted publishing is configured in npm for:" >&2
172+
echo " - Organization/User: JSONbored" >&2
173+
echo " - Repository: ${{ github.repository }}" >&2
174+
echo " - Workflow filename: publish-release.yml (must match exactly)" >&2
175+
echo " - Environment: production (must match exactly)" >&2
176+
echo " - See: https://docs.npmjs.com/trusted-publishers/" >&2
177+
echo " - Documentation: .cursor/npm-trusted-publishing-setup.md" >&2
178+
exit 1
179+
fi
136180
137181
echo "✅ Published @jsonbored/safemocker@$VERSION to npm"
138182
183+
- name: Verify npm publish
184+
run: |
185+
set -e # Exit on error
186+
187+
VERSION="${{ steps.version.outputs.VERSION }}"
188+
189+
echo "🔍 DEBUG: Verifying package is published on npm..."
190+
191+
# Wait a moment for npm registry to update
192+
sleep 2
193+
194+
# Verify package exists on npm
195+
if ! npm view "@jsonbored/safemocker@$VERSION" version > /dev/null 2>&1; then
196+
echo "❌ Error: Package not found on npm after publish" >&2
197+
echo "🔍 DEBUG: Attempted to verify: @jsonbored/safemocker@$VERSION" >&2
198+
echo "🔍 DEBUG: This may indicate the publish failed or npm registry hasn't updated yet" >&2
199+
exit 1
200+
fi
201+
202+
PUBLISHED_VERSION=$(npm view "@jsonbored/safemocker@$VERSION" version)
203+
echo "✅ Verified: @jsonbored/safemocker@$PUBLISHED_VERSION is published on npm"
204+
139205
- name: Extract changelog for version
140206
id: changelog
141207
run: |
@@ -196,7 +262,7 @@ jobs:
196262
head -10 /tmp/release-notes.md || echo "(empty)"
197263
198264
- name: Create GitHub Release
199-
uses: softprops/action-gh-release@v2
265+
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
200266
with:
201267
tag_name: ${{ steps.version.outputs.TAG }}
202268
name: ${{ steps.version.outputs.TAG }}

0 commit comments

Comments
 (0)