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
package/README.md ADDED
@@ -0,0 +1,455 @@
1
+ # glab-setup-git-identity
2
+
3
+ A tool to setup git identity based on current GitLab user.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/glab-setup-git-identity)](https://www.npmjs.com/package/glab-setup-git-identity)
6
+ [![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](http://unlicense.org/)
7
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg)](https://nodejs.org/)
8
+
9
+ ## Overview
10
+
11
+ `glab-setup-git-identity` is a CLI tool that simplifies setting up your git identity using your GitLab account. It automatically fetches your GitLab username and primary email address, then configures git with these values.
12
+
13
+ Instead of manually running:
14
+
15
+ ```bash
16
+ glab auth login --hostname gitlab.com --git-protocol https
17
+ glab auth git-credential # For HTTPS authentication helper
18
+
19
+ USERNAME=$(glab api user --jq '.username')
20
+ EMAIL=$(glab api user --jq '.email')
21
+
22
+ git config --global user.name "$USERNAME"
23
+ git config --global user.email "$EMAIL"
24
+ ```
25
+
26
+ You can simply run:
27
+
28
+ ```bash
29
+ glab-setup-git-identity
30
+ ```
31
+
32
+ ## Features
33
+
34
+ - **Automatic identity setup**: Fetches username and email from GitLab
35
+ - **Global and local configuration**: Configure git globally or per-repository
36
+ - **Authentication check**: Prompts you to login if not authenticated
37
+ - **Git credential helper setup**: Automatically configures git to use GitLab CLI for HTTPS authentication
38
+ - **Dry-run mode**: Preview changes without making them
39
+ - **Cross-platform**: Works on macOS, Linux, and Windows
40
+ - **Verbose mode**: Built-in verbose mode for debugging
41
+ - **Self-hosted GitLab support**: Works with GitLab.com and self-hosted instances
42
+
43
+ ## Prerequisites
44
+
45
+ - Node.js >= 20.0.0 (or Bun >= 1.0.0)
46
+ - Git (installed and configured)
47
+ - GitLab CLI (`glab`) installed
48
+
49
+ To install GitLab CLI, see: https://gitlab.com/gitlab-org/cli#installation
50
+
51
+ ## Installation
52
+
53
+ ### Global Installation (CLI)
54
+
55
+ ```bash
56
+ # Using npm
57
+ npm install -g glab-setup-git-identity
58
+
59
+ # Using bun
60
+ bun install -g glab-setup-git-identity
61
+ ```
62
+
63
+ ### Local Installation (Library)
64
+
65
+ ```bash
66
+ # Using npm
67
+ npm install glab-setup-git-identity
68
+
69
+ # Using bun
70
+ bun install glab-setup-git-identity
71
+ ```
72
+
73
+ ## CLI Usage
74
+
75
+ ### Basic Usage
76
+
77
+ ```bash
78
+ # Setup git identity globally (default)
79
+ glab-setup-git-identity
80
+
81
+ # Setup git identity for current repository only
82
+ glab-setup-git-identity --local
83
+
84
+ # Preview what would be configured (dry run)
85
+ glab-setup-git-identity --dry-run
86
+
87
+ # Verify current git identity configuration
88
+ glab-setup-git-identity --verify
89
+
90
+ # Enable verbose output
91
+ glab-setup-git-identity --verbose
92
+ ```
93
+
94
+ ### CLI Options
95
+
96
+ ```
97
+ Usage: glab-setup-git-identity [options]
98
+
99
+ Git Identity Options:
100
+ --global, -g Set git config globally (default: true)
101
+ --local, -l Set git config locally (in current repository)
102
+ --dry-run, --dry Dry run - show what would be done without making changes
103
+ --verify Verify current git identity configuration
104
+ --verbose, -v Enable verbose output
105
+
106
+ GitLab Authentication Options:
107
+ --hostname GitLab hostname to authenticate with (default: gitlab.com)
108
+ --token, -t GitLab access token (reads from stdin if --stdin is used)
109
+ --stdin Read token from standard input
110
+ --git-protocol, -p Protocol for git operations: ssh, https, or http (default: https)
111
+ --api-protocol Protocol for API calls: https or http (default: https)
112
+ --api-host Custom API host URL
113
+ --use-keyring Store token in system keyring
114
+ --job-token, -j CI job token for authentication
115
+
116
+ General Options:
117
+ --help, -h Show help
118
+ --version Show version number
119
+ ```
120
+
121
+ ### Advanced Authentication Examples
122
+
123
+ ```bash
124
+ # Authenticate with self-hosted GitLab
125
+ glab-setup-git-identity --hostname gitlab.company.com
126
+
127
+ # Use SSH protocol instead of HTTPS
128
+ glab-setup-git-identity --git-protocol ssh
129
+
130
+ # Authenticate with token from environment variable
131
+ echo "$GITLAB_TOKEN" | glab-setup-git-identity --stdin
132
+
133
+ # Use token-based authentication directly
134
+ glab-setup-git-identity --token glpat-xxxxx
135
+
136
+ # Store credentials in system keyring
137
+ glab-setup-git-identity --use-keyring
138
+
139
+ # Use in CI/CD with job token
140
+ glab-setup-git-identity --job-token "$CI_JOB_TOKEN" --hostname gitlab.company.com
141
+ ```
142
+
143
+ ### First Run (Not Authenticated)
144
+
145
+ If you haven't authenticated with GitLab CLI yet, the tool will automatically start the authentication process:
146
+
147
+ ```
148
+ GitLab CLI is not authenticated. Starting authentication...
149
+
150
+ Starting GitLab CLI authentication...
151
+
152
+ ? What GitLab instance do you want to log into? gitlab.com
153
+ ? What is your preferred protocol for Git operations? HTTPS
154
+ ? How would you like to login? Token
155
+
156
+ Tip: you can generate a Personal Access Token here https://gitlab.com/-/profile/personal_access_tokens
157
+ The minimum required scopes are 'api' and 'write_repository'.
158
+ ? Paste your authentication token:
159
+ ```
160
+
161
+ The tool runs `glab auth login` automatically, followed by configuring git to use GitLab CLI as the credential helper. Follow the prompts to complete login.
162
+
163
+ If automatic authentication fails, you can run the commands manually:
164
+
165
+ ```bash
166
+ glab auth login --hostname gitlab.com --git-protocol https
167
+ ```
168
+
169
+ ### Successful Run
170
+
171
+ ```
172
+ Fetching GitLab user information...
173
+ GitLab user: your-username
174
+ GitLab email: your-email@example.com
175
+
176
+ Configuring git (global)...
177
+ Git identity configured successfully!
178
+
179
+ Git configured:
180
+ user.name: your-username
181
+ user.email: your-email@example.com
182
+ Scope: global (--global)
183
+
184
+ Git identity setup complete!
185
+
186
+ You can verify your configuration with:
187
+ glab auth status
188
+ git config --global user.name
189
+ git config --global user.email
190
+ ```
191
+
192
+ ### Verifying Configuration
193
+
194
+ You can verify your git identity configuration at any time using:
195
+
196
+ ```bash
197
+ glab-setup-git-identity --verify
198
+ ```
199
+
200
+ Or by running the verification commands directly:
201
+
202
+ ```bash
203
+ glab auth status
204
+ git config --global user.name
205
+ git config --global user.email
206
+ ```
207
+
208
+ For local repository configuration, use `--local`:
209
+
210
+ ```bash
211
+ glab-setup-git-identity --verify --local
212
+ git config --local user.name
213
+ git config --local user.email
214
+ ```
215
+
216
+ ## Library Usage
217
+
218
+ ```javascript
219
+ import {
220
+ isGlabAuthenticated,
221
+ runGlabAuthLogin,
222
+ runGlabAuthSetupGit,
223
+ setupGitIdentity,
224
+ verifyGitIdentity,
225
+ } from 'glab-setup-git-identity';
226
+
227
+ // Check if already authenticated
228
+ const authenticated = await isGlabAuthenticated();
229
+
230
+ if (!authenticated) {
231
+ // Run interactive login
232
+ await runGlabAuthLogin();
233
+ }
234
+
235
+ // Setup git credential helper for GitLab HTTPS operations
236
+ // This configures git to use glab for authentication when pushing/pulling
237
+ await runGlabAuthSetupGit();
238
+
239
+ // Setup git identity from GitLab account
240
+ const { username, email } = await setupGitIdentity();
241
+ console.log(`Configured git as: ${username} <${email}>`);
242
+
243
+ // Verify the configuration
244
+ const identity = await verifyGitIdentity();
245
+ console.log('Current git identity:', identity);
246
+ ```
247
+
248
+ ## API Reference
249
+
250
+ ### Authentication Functions
251
+
252
+ #### `isGlabAuthenticated(options?)`
253
+
254
+ Check if GitLab CLI is authenticated.
255
+
256
+ ```javascript
257
+ const authenticated = await isGlabAuthenticated({
258
+ hostname: 'gitlab.company.com', // optional, for self-hosted GitLab
259
+ verbose: true, // optional, enable debug logging
260
+ });
261
+ ```
262
+
263
+ #### `runGlabAuthLogin(options?)`
264
+
265
+ Run `glab auth login` interactively or with a token.
266
+
267
+ ```javascript
268
+ // Interactive login
269
+ await runGlabAuthLogin();
270
+
271
+ // Login with token (non-interactive)
272
+ await runGlabAuthLogin({
273
+ hostname: 'gitlab.com',
274
+ token: 'your-access-token',
275
+ gitProtocol: 'https', // 'ssh', 'https', or 'http'
276
+ useKeyring: true, // store token in OS keyring
277
+ });
278
+
279
+ // Login with CI job token
280
+ await runGlabAuthLogin({
281
+ hostname: 'gitlab.company.com',
282
+ jobToken: 'CI_JOB_TOKEN_VALUE',
283
+ });
284
+ ```
285
+
286
+ #### `runGlabAuthSetupGit(options?)`
287
+
288
+ Configure git to use GitLab CLI as a credential helper for HTTPS operations.
289
+
290
+ Without this configuration, `git push/pull` may fail with "could not read Username" error when using HTTPS protocol.
291
+
292
+ ```javascript
293
+ // Setup credential helper for gitlab.com
294
+ await runGlabAuthSetupGit();
295
+
296
+ // Setup for self-hosted GitLab
297
+ await runGlabAuthSetupGit({
298
+ hostname: 'gitlab.company.com',
299
+ force: true, // overwrite existing configuration
300
+ verbose: true,
301
+ });
302
+ ```
303
+
304
+ The function automatically detects the glab installation path, so it works regardless of how glab was installed (Homebrew, apt, npm, etc.).
305
+
306
+ #### `getGlabPath(options?)`
307
+
308
+ Get the full path to the glab executable. Useful for debugging or custom integrations.
309
+
310
+ ```javascript
311
+ const glabPath = await getGlabPath();
312
+ console.log(`glab is installed at: ${glabPath}`);
313
+ // e.g., /opt/homebrew/bin/glab, /usr/bin/glab, etc.
314
+ ```
315
+
316
+ ### User Information Functions
317
+
318
+ #### `getGitLabUsername(options?)`
319
+
320
+ Get the authenticated GitLab username.
321
+
322
+ ```javascript
323
+ const username = await getGitLabUsername();
324
+ ```
325
+
326
+ #### `getGitLabEmail(options?)`
327
+
328
+ Get the primary email from the GitLab account.
329
+
330
+ ```javascript
331
+ const email = await getGitLabEmail();
332
+ ```
333
+
334
+ #### `getGitLabUserInfo(options?)`
335
+
336
+ Get both username and email.
337
+
338
+ ```javascript
339
+ const { username, email } = await getGitLabUserInfo({
340
+ hostname: 'gitlab.company.com', // optional
341
+ });
342
+ ```
343
+
344
+ ### Git Configuration Functions
345
+
346
+ #### `setGitConfig(key, value, options?)`
347
+
348
+ Set a git configuration value.
349
+
350
+ ```javascript
351
+ await setGitConfig('user.name', 'John Doe', {
352
+ scope: 'global', // or 'local'
353
+ });
354
+ ```
355
+
356
+ #### `getGitConfig(key, options?)`
357
+
358
+ Get a git configuration value.
359
+
360
+ ```javascript
361
+ const name = await getGitConfig('user.name', {
362
+ scope: 'global', // or 'local'
363
+ });
364
+ ```
365
+
366
+ ### Identity Setup Functions
367
+
368
+ #### `setupGitIdentity(options?)`
369
+
370
+ Configure git identity based on GitLab user.
371
+
372
+ ```javascript
373
+ const { username, email } = await setupGitIdentity({
374
+ hostname: 'gitlab.com', // optional
375
+ scope: 'global', // or 'local'
376
+ dryRun: false, // set to true to preview changes without applying
377
+ verbose: true, // enable debug logging
378
+ });
379
+ ```
380
+
381
+ #### `verifyGitIdentity(options?)`
382
+
383
+ Get the current git identity configuration.
384
+
385
+ ```javascript
386
+ const { username, email } = await verifyGitIdentity({
387
+ scope: 'global', // or 'local'
388
+ });
389
+ ```
390
+
391
+ ### Default Options
392
+
393
+ ```javascript
394
+ import { defaultAuthOptions } from 'glab-setup-git-identity';
395
+
396
+ console.log(defaultAuthOptions);
397
+ // {
398
+ // hostname: 'gitlab.com',
399
+ // gitProtocol: 'https',
400
+ // apiProtocol: 'https',
401
+ // useKeyring: false
402
+ // }
403
+ ```
404
+
405
+ ## Token Requirements
406
+
407
+ When using token-based authentication, ensure your GitLab access token has the following minimum scopes:
408
+
409
+ - `api` - Full API access
410
+ - `write_repository` - Push access to repositories
411
+
412
+ ## Self-Hosted GitLab
413
+
414
+ All functions support the `hostname` option for self-hosted GitLab instances:
415
+
416
+ ```javascript
417
+ await setupGitIdentity({
418
+ hostname: 'gitlab.company.com',
419
+ });
420
+ ```
421
+
422
+ Or via CLI:
423
+
424
+ ```bash
425
+ glab-setup-git-identity --hostname gitlab.company.com
426
+ ```
427
+
428
+ ## TypeScript Support
429
+
430
+ This package includes TypeScript type definitions. All interfaces are exported:
431
+
432
+ ```typescript
433
+ import type {
434
+ AuthOptions,
435
+ AuthStatusOptions,
436
+ SetupGitOptions,
437
+ UserInfoOptions,
438
+ GitConfigOptions,
439
+ SetupOptions,
440
+ UserInfo,
441
+ GitIdentity,
442
+ } from 'glab-setup-git-identity';
443
+ ```
444
+
445
+ ## Multi-Runtime Support
446
+
447
+ This package works with:
448
+
449
+ - **Node.js** (>=20.0.0)
450
+ - **Bun** (>=1.0.0)
451
+ - **Deno**
452
+
453
+ ## License
454
+
455
+ [Unlicense](LICENSE) - Public Domain
package/bunfig.toml ADDED
@@ -0,0 +1,3 @@
1
+ # Bun configuration
2
+ # Note: Bun doesn't support excluding test files directly in config
3
+ # Use CLI flags or file naming patterns instead
package/deno.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "nodeModulesDir": "auto",
3
+ "test": {
4
+ "include": ["tests/"],
5
+ "exclude": ["examples/", "node_modules/"]
6
+ }
7
+ }
@@ -0,0 +1,195 @@
1
+ # Case Study: Robust Changeset CI/CD for Concurrent PRs
2
+
3
+ ## Issue Reference
4
+
5
+ - **Issue**: [#13 - Apply latest CI/CD experience from hive-mind project](https://github.com/link-foundation/js-ai-driven-development-pipeline-template/issues/13)
6
+ - **Reference PR**: [hive-mind#961 - Improve changeset CI/CD robustness for concurrent PRs](https://github.com/link-assistant/hive-mind/pull/961)
7
+ - **Reference Issue**: [hive-mind#960 - Better changeset CI/CD](https://github.com/link-assistant/hive-mind/issues/960)
8
+
9
+ ## Timeline of Events
10
+
11
+ ### 2025-12-21 18:23 UTC - CI Failure Trigger
12
+
13
+ A CI failure occurred on hive-mind PR #728 during the changeset validation step:
14
+
15
+ ```
16
+ Found 4 changeset file(s)
17
+ Error: Multiple changesets found (4). Each PR should have exactly ONE changeset.
18
+ Error: Found changeset files:
19
+ fix-perlbrew-unbound-variable.md
20
+ increase-min-disk-space.md
21
+ readme-initialization.md
22
+ sync-package-lock-json.md
23
+ Error: Process completed with exit code 1.
24
+ ```
25
+
26
+ **Source**: [GitHub Actions Run #20413963959](https://github.com/link-assistant/hive-mind/actions/runs/20413963959/job/58654854890?pr=728)
27
+
28
+ ### 2025-12-22 06:31 UTC - Issue Created
29
+
30
+ Issue #960 was created describing the problem and proposing a more robust changeset CI/CD system.
31
+
32
+ ### 2025-12-22 ~18:00 UTC - Solution Implemented
33
+
34
+ PR #961 was created and merged implementing the solution.
35
+
36
+ ### 2025-12-22 19:22 UTC - Issue Closed
37
+
38
+ Issue #960 was closed as the fix was merged.
39
+
40
+ ## Root Cause Analysis
41
+
42
+ ### The Problem
43
+
44
+ The original `validate-changeset.mjs` script checked **all** changeset files in the `.changeset` directory:
45
+
46
+ ```javascript
47
+ // Original problematic approach
48
+ const changesetFiles = readdirSync(changesetDir).filter(
49
+ (file) => file.endsWith('.md') && file !== 'README.md'
50
+ );
51
+
52
+ if (changesetCount > 1) {
53
+ console.error(`Multiple changesets found (${changesetCount})`);
54
+ process.exit(1);
55
+ }
56
+ ```
57
+
58
+ This approach fails when:
59
+
60
+ 1. **Multiple PRs merge before a release cycle completes**: If PR-A merges with changeset-A, then PR-B opens, it will see changeset-A in the directory and fail validation even though PR-B only added changeset-B.
61
+
62
+ 2. **Race condition in concurrent development**: In active repositories, multiple contributors work simultaneously. If their PRs don't merge fast enough between release cycles, changesets accumulate.
63
+
64
+ 3. **Release process delay or failure**: If the release workflow fails (npm outage, CI failure), changesets accumulate and block all subsequent PRs.
65
+
66
+ ### Why This Happens
67
+
68
+ The [changesets](https://github.com/changesets/changesets) workflow is designed to accumulate changes:
69
+
70
+ > "When two changesets are included which are of the type minor, the minor release will only be bumped once."
71
+
72
+ However, this design assumes:
73
+
74
+ - The release workflow runs successfully after each merge
75
+ - Or the PR validation is aware of what the PR actually added vs. what was pre-existing
76
+
77
+ The original implementation violated the second assumption by treating all existing changesets as if they were added by the current PR.
78
+
79
+ ## Proposed Solutions
80
+
81
+ ### Solution 1: Check Only PR-Added Changesets (Implemented)
82
+
83
+ Use `git diff` to compare the PR branch against the base branch and only validate changesets that were **added** by the current PR:
84
+
85
+ ```javascript
86
+ // Get changeset files ADDED by this PR only
87
+ const diffOutput = execSync(`git diff --name-status ${baseSha} ${headSha}`);
88
+ const addedChangesets = [];
89
+
90
+ for (const line of diffOutput.trim().split('\n')) {
91
+ const [status, filePath] = line.split('\t');
92
+ if (
93
+ status === 'A' &&
94
+ filePath.startsWith('.changeset/') &&
95
+ filePath.endsWith('.md')
96
+ ) {
97
+ addedChangesets.push(filePath);
98
+ }
99
+ }
100
+ ```
101
+
102
+ **Benefits**:
103
+
104
+ - PRs are validated in isolation
105
+ - Pre-existing changesets don't cause false failures
106
+ - No need to merge default branch before PR can pass
107
+
108
+ ### Solution 2: Merge Multiple Changesets at Release Time
109
+
110
+ During the release workflow, if multiple changesets exist, merge them into a single changeset before running `changeset version`:
111
+
112
+ ```javascript
113
+ // Determine highest bump type (major > minor > patch)
114
+ const highestBumpType = getHighestBumpType(parsedChangesets.map((c) => c.type));
115
+
116
+ // Combine descriptions chronologically
117
+ const descriptions = parsedChangesets
118
+ .sort((a, b) => a.mtime - b.mtime)
119
+ .map((c) => c.description);
120
+
121
+ // Create merged changeset
122
+ const mergedContent = createMergedChangeset(highestBumpType, descriptions);
123
+ ```
124
+
125
+ **Benefits**:
126
+
127
+ - Preserves changelog history from all changes
128
+ - Uses correct version bump (highest severity wins)
129
+ - Maintains chronological order of changes
130
+ - Cleans up after merging
131
+
132
+ ### Solution 3: Environment Variables for Git Context
133
+
134
+ Pass explicit SHA references from GitHub Actions to the validation script:
135
+
136
+ ```yaml
137
+ - name: Check for changesets
138
+ env:
139
+ GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
140
+ GITHUB_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
141
+ run: node scripts/validate-changeset.mjs
142
+ ```
143
+
144
+ **Benefits**:
145
+
146
+ - Reliable git context in CI
147
+ - No need to fetch/calculate base branch
148
+ - Works with shallow clones
149
+
150
+ ## Industry Best Practices
151
+
152
+ According to [Changesets documentation](https://github.com/changesets/changesets):
153
+
154
+ 1. **Use the changeset bot** to detect missing changesets rather than failing builds
155
+ 2. **Not every commit needs a changeset** - docs and tests don't require releases
156
+ 3. **Changesets decouple intent from publishing** - team transparency
157
+
158
+ According to [pnpm documentation](https://pnpm.io/using-changesets):
159
+
160
+ 1. **Changesets accumulate** and are processed together at release time
161
+ 2. **Multiple PRs with changesets** should merge cleanly
162
+ 3. **The release PR** handles version bumping and changelog generation
163
+
164
+ According to [Infinum Frontend Handbook](https://infinum.com/handbook/frontend/changesets):
165
+
166
+ 1. **PRs created with GITHUB_TOKEN don't trigger other workflows**
167
+ 2. **Use a Personal Access Token (PAT)** for automated PRs that need CI checks
168
+ 3. **Changesets action** can automate versioning PRs
169
+
170
+ ## Files Changed in Reference PR #961
171
+
172
+ | File | Change Type | Purpose |
173
+ | ---------------------------------------- | ----------- | ----------------------------------------- |
174
+ | `scripts/validate-changeset.mjs` | Modified | Check only PR-added changesets |
175
+ | `scripts/merge-changesets.mjs` | Added | Merge multiple changesets at release time |
176
+ | `.github/workflows/release.yml` | Modified | Pass SHA env vars, add merge step |
177
+ | `experiments/test-changeset-scripts.mjs` | Added | Comprehensive test suite |
178
+
179
+ ## Implementation Status
180
+
181
+ This case study documents the analysis. The actual implementation will:
182
+
183
+ 1. Update `scripts/validate-changeset.mjs` to use git diff approach
184
+ 2. Add `scripts/merge-changesets.mjs` for release-time merging
185
+ 3. Update `.github/workflows/release.yml` with new steps
186
+ 4. Add tests in `experiments/test-changeset-scripts.mjs`
187
+ 5. Update README.md with design decision documentation
188
+
189
+ ## References
190
+
191
+ - [Changesets GitHub Repository](https://github.com/changesets/changesets)
192
+ - [Using Changesets with pnpm](https://pnpm.io/using-changesets)
193
+ - [Infinum Frontend Handbook - Changesets](https://infinum.com/handbook/frontend/changesets)
194
+ - [Automate NPM releases with changesets](https://dev.to/ignace/automate-npm-releases-on-github-using-changesets-25b8)
195
+ - [Failed CI Run Log](https://github.com/link-assistant/hive-mind/actions/runs/20413963959/job/58654854890?pr=728)
@@ -0,0 +1,23 @@
1
+ {
2
+ "author": {
3
+ "id": "MDQ6VXNlcjE0MzE5MDQ=",
4
+ "is_bot": false,
5
+ "login": "konard",
6
+ "name": "Konstantin Diachenko"
7
+ },
8
+ "body": "```\nRun # Skip changeset check for automated version PRs\nFound 4 changeset file(s)\nError: Multiple changesets found (4). Each PR should have exactly ONE changeset.\nError: Found changeset files:\n fix-perlbrew-unbound-variable.md\n increase-min-disk-space.md\n readme-initialization.md\n sync-package-lock-json.md\nError: Process completed with exit code 1.\n```\n\nSource: https://github.com/link-assistant/hive-mind/actions/runs/20413963959/job/58654854890?pr=728\n\nPlease make our changesets CI/CD much more robust.\n\nFirst of all in pull requests, we need to check only changesets added by the pull request, and that should be exactly one.\n\nIf there some changesets not yet processed, we need to make sure we do merge them in order they were created into single changeset.\n\nAlso for example if any of these changeset were minor the resulting merged changeset whould be minor, and so on.\n\nSo all previously unprocessed changesets will get a chance to be proccessed, but to reduce load on CI/CD we can just merge them in a sequence they they were arriving.\n\nAnd to a single version bump of previous one was unsuccesful.\n\nSo in all cases newly added changeset is checked only in the scope of the pull request, so it will never depend on what is in default branch, meaning there will be never need to pull changes from default branch and no possible conflicts abour it.\n\nEven on errors, unprocessed changesets will be just merged and published as soon we get error preventing release resolved.\n\nThe sequence of changesets is preserved. If you see any other potential issues, try to make sure they are handled properly to minimize the need for `git merge` or to minimize probability of conflicts.",
9
+ "closedAt": "2025-12-22T19:22:17Z",
10
+ "comments": [],
11
+ "createdAt": "2025-12-22T06:31:35Z",
12
+ "labels": [
13
+ {
14
+ "id": "LA_kwDOPUU0qc8AAAACGYm6iw",
15
+ "name": "bug",
16
+ "description": "Something isn't working",
17
+ "color": "d73a4a"
18
+ }
19
+ ],
20
+ "number": 960,
21
+ "state": "CLOSED",
22
+ "title": "Better changeset CI/CD"
23
+ }