glab-setup-git-identity 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.github/workflows/release.yml +372 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.jscpd.json +20 -0
  6. package/.prettierignore +7 -0
  7. package/.prettierrc +10 -0
  8. package/CHANGELOG.md +143 -0
  9. package/LICENSE +24 -0
  10. package/README.md +455 -0
  11. package/bunfig.toml +3 -0
  12. package/deno.json +7 -0
  13. package/docs/case-studies/issue-13/README.md +195 -0
  14. package/docs/case-studies/issue-13/hive-mind-issue-960.json +23 -0
  15. package/docs/case-studies/issue-13/hive-mind-pr-961-diff.txt +773 -0
  16. package/docs/case-studies/issue-13/hive-mind-pr-961.json +126 -0
  17. package/docs/case-studies/issue-21/README.md +384 -0
  18. package/docs/case-studies/issue-21/ci-logs/run-20803315337.txt +1188 -0
  19. package/docs/case-studies/issue-21/ci-logs/run-20885464993.txt +1310 -0
  20. package/docs/case-studies/issue-21/issue-111-data.txt +15 -0
  21. package/docs/case-studies/issue-21/issue-113-data.txt +15 -0
  22. package/docs/case-studies/issue-21/pr-112-data.json +109 -0
  23. package/docs/case-studies/issue-21/pr-112-diff.patch +1336 -0
  24. package/docs/case-studies/issue-21/pr-114-data.json +126 -0
  25. package/docs/case-studies/issue-21/pr-114-diff.patch +879 -0
  26. package/docs/case-studies/issue-3/README.md +338 -0
  27. package/docs/case-studies/issue-3/created-issues.md +32 -0
  28. package/docs/case-studies/issue-3/issue-data.json +29 -0
  29. package/docs/case-studies/issue-3/original-format-release-notes.mjs +212 -0
  30. package/docs/case-studies/issue-3/reference-pr-59-diff.txt +614 -0
  31. package/docs/case-studies/issue-3/reference-pr-59.json +109 -0
  32. package/docs/case-studies/issue-3/release-v0.1.0.json +9 -0
  33. package/docs/case-studies/issue-3/repositories-with-same-script.json +22 -0
  34. package/docs/case-studies/issue-3/research-notes.md +33 -0
  35. package/docs/case-studies/issue-7/BEST-PRACTICES-COMPARISON.md +334 -0
  36. package/docs/case-studies/issue-7/FORMATTER-COMPARISON.md +649 -0
  37. package/docs/case-studies/issue-7/current-repository-analysis.json +70 -0
  38. package/docs/case-studies/issue-7/effect-template-analysis.json +178 -0
  39. package/eslint.config.js +91 -0
  40. package/examples/basic-usage.js +64 -0
  41. package/experiments/test-changeset-scripts.mjs +303 -0
  42. package/experiments/test-failure-detection.mjs +143 -0
  43. package/experiments/test-format-major-changes.mjs +49 -0
  44. package/experiments/test-format-minor-changes.mjs +52 -0
  45. package/experiments/test-format-no-hash.mjs +43 -0
  46. package/experiments/test-format-patch-changes.mjs +46 -0
  47. package/package.json +80 -0
  48. package/scripts/changeset-version.mjs +75 -0
  49. package/scripts/check-changesets.mjs +67 -0
  50. package/scripts/check-version.mjs +129 -0
  51. package/scripts/create-github-release.mjs +93 -0
  52. package/scripts/create-manual-changeset.mjs +89 -0
  53. package/scripts/detect-code-changes.mjs +194 -0
  54. package/scripts/format-github-release.mjs +83 -0
  55. package/scripts/format-release-notes.mjs +219 -0
  56. package/scripts/instant-version-bump.mjs +172 -0
  57. package/scripts/js-paths.mjs +177 -0
  58. package/scripts/merge-changesets.mjs +263 -0
  59. package/scripts/publish-to-npm.mjs +302 -0
  60. package/scripts/setup-npm.mjs +37 -0
  61. package/scripts/validate-changeset.mjs +265 -0
  62. package/scripts/version-and-commit.mjs +284 -0
  63. package/src/cli.js +386 -0
  64. package/src/index.d.ts +255 -0
  65. package/src/index.js +563 -0
  66. package/tests/index.test.js +137 -0
@@ -0,0 +1,338 @@
1
+ # Case Study: Issue #3 - Release Formatting Script Only Handles Patch Changes
2
+
3
+ ## Issue Overview
4
+
5
+ **Issue:** [#3](https://github.com/link-foundation/js-ai-driven-development-pipeline-template/issues/3)
6
+ **Title:** Release formatting script only handles Patch changes, not Minor/Major
7
+ **Status:** In Progress
8
+ **Created:** 2025-12-17
9
+
10
+ ### Problem Statement
11
+
12
+ The `scripts/format-release-notes.mjs` script only handles `### Patch Changes` sections, causing it to fail on Minor and Major releases.
13
+
14
+ **Current Behavior:**
15
+
16
+ - Section headers (### Minor Changes, ### Major Changes) remain in release notes
17
+ - PR detection is skipped
18
+ - Release formatting fails silently
19
+
20
+ **Expected Behavior:**
21
+
22
+ - Release notes should be formatted cleanly without section headers
23
+ - PR detection should work for all release types
24
+ - NPM badge should be added
25
+
26
+ ## Timeline of Events
27
+
28
+ ### December 13, 2025
29
+
30
+ 1. **Initial commit** - Template repository created with format-release-notes.mjs script
31
+ 2. **Release v0.1.0 created** - First release with Minor Changes section
32
+ 3. **Bug manifested** - Release shows "### Minor Changes" header and no PR link
33
+
34
+ ### December 16, 2025
35
+
36
+ 1. **Bug discovered in link-assistant/agent** - Issue #58 reported in downstream repository
37
+ 2. **Fix implemented** - PR #59 created with solution
38
+ 3. **Case study documented** - Comprehensive analysis in docs/case-studies/issue-58/
39
+
40
+ ### December 17, 2025
41
+
42
+ 1. **03:45 UTC** - Issue #3 created in template repository
43
+ 2. **03:47 UTC** - Comment added requesting:
44
+ - Find all repositories with same issue
45
+ - Create issues in those repositories
46
+ - Compile comprehensive case study in docs/case-studies/issue-3/
47
+ - Reconstruct timeline and root causes
48
+ - Propose solutions
49
+
50
+ ## Data Collection
51
+
52
+ ### Affected Repositories
53
+
54
+ Search revealed 4 repositories with the same script:
55
+
56
+ 1. **link-foundation/js-ai-driven-development-pipeline-template** (this repo)
57
+ - Path: scripts/format-release-notes.mjs
58
+ - URL: https://github.com/link-foundation/js-ai-driven-development-pipeline-template
59
+
60
+ 2. **link-foundation/test-anywhere**
61
+ - Path: scripts/format-release-notes.mjs
62
+ - URL: https://github.com/link-foundation/test-anywhere
63
+
64
+ 3. **link-foundation/gh-download-pull-request**
65
+ - Path: scripts/format-release-notes.mjs
66
+ - URL: https://github.com/link-foundation/gh-download-pull-request
67
+
68
+ 4. **link-foundation/gh-download-issue**
69
+ - Path: scripts/format-release-notes.mjs
70
+ - URL: https://github.com/link-foundation/gh-download-issue
71
+
72
+ ### Release v0.1.0 Data
73
+
74
+ **Release:** https://github.com/link-foundation/js-ai-driven-development-pipeline-template/releases/tag/v0.1.0
75
+
76
+ **Current Body (Problematic):**
77
+
78
+ ```markdown
79
+ ### Minor Changes
80
+
81
+ - 65d76dc: Initial template setup with complete AI-driven development pipeline
82
+
83
+ Features:
84
+ - Multi-runtime support for Node.js, Bun, and Deno
85
+ - Universal testing with test-anywhere framework
86
+ - Automated release workflow with changesets
87
+ - GitHub Actions CI/CD pipeline with 9 test combinations
88
+ - Code quality tools: ESLint + Prettier with Husky pre-commit hooks
89
+ - Package manager agnostic design
90
+ ```
91
+
92
+ **CHANGELOG.md Content:**
93
+
94
+ ```markdown
95
+ ## 0.1.0
96
+
97
+ ### Minor Changes
98
+
99
+ - 65d76dc: Initial template setup with complete AI-driven development pipeline
100
+
101
+ Features:
102
+ - Multi-runtime support for Node.js, Bun, and Deno
103
+ - Universal testing with test-anywhere framework
104
+ - Automated release workflow with changesets
105
+ - GitHub Actions CI/CD pipeline with 9 test combinations
106
+ - Code quality tools: ESLint + Prettier with Husky pre-commit hooks
107
+ - Package manager agnostic design
108
+ ```
109
+
110
+ ## Root Cause Analysis
111
+
112
+ ### Bug #1: Incorrect Section Header Remaining
113
+
114
+ **File:** `scripts/format-release-notes.mjs`
115
+ **Lines:** 92-115
116
+
117
+ The script only matches `### Patch Changes` sections:
118
+
119
+ ```javascript
120
+ const patchChangesMatchWithHash = currentBody.match(
121
+ /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
122
+ );
123
+ const patchChangesMatchNoHash = currentBody.match(
124
+ /### Patch Changes\s*\n\s*-\s+(.+?)$/s
125
+ );
126
+ ```
127
+
128
+ **Root Cause:**
129
+
130
+ - The regex pattern is hardcoded to only match `### Patch Changes`
131
+ - When a release contains `### Minor Changes` or `### Major Changes`, the pattern doesn't match
132
+ - Script exits early with warning message (line 113)
133
+ - Section header remains in the release notes unprocessed
134
+
135
+ ### Bug #2: Missing PR Link
136
+
137
+ **Related to Bug #1**
138
+
139
+ Because the script exits early when it can't parse the changes section:
140
+
141
+ - Commit hash is never extracted (lines 106-115)
142
+ - PR detection logic is never reached (lines 136-182)
143
+ - No PR link is added to release notes
144
+
145
+ ### Bug #3: Missing NPM Badge
146
+
147
+ **Related to Bug #1**
148
+
149
+ The NPM badge formatting (lines 184-195) is also never reached:
150
+
151
+ - Script exits before building formatted body
152
+ - No shields.io badge is added
153
+
154
+ ## Comparison with Changesets Default Behavior
155
+
156
+ ### Changesets CHANGELOG Format
157
+
158
+ Changesets CLI generates CHANGELOG.md with section headers for organizational purposes:
159
+
160
+ - `### Major Changes` - Breaking changes (X.0.0)
161
+ - `### Minor Changes` - New features (0.X.0)
162
+ - `### Patch Changes` - Bug fixes (0.0.X)
163
+
164
+ **Sources:**
165
+
166
+ - [Changesets GitHub Repository](https://github.com/changesets/changesets)
167
+ - [Changesets Detailed Documentation](https://github.com/changesets/changesets/blob/main/docs/detailed-explanation.md)
168
+ - [NPM Package](https://www.npmjs.com/package/@changesets/cli)
169
+ - [LogRocket Guide to Changesets](https://blog.logrocket.com/version-management-changesets/)
170
+
171
+ ### Why This is a Problem for GitHub Releases
172
+
173
+ 1. **CHANGELOG.md vs GitHub Releases** - Section headers are useful in CHANGELOG.md for organizing multiple version entries, but redundant in individual GitHub Releases
174
+ 2. **Version already indicates type** - A v0.1.0 release is clearly a minor version; the "### Minor Changes" header is redundant
175
+ 3. **User expectations** - GitHub Release notes should be clean, concise, and focused on content, not categorization
176
+
177
+ ## Reference Implementation
178
+
179
+ ### link-assistant/agent Fix
180
+
181
+ The bug was already fixed in a downstream repository:
182
+
183
+ - **Repository:** link-assistant/agent
184
+ - **Issue:** [#58](https://github.com/link-assistant/agent/issues/58)
185
+ - **Pull Request:** [#59](https://github.com/link-assistant/agent/pull/59)
186
+ - **Case Study:** docs/case-studies/issue-58/README.md
187
+
188
+ ### The Solution
189
+
190
+ Replace hardcoded `### Patch Changes` regex with flexible pattern matching:
191
+
192
+ ```javascript
193
+ // Match any changeset type (Major, Minor, or Patch)
194
+ const changesPattern =
195
+ /### (Major|Minor|Patch) Changes\s*\n\s*-\s+(?:([a-f0-9]+):\s+)?(.+?)$/s;
196
+ const changesMatch = currentBody.match(changesPattern);
197
+
198
+ let commitHash = null;
199
+ let rawDescription = null;
200
+ let changeType = null;
201
+
202
+ if (changesMatch) {
203
+ // Extract: [full match, changeType, commitHash (optional), description]
204
+ [, changeType, commitHash, rawDescription] = changesMatch;
205
+ console.log(`ℹ️ Found ${changeType} Changes section`);
206
+
207
+ // If commitHash is undefined and description contains it, try to extract
208
+ if (!commitHash && rawDescription) {
209
+ const descWithHashMatch = rawDescription.match(/^([a-f0-9]+):\s+(.+)$/s);
210
+ if (descWithHashMatch) {
211
+ [, commitHash, rawDescription] = descWithHashMatch;
212
+ }
213
+ }
214
+ } else {
215
+ console.log('⚠️ Could not parse changes from release notes');
216
+ console.log(' Looking for pattern: ### [Major|Minor|Patch] Changes');
217
+ process.exit(0);
218
+ }
219
+ ```
220
+
221
+ **Key Improvements:**
222
+
223
+ 1. Uses capture group `(Major|Minor|Patch)` to match all changeset types
224
+ 2. Makes commit hash optional with non-capturing group `(?:...)?`
225
+ 3. Handles both formats: with and without commit hash
226
+ 4. Provides informative logging for debugging
227
+ 5. Continues to PR detection and formatting instead of exiting early
228
+
229
+ ## Proposed Solution
230
+
231
+ ### Implementation Steps
232
+
233
+ 1. **Update regex pattern** in scripts/format-release-notes.mjs:92-115
234
+ - Replace hardcoded "Patch Changes" with flexible "(Major|Minor|Patch) Changes"
235
+ - Handle optional commit hash in single regex
236
+ - Add fallback extraction for embedded commit hashes
237
+
238
+ 2. **Test all changeset types:**
239
+ - Create test script for Major changes
240
+ - Create test script for Minor changes
241
+ - Create test script for Patch changes
242
+
243
+ 3. **Verify expected outcomes:**
244
+ - Section headers removed
245
+ - PR detection works for all types
246
+ - NPM badge added
247
+ - Formatting preserved
248
+
249
+ ### Expected Results
250
+
251
+ **After Fix - Release Notes Format:**
252
+
253
+ ```markdown
254
+ Initial template setup with complete AI-driven development pipeline
255
+
256
+ Features:
257
+
258
+ - Multi-runtime support for Node.js, Bun, and Deno
259
+ - Universal testing with test-anywhere framework
260
+ - Automated release workflow with changesets
261
+ - GitHub Actions CI/CD pipeline with 9 test combinations
262
+ - Code quality tools: ESLint + Prettier with Husky pre-commit hooks
263
+ - Package manager agnostic design
264
+
265
+ **Related Pull Request:** #X
266
+
267
+ ---
268
+
269
+ [![npm version](https://img.shields.io/badge/npm-0.1.0-blue.svg)](https://www.npmjs.com/package/my-package/v/0.1.0)
270
+ ```
271
+
272
+ **Key Changes:**
273
+
274
+ 1. ✅ NO "### Minor Changes" header
275
+ 2. ✅ Clean description starting directly with content
276
+ 3. ✅ PR link detected and shown
277
+ 4. ✅ NPM badge included
278
+ 5. ✅ Proper formatting with separator
279
+
280
+ ## Impact Assessment
281
+
282
+ ### Affected Releases
283
+
284
+ **In this repository:**
285
+
286
+ - v0.1.0 - Minor release with formatting bug
287
+
288
+ **In downstream repositories:**
289
+
290
+ - All Minor and Major releases fail formatting
291
+ - Patch releases work correctly
292
+
293
+ ### Risk Analysis
294
+
295
+ **Low Risk Fix:**
296
+
297
+ - Script already handles edge cases for commit hash extraction
298
+ - Only expanding pattern matching, not changing logic
299
+ - Backward compatible with Patch changes
300
+ - Already tested and proven in link-assistant/agent#59
301
+
302
+ ## Next Steps
303
+
304
+ 1. ✅ Create comprehensive case study (this document)
305
+ 2. ⏳ Create issues in affected repositories:
306
+ - link-foundation/test-anywhere
307
+ - link-foundation/gh-download-pull-request
308
+ - link-foundation/gh-download-issue
309
+ 3. ⏳ Implement fix in this repository
310
+ 4. ⏳ Create test scripts to validate all changeset types
311
+ 5. ⏳ Run local CI checks before committing
312
+ 6. ⏳ Update PR with solution details
313
+ 7. ⏳ Mark PR as ready for review
314
+
315
+ ## Files Modified
316
+
317
+ 1. `scripts/format-release-notes.mjs` - Implement flexible pattern matching
318
+ 2. `docs/case-studies/issue-3/README.md` - This case study
319
+ 3. `experiments/test-format-release-notes-*.mjs` - Test scripts for validation
320
+
321
+ ## Verification Steps
322
+
323
+ 1. Test script against mock Major changes
324
+ 2. Test script against mock Minor changes
325
+ 3. Test script against mock Patch changes
326
+ 4. Verify all three types:
327
+ - Remove section headers
328
+ - Extract commit hash
329
+ - Detect and link PR
330
+ - Add NPM badge
331
+ - Preserve formatting
332
+
333
+ ## References
334
+
335
+ - [Changesets GitHub](https://github.com/changesets/changesets)
336
+ - [Changesets Documentation](https://github.com/changesets/changesets/blob/main/docs/detailed-explanation.md)
337
+ - [Reference Fix PR](https://github.com/link-assistant/agent/pull/59)
338
+ - [Original Issue](https://github.com/link-foundation/js-ai-driven-development-pipeline-template/issues/3)
@@ -0,0 +1,32 @@
1
+ # Created Issues in Affected Repositories
2
+
3
+ As requested in the issue comment, we identified all repositories with the same bug and created issues in them.
4
+
5
+ ## Issues Created
6
+
7
+ 1. **link-foundation/test-anywhere**
8
+ - Issue: (creation failed - may already exist or repo may have issues disabled)
9
+ - Script path: scripts/format-release-notes.mjs
10
+
11
+ 2. **link-foundation/gh-download-pull-request**
12
+ - Issue: https://github.com/link-foundation/gh-download-pull-request/issues/5
13
+ - Script path: scripts/format-release-notes.mjs
14
+
15
+ 3. **link-foundation/gh-download-issue**
16
+ - Issue: https://github.com/link-foundation/gh-download-issue/issues/5
17
+ - Script path: scripts/format-release-notes.mjs
18
+
19
+ 4. **link-foundation/js-ai-driven-development-pipeline-template** (this repository)
20
+ - Issue: https://github.com/link-foundation/js-ai-driven-development-pipeline-template/issues/3
21
+ - Script path: scripts/format-release-notes.mjs
22
+
23
+ ## Issue Summary
24
+
25
+ All issues describe:
26
+
27
+ - The bug (script only handles Patch changes)
28
+ - Current behavior (section headers remain, PR detection fails)
29
+ - Expected behavior (clean formatting with PR links)
30
+ - Root cause (hardcoded regex pattern)
31
+ - Proposed solution (flexible pattern matching)
32
+ - Reference to upstream fix (link-assistant/agent#59)
@@ -0,0 +1,29 @@
1
+ {
2
+ "author": {
3
+ "id": "MDQ6VXNlcjE0MzE5MDQ=",
4
+ "is_bot": false,
5
+ "login": "konard",
6
+ "name": "Konstantin Diachenko"
7
+ },
8
+ "body": "## Bug Description\n\nThe `scripts/format-release-notes.mjs` script only handles `### Patch Changes` sections, causing it to fail on Minor and Major releases.\n\n## Current Behavior\n\nWhen a Minor or Major release is created:\n- ❌ Section headers (\"### Minor Changes\", \"### Major Changes\") remain in release notes\n- ❌ PR detection is skipped\n- ❌ Release formatting fails silently\n\n**Example:** Release v0.1.0 shows:\n```\n### Minor Changes\n\n- 65d76dc: Initial template setup with complete AI-driven development pipeline\n ...\n```\n\n## Expected Behavior\n\nRelease notes should be formatted cleanly:\n```\nInitial template setup with complete AI-driven development pipeline\n...\n\n**Related Pull Request:** #X\n\n---\n\n[![npm version](https://img.shields.io/badge/npm-0.1.0-blue.svg)](...)\n```\n\n## Root Cause\n\nFile: `scripts/format-release-notes.mjs` (lines 92-115)\n\n```javascript\n// Current code - only matches Patch changes\nconst patchChangesMatchWithHash = currentBody.match(\n /### Patch Changes\\s*\\n\\s*-\\s+([a-f0-9]+):\\s+(.+?)$/s\n);\nconst patchChangesMatchNoHash = currentBody.match(\n /### Patch Changes\\s*\\n\\s*-\\s+(.+?)$/s\n);\n```\n\nWhen the regex doesn't match:\n1. Script exits early with \"⚠️ Could not parse patch changes\"\n2. Section header remains in release notes\n3. PR detection is never executed\n\n## Proposed Solution\n\nUpdate regex to match all changeset types:\n\n```javascript\n// Match any changeset type (Major, Minor, or Patch)\nconst changesPattern =\n /### (Major|Minor|Patch) Changes\\s*\\n\\s*-\\s+(?:([a-f0-9]+):\\s+)?(.+?)$/s;\nconst changesMatch = currentBody.match(changesPattern);\n\nlet commitHash = null;\nlet rawDescription = null;\nlet changeType = null;\n\nif (changesMatch) {\n [, changeType, commitHash, rawDescription] = changesMatch;\n console.log(`ℹ️ Found ${changeType} Changes section`);\n \n // Extract commit hash if embedded in description\n if (!commitHash && rawDescription) {\n const descWithHashMatch = rawDescription.match(/^([a-f0-9]+):\\s+(.+)$/s);\n if (descWithHashMatch) {\n [, commitHash, rawDescription] = descWithHashMatch;\n }\n }\n} else {\n console.log('⚠️ Could not parse changes from release notes');\n console.log(' Looking for pattern: ### [Major|Minor|Patch] Changes');\n process.exit(0);\n}\n```\n\n## Fix Implementation\n\nWe've implemented this fix in our repository:\n- **Repository:** link-assistant/agent\n- **PR:** https://github.com/link-assistant/agent/pull/59\n- **Case Study:** docs/case-studies/issue-58/README.md\n\nThe fix:\n1. ✅ Matches all changeset types (Major, Minor, Patch)\n2. ✅ Removes section headers from release notes\n3. ✅ Enables PR detection for all release types\n4. ✅ Handles both commit hash formats\n\n## Impact\n\n**Affected Releases:**\n- All Minor and Major releases (v0.1.0, etc.)\n\n**Unaffected:**\n- Patch releases (these work correctly)\n\n## References\n\n- **Discovered in:** link-assistant/agent#58\n- **Fixed in:** link-assistant/agent#59\n- **Changeset docs:** https://github.com/changesets/changesets\n\n---\n\nThis issue was discovered through comprehensive case study analysis documented in our repository. Happy to contribute the fix back to the template if needed!",
9
+ "comments": [
10
+ {
11
+ "id": "IC_kwDOQmfsR87aXMnl",
12
+ "author": { "login": "konard" },
13
+ "authorAssociation": "MEMBER",
14
+ "body": "Also find all our repositories on GitHub, that have copied the same issue so far, and create issues in these repositories to do fixes.\n\nPlease download all logs and data related about the issue to this repository, make sure we compile that data to `./docs/case-studies/issue-{id}` folder, and use it to do deep case study analysis (also make sure to search online for additional facts and data), in which we will reconstruct timeline/sequence of events, find root causes of the problem, and propose possible solutions.",
15
+ "createdAt": "2025-12-17T03:47:00Z",
16
+ "includesCreatedEdit": true,
17
+ "isMinimized": false,
18
+ "minimizedReason": "",
19
+ "reactionGroups": [],
20
+ "url": "https://github.com/link-foundation/js-ai-driven-development-pipeline-template/issues/3#issuecomment-3663514085",
21
+ "viewerDidAuthor": true
22
+ }
23
+ ],
24
+ "createdAt": "2025-12-16T21:27:52Z",
25
+ "labels": [],
26
+ "state": "OPEN",
27
+ "title": "Release formatting script only handles Patch changes, not Minor/Major",
28
+ "updatedAt": "2025-12-17T03:47:10Z"
29
+ }
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * Script to format GitHub release notes with proper formatting:
5
+ * - Fix special characters like \n
6
+ * - Add link to PR that contains the release commit (if found)
7
+ * - Add shields.io NPM version badge
8
+ * - Format nicely with proper markdown
9
+ *
10
+ * IMPORTANT: Update the PACKAGE_NAME constant below to match your package.json
11
+ *
12
+ * PR Detection Logic:
13
+ * 1. Extract commit hash from changelog entry (if present)
14
+ * 2. Fall back to --commit-sha argument (passed from workflow)
15
+ * 3. Look up PRs that contain the commit via GitHub API
16
+ * 4. If no PR found, simply don't display any PR link (no guessing)
17
+ *
18
+ * Uses link-foundation libraries:
19
+ * - use-m: Dynamic package loading without package.json dependencies
20
+ * - command-stream: Modern shell command execution with streaming support
21
+ * - lino-arguments: Unified configuration from CLI args, env vars, and .lenv files
22
+ *
23
+ * Note: Uses --release-version instead of --version to avoid conflict with yargs' built-in --version flag.
24
+ */
25
+
26
+ // TODO: Update this to match your package name in package.json
27
+ const PACKAGE_NAME = 'my-package';
28
+
29
+ // Load use-m dynamically
30
+ const { use } = eval(
31
+ await (await fetch('https://unpkg.com/use-m/use.js')).text()
32
+ );
33
+
34
+ // Import link-foundation libraries
35
+ const { $ } = await use('command-stream');
36
+ const { makeConfig } = await use('lino-arguments');
37
+
38
+ // Parse CLI arguments using lino-arguments
39
+ // Note: Using --release-version instead of --version to avoid conflict with yargs' built-in --version flag
40
+ const config = makeConfig({
41
+ yargs: ({ yargs, getenv }) =>
42
+ yargs
43
+ .option('release-version', {
44
+ type: 'string',
45
+ default: getenv('VERSION', ''),
46
+ describe: 'Version number (e.g., v0.8.36)',
47
+ })
48
+ .option('release-id', {
49
+ type: 'string',
50
+ default: getenv('RELEASE_ID', ''),
51
+ describe: 'GitHub release ID',
52
+ })
53
+ .option('repository', {
54
+ type: 'string',
55
+ default: getenv('REPOSITORY', ''),
56
+ describe: 'GitHub repository (e.g., owner/repo)',
57
+ })
58
+ .option('commit-sha', {
59
+ type: 'string',
60
+ default: getenv('COMMIT_SHA', ''),
61
+ describe: 'Commit SHA for PR detection',
62
+ }),
63
+ });
64
+
65
+ const releaseId = config.releaseId;
66
+ const version = config.releaseVersion;
67
+ const repository = config.repository;
68
+ const passedCommitSha = config.commitSha;
69
+
70
+ if (!releaseId || !version || !repository) {
71
+ console.error(
72
+ 'Usage: format-release-notes.mjs --release-id <releaseId> --release-version <version> --repository <repository> [--commit-sha <sha>]'
73
+ );
74
+ process.exit(1);
75
+ }
76
+
77
+ try {
78
+ // Get current release body
79
+ const result = await $`gh api repos/${repository}/releases/${releaseId}`.run({
80
+ capture: true,
81
+ });
82
+ const releaseData = JSON.parse(result.stdout);
83
+
84
+ const currentBody = releaseData.body || '';
85
+
86
+ // Skip if already formatted (has shields.io badge image)
87
+ if (currentBody.includes('img.shields.io')) {
88
+ console.log('ℹ️ Release notes already formatted');
89
+ process.exit(0);
90
+ }
91
+
92
+ // Extract the patch changes section
93
+ // This regex handles two formats:
94
+ // 1. With commit hash: "- abc1234: Description"
95
+ // 2. Without commit hash: "- Description"
96
+ const patchChangesMatchWithHash = currentBody.match(
97
+ /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
98
+ );
99
+ const patchChangesMatchNoHash = currentBody.match(
100
+ /### Patch Changes\s*\n\s*-\s+(.+?)$/s
101
+ );
102
+
103
+ let commitHash = null;
104
+ let rawDescription = null;
105
+
106
+ if (patchChangesMatchWithHash) {
107
+ // Format: - abc1234: Description
108
+ [, commitHash, rawDescription] = patchChangesMatchWithHash;
109
+ } else if (patchChangesMatchNoHash) {
110
+ // Format: - Description (no commit hash)
111
+ [, rawDescription] = patchChangesMatchNoHash;
112
+ } else {
113
+ console.log('⚠️ Could not parse patch changes from release notes');
114
+ process.exit(0);
115
+ }
116
+
117
+ // Clean up the description:
118
+ // 1. Convert literal \n sequences (escaped newlines from GitHub API) to actual newlines
119
+ // 2. Remove leading/trailing quotes (including escaped quotes from command-stream shell escaping)
120
+ // 3. Remove any trailing npm package links or markdown that might be there
121
+ // 4. Normalize whitespace while preserving line breaks
122
+ const cleanDescription = rawDescription
123
+ .replace(/\\n/g, '\n') // Convert escaped \n to actual newlines
124
+ .replace(/^(\\['"])+/g, '') // Remove leading escaped quotes (e.g., \', \", \'', \'')
125
+ .replace(/(['"])+$/g, '') // Remove trailing unescaped quotes (e.g., ', ", '', '')
126
+ .replace(/^(['"])+/g, '') // Remove leading unescaped quotes
127
+ .replace(/📦.*$/s, '') // Remove any existing npm package info
128
+ .replace(/---.*$/s, '') // Remove any existing separators and everything after
129
+ .trim()
130
+ .split('\n') // Split by lines
131
+ .map((line) => line.trim()) // Trim whitespace from each line
132
+ .join('\n') // Rejoin with newlines
133
+ .replace(/\n{3,}/g, '\n\n'); // Normalize excessive blank lines (3+ becomes 2)
134
+
135
+ // Find the PR that contains the release commit
136
+ // Uses commit hash from changelog or passed commit SHA from workflow
137
+ let prNumber = null;
138
+
139
+ // Determine which commit SHA to use for PR lookup
140
+ const commitShaToLookup = commitHash || passedCommitSha;
141
+
142
+ if (commitShaToLookup) {
143
+ const source = commitHash ? 'changelog' : 'workflow';
144
+ console.log(
145
+ `ℹ️ Looking up PR for commit ${commitShaToLookup} (from ${source})`
146
+ );
147
+
148
+ try {
149
+ const prResult =
150
+ await $`gh api "repos/${repository}/commits/${commitShaToLookup}/pulls"`.run(
151
+ { capture: true }
152
+ );
153
+ const prsData = JSON.parse(prResult.stdout);
154
+
155
+ // Find the PR that's not the version bump PR (not "chore: version packages")
156
+ const relevantPr = prsData.find(
157
+ (pr) => !pr.title.includes('version packages')
158
+ );
159
+
160
+ if (relevantPr) {
161
+ prNumber = relevantPr.number;
162
+ console.log(`✅ Found PR #${prNumber} containing commit`);
163
+ } else if (prsData.length > 0) {
164
+ console.log(
165
+ '⚠️ Found PRs but all are version bump PRs, not linking any'
166
+ );
167
+ } else {
168
+ console.log(
169
+ 'ℹ️ No PR found containing this commit - not adding PR link'
170
+ );
171
+ }
172
+ } catch (error) {
173
+ console.log('⚠️ Could not find PR for commit', commitShaToLookup);
174
+ console.log(' Error:', error.message);
175
+ if (process.env.DEBUG) {
176
+ console.error(error);
177
+ }
178
+ }
179
+ } else {
180
+ // No commit hash available from any source
181
+ console.log('ℹ️ No commit SHA available - not adding PR link');
182
+ }
183
+
184
+ // Build formatted release notes
185
+ const versionWithoutV = version.replace(/^v/, '');
186
+ const npmBadge = `[![npm version](https://img.shields.io/badge/npm-${versionWithoutV}-blue.svg)](https://www.npmjs.com/package/${PACKAGE_NAME}/v/${versionWithoutV})`;
187
+
188
+ let formattedBody = `${cleanDescription}`;
189
+
190
+ // Add PR link if available
191
+ if (prNumber) {
192
+ formattedBody += `\n\n**Related Pull Request:** #${prNumber}`;
193
+ }
194
+
195
+ formattedBody += `\n\n---\n\n${npmBadge}`;
196
+
197
+ // Update the release using JSON input to properly handle special characters
198
+ const updatePayload = JSON.stringify({ body: formattedBody });
199
+ await $`gh api repos/${repository}/releases/${releaseId} -X PATCH --input -`.run(
200
+ { stdin: updatePayload }
201
+ );
202
+
203
+ console.log(`✅ Formatted release notes for v${versionWithoutV}`);
204
+ if (prNumber) {
205
+ console.log(` - Added link to PR #${prNumber}`);
206
+ }
207
+ console.log(' - Added shields.io npm badge');
208
+ console.log(' - Cleaned up formatting');
209
+ } catch (error) {
210
+ console.error('❌ Error formatting release notes:', error.message);
211
+ process.exit(1);
212
+ }