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.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.github/workflows/release.yml +372 -0
- package/.husky/pre-commit +1 -0
- package/.jscpd.json +20 -0
- package/.prettierignore +7 -0
- package/.prettierrc +10 -0
- package/CHANGELOG.md +143 -0
- package/LICENSE +24 -0
- package/README.md +455 -0
- package/bunfig.toml +3 -0
- package/deno.json +7 -0
- package/docs/case-studies/issue-13/README.md +195 -0
- package/docs/case-studies/issue-13/hive-mind-issue-960.json +23 -0
- package/docs/case-studies/issue-13/hive-mind-pr-961-diff.txt +773 -0
- package/docs/case-studies/issue-13/hive-mind-pr-961.json +126 -0
- package/docs/case-studies/issue-21/README.md +384 -0
- package/docs/case-studies/issue-21/ci-logs/run-20803315337.txt +1188 -0
- package/docs/case-studies/issue-21/ci-logs/run-20885464993.txt +1310 -0
- package/docs/case-studies/issue-21/issue-111-data.txt +15 -0
- package/docs/case-studies/issue-21/issue-113-data.txt +15 -0
- package/docs/case-studies/issue-21/pr-112-data.json +109 -0
- package/docs/case-studies/issue-21/pr-112-diff.patch +1336 -0
- package/docs/case-studies/issue-21/pr-114-data.json +126 -0
- package/docs/case-studies/issue-21/pr-114-diff.patch +879 -0
- package/docs/case-studies/issue-3/README.md +338 -0
- package/docs/case-studies/issue-3/created-issues.md +32 -0
- package/docs/case-studies/issue-3/issue-data.json +29 -0
- package/docs/case-studies/issue-3/original-format-release-notes.mjs +212 -0
- package/docs/case-studies/issue-3/reference-pr-59-diff.txt +614 -0
- package/docs/case-studies/issue-3/reference-pr-59.json +109 -0
- package/docs/case-studies/issue-3/release-v0.1.0.json +9 -0
- package/docs/case-studies/issue-3/repositories-with-same-script.json +22 -0
- package/docs/case-studies/issue-3/research-notes.md +33 -0
- package/docs/case-studies/issue-7/BEST-PRACTICES-COMPARISON.md +334 -0
- package/docs/case-studies/issue-7/FORMATTER-COMPARISON.md +649 -0
- package/docs/case-studies/issue-7/current-repository-analysis.json +70 -0
- package/docs/case-studies/issue-7/effect-template-analysis.json +178 -0
- package/eslint.config.js +91 -0
- package/examples/basic-usage.js +64 -0
- package/experiments/test-changeset-scripts.mjs +303 -0
- package/experiments/test-failure-detection.mjs +143 -0
- package/experiments/test-format-major-changes.mjs +49 -0
- package/experiments/test-format-minor-changes.mjs +52 -0
- package/experiments/test-format-no-hash.mjs +43 -0
- package/experiments/test-format-patch-changes.mjs +46 -0
- package/package.json +80 -0
- package/scripts/changeset-version.mjs +75 -0
- package/scripts/check-changesets.mjs +67 -0
- package/scripts/check-version.mjs +129 -0
- package/scripts/create-github-release.mjs +93 -0
- package/scripts/create-manual-changeset.mjs +89 -0
- package/scripts/detect-code-changes.mjs +194 -0
- package/scripts/format-github-release.mjs +83 -0
- package/scripts/format-release-notes.mjs +219 -0
- package/scripts/instant-version-bump.mjs +172 -0
- package/scripts/js-paths.mjs +177 -0
- package/scripts/merge-changesets.mjs +263 -0
- package/scripts/publish-to-npm.mjs +302 -0
- package/scripts/setup-npm.mjs +37 -0
- package/scripts/validate-changeset.mjs +265 -0
- package/scripts/version-and-commit.mjs +284 -0
- package/src/cli.js +386 -0
- package/src/index.d.ts +255 -0
- package/src/index.js +563 -0
- package/tests/index.test.js +137 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
diff --git a/.changeset/robust-changeset-cicd.md b/.changeset/robust-changeset-cicd.md
|
|
2
|
+
new file mode 100644
|
|
3
|
+
index 00000000..aa632642
|
|
4
|
+
--- /dev/null
|
|
5
|
+
+++ b/.changeset/robust-changeset-cicd.md
|
|
6
|
+
@@ -0,0 +1,11 @@
|
|
7
|
+
+---
|
|
8
|
+
+'@link-assistant/hive-mind': minor
|
|
9
|
+
+---
|
|
10
|
+
+
|
|
11
|
+
+Improve changeset CI/CD robustness for multiple concurrent PRs
|
|
12
|
+
+
|
|
13
|
+
+- Update validate-changeset.mjs to only check changesets ADDED by the current PR (not pre-existing ones)
|
|
14
|
+
+- Add merge-changesets.mjs script to combine multiple pending changesets during release
|
|
15
|
+
+- Merged changesets use highest version bump type (major > minor > patch) and combine descriptions chronologically
|
|
16
|
+
+- Update release workflow to merge multiple changesets before version bump
|
|
17
|
+
+- This prevents PR failures when multiple PRs merge before a release cycle completes
|
|
18
|
+
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
|
|
19
|
+
index 6a810320..148c34bc 100644
|
|
20
|
+
--- a/.github/workflows/release.yml
|
|
21
|
+
+++ b/.github/workflows/release.yml
|
|
22
|
+
@@ -129,6 +129,11 @@ jobs:
|
|
23
|
+
run: npm install
|
|
24
|
+
|
|
25
|
+
- name: Check for changesets
|
|
26
|
+
+ env:
|
|
27
|
+
+ # Pass PR context to the validation script
|
|
28
|
+
+ GITHUB_BASE_REF: ${{ github.base_ref }}
|
|
29
|
+
+ GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
30
|
+
+ GITHUB_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
31
|
+
run: |
|
|
32
|
+
# Skip changeset check for automated version PRs
|
|
33
|
+
if [[ "${{ github.head_ref }}" == "changeset-release/"* ]]; then
|
|
34
|
+
@@ -137,6 +142,8 @@ jobs:
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Run changeset validation script
|
|
38
|
+
+ # This validates that exactly ONE changeset was ADDED by this PR
|
|
39
|
+
+ # Pre-existing changesets from other merged PRs are ignored
|
|
40
|
+
node scripts/validate-changeset.mjs
|
|
41
|
+
|
|
42
|
+
# === FAST CHECKS (FAIL FAST) ===
|
|
43
|
+
@@ -1521,6 +1528,13 @@ jobs:
|
|
44
|
+
CHANGESET_COUNT=$(find .changeset -name "*.md" ! -name "README.md" | wc -l)
|
|
45
|
+
echo "Found $CHANGESET_COUNT changeset file(s)"
|
|
46
|
+
echo "has_changesets=$([[ $CHANGESET_COUNT -gt 0 ]] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
|
|
47
|
+
+ echo "changeset_count=$CHANGESET_COUNT" >> $GITHUB_OUTPUT
|
|
48
|
+
+
|
|
49
|
+
+ - name: Merge multiple changesets
|
|
50
|
+
+ if: steps.check_changesets.outputs.has_changesets == 'true' && steps.check_changesets.outputs.changeset_count > 1
|
|
51
|
+
+ run: |
|
|
52
|
+
+ echo "Multiple changesets detected, merging..."
|
|
53
|
+
+ node scripts/merge-changesets.mjs
|
|
54
|
+
|
|
55
|
+
- name: Version packages and commit to main
|
|
56
|
+
if: steps.check_changesets.outputs.has_changesets == 'true'
|
|
57
|
+
diff --git a/experiments/test-changeset-scripts.mjs b/experiments/test-changeset-scripts.mjs
|
|
58
|
+
new file mode 100644
|
|
59
|
+
index 00000000..b8ea38b1
|
|
60
|
+
--- /dev/null
|
|
61
|
+
+++ b/experiments/test-changeset-scripts.mjs
|
|
62
|
+
@@ -0,0 +1,257 @@
|
|
63
|
+
+#!/usr/bin/env node
|
|
64
|
+
+
|
|
65
|
+
+/**
|
|
66
|
+
+ * Test suite for changeset-related scripts
|
|
67
|
+
+ * Tests validate-changeset.mjs and merge-changesets.mjs functionality
|
|
68
|
+
+ */
|
|
69
|
+
+
|
|
70
|
+
+import { execSync } from 'child_process';
|
|
71
|
+
+import { fileURLToPath } from 'url';
|
|
72
|
+
+import { dirname, join } from 'path';
|
|
73
|
+
+import { mkdirSync, writeFileSync, rmSync, existsSync, readdirSync } from 'fs';
|
|
74
|
+
+
|
|
75
|
+
+const __filename = fileURLToPath(import.meta.url);
|
|
76
|
+
+const __dirname = dirname(__filename);
|
|
77
|
+
+const projectRoot = join(__dirname, '..');
|
|
78
|
+
+const validateChangesetPath = join(projectRoot, 'scripts', 'validate-changeset.mjs');
|
|
79
|
+
+const mergeChangesetsPath = join(projectRoot, 'scripts', 'merge-changesets.mjs');
|
|
80
|
+
+
|
|
81
|
+
+let testsPassed = 0;
|
|
82
|
+
+let testsFailed = 0;
|
|
83
|
+
+
|
|
84
|
+
+function runTest(name, testFn) {
|
|
85
|
+
+ process.stdout.write(`Testing ${name}... `);
|
|
86
|
+
+ try {
|
|
87
|
+
+ testFn();
|
|
88
|
+
+ console.log('✅ PASSED');
|
|
89
|
+
+ testsPassed++;
|
|
90
|
+
+ } catch (error) {
|
|
91
|
+
+ console.log(`❌ FAILED: ${error.message}`);
|
|
92
|
+
+ testsFailed++;
|
|
93
|
+
+ }
|
|
94
|
+
+}
|
|
95
|
+
+
|
|
96
|
+
+function execCommand(command, options = {}) {
|
|
97
|
+
+ try {
|
|
98
|
+
+ return {
|
|
99
|
+
+ output: execSync(command, { encoding: 'utf8', stdio: 'pipe', cwd: projectRoot, ...options }),
|
|
100
|
+
+ exitCode: 0,
|
|
101
|
+
+ };
|
|
102
|
+
+ } catch (error) {
|
|
103
|
+
+ return {
|
|
104
|
+
+ output: (error.stdout || '') + (error.stderr || ''),
|
|
105
|
+
+ exitCode: error.status || 1,
|
|
106
|
+
+ };
|
|
107
|
+
+ }
|
|
108
|
+
+}
|
|
109
|
+
+
|
|
110
|
+
+// ==========================================
|
|
111
|
+
+// Tests for validate-changeset.mjs
|
|
112
|
+
+// ==========================================
|
|
113
|
+
+
|
|
114
|
+
+// Test 1: Script exists and is executable
|
|
115
|
+
+runTest('validate-changeset.mjs exists', () => {
|
|
116
|
+
+ if (!existsSync(validateChangesetPath)) {
|
|
117
|
+
+ throw new Error('validate-changeset.mjs not found');
|
|
118
|
+
+ }
|
|
119
|
+
+});
|
|
120
|
+
+
|
|
121
|
+
+// Test 2: Syntax check
|
|
122
|
+
+runTest('validate-changeset.mjs syntax check', () => {
|
|
123
|
+
+ const { output, exitCode } = execCommand(`node --check ${validateChangesetPath}`);
|
|
124
|
+
+ if (exitCode !== 0) {
|
|
125
|
+
+ throw new Error(`Syntax error: ${output}`);
|
|
126
|
+
+ }
|
|
127
|
+
+});
|
|
128
|
+
+
|
|
129
|
+
+// Test 3: Script runs without crashing (fallback mode)
|
|
130
|
+
+runTest('validate-changeset.mjs runs in fallback mode', () => {
|
|
131
|
+
+ // Without git diff context, it falls back to checking all changesets
|
|
132
|
+
+ const { output, exitCode } = execCommand(`node ${validateChangesetPath}`);
|
|
133
|
+
+ // Should either pass (if there's exactly one changeset) or fail (if not)
|
|
134
|
+
+ // But should not crash with an exception
|
|
135
|
+
+ if (output.includes('Error during changeset validation') && output.includes('Cannot read')) {
|
|
136
|
+
+ throw new Error('Script crashed unexpectedly');
|
|
137
|
+
+ }
|
|
138
|
+
+});
|
|
139
|
+
+
|
|
140
|
+
+// ==========================================
|
|
141
|
+
+// Tests for merge-changesets.mjs
|
|
142
|
+
+// ==========================================
|
|
143
|
+
+
|
|
144
|
+
+// Test 4: Script exists
|
|
145
|
+
+runTest('merge-changesets.mjs exists', () => {
|
|
146
|
+
+ if (!existsSync(mergeChangesetsPath)) {
|
|
147
|
+
+ throw new Error('merge-changesets.mjs not found');
|
|
148
|
+
+ }
|
|
149
|
+
+});
|
|
150
|
+
+
|
|
151
|
+
+// Test 5: Syntax check
|
|
152
|
+
+runTest('merge-changesets.mjs syntax check', () => {
|
|
153
|
+
+ const { output, exitCode } = execCommand(`node --check ${mergeChangesetsPath}`);
|
|
154
|
+
+ if (exitCode !== 0) {
|
|
155
|
+
+ throw new Error(`Syntax error: ${output}`);
|
|
156
|
+
+ }
|
|
157
|
+
+});
|
|
158
|
+
+
|
|
159
|
+
+// ==========================================
|
|
160
|
+
+// Unit tests using mock changeset directories
|
|
161
|
+
+// ==========================================
|
|
162
|
+
+
|
|
163
|
+
+const testDir = join(projectRoot, 'experiments', 'test-changesets-temp');
|
|
164
|
+
+const testChangesetDir = join(testDir, '.changeset');
|
|
165
|
+
+
|
|
166
|
+
+function setupTestEnvironment() {
|
|
167
|
+
+ // Clean up if exists
|
|
168
|
+
+ if (existsSync(testDir)) {
|
|
169
|
+
+ rmSync(testDir, { recursive: true });
|
|
170
|
+
+ }
|
|
171
|
+
+ mkdirSync(testChangesetDir, { recursive: true });
|
|
172
|
+
+
|
|
173
|
+
+ // Create README.md (should be ignored)
|
|
174
|
+
+ writeFileSync(join(testChangesetDir, 'README.md'), '# Changesets\n');
|
|
175
|
+
+
|
|
176
|
+
+ // Create config.json (should be ignored)
|
|
177
|
+
+ writeFileSync(join(testChangesetDir, 'config.json'), '{}');
|
|
178
|
+
+}
|
|
179
|
+
+
|
|
180
|
+
+function cleanupTestEnvironment() {
|
|
181
|
+
+ if (existsSync(testDir)) {
|
|
182
|
+
+ rmSync(testDir, { recursive: true });
|
|
183
|
+
+ }
|
|
184
|
+
+}
|
|
185
|
+
+
|
|
186
|
+
+function createChangeset(filename, type, description) {
|
|
187
|
+
+ const content = `---
|
|
188
|
+
+'@link-assistant/hive-mind': ${type}
|
|
189
|
+
+---
|
|
190
|
+
+
|
|
191
|
+
+${description}
|
|
192
|
+
+`;
|
|
193
|
+
+ writeFileSync(join(testChangesetDir, filename), content);
|
|
194
|
+
+}
|
|
195
|
+
+
|
|
196
|
+
+// Test 6: Merge changesets correctly combines multiple changesets
|
|
197
|
+
+runTest('merge-changesets.mjs combines multiple changesets', () => {
|
|
198
|
+
+ setupTestEnvironment();
|
|
199
|
+
+ try {
|
|
200
|
+
+ // Create two changesets
|
|
201
|
+
+ createChangeset('first-change.md', 'patch', 'First change description');
|
|
202
|
+
+
|
|
203
|
+
+ // Wait a bit to ensure different mtime
|
|
204
|
+
+ execSync('sleep 0.1');
|
|
205
|
+
+
|
|
206
|
+
+ createChangeset('second-change.md', 'minor', 'Second change description');
|
|
207
|
+
+
|
|
208
|
+
+ // Run merge script in test directory
|
|
209
|
+
+ const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, { cwd: testDir });
|
|
210
|
+
+
|
|
211
|
+
+ if (exitCode !== 0) {
|
|
212
|
+
+ throw new Error(`Merge failed: ${output}`);
|
|
213
|
+
+ }
|
|
214
|
+
+
|
|
215
|
+
+ // Check that merged changeset was created
|
|
216
|
+
+ const files = readdirSync(testChangesetDir).filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
217
|
+
+ if (files.length !== 1) {
|
|
218
|
+
+ throw new Error(`Expected 1 merged changeset, found ${files.length}`);
|
|
219
|
+
+ }
|
|
220
|
+
+
|
|
221
|
+
+ // Check that it uses the higher bump type (minor)
|
|
222
|
+
+ if (!output.includes('Using highest: minor')) {
|
|
223
|
+
+ throw new Error('Expected merged changeset to use minor bump type');
|
|
224
|
+
+ }
|
|
225
|
+
+
|
|
226
|
+
+ // Check that both descriptions are included
|
|
227
|
+
+ if (!output.includes('First change description') || !output.includes('Second change description')) {
|
|
228
|
+
+ throw new Error('Merged changeset should contain both descriptions');
|
|
229
|
+
+ }
|
|
230
|
+
+ } finally {
|
|
231
|
+
+ cleanupTestEnvironment();
|
|
232
|
+
+ }
|
|
233
|
+
+});
|
|
234
|
+
+
|
|
235
|
+
+// Test 7: Merge changesets uses major if any is major
|
|
236
|
+
+runTest('merge-changesets.mjs uses highest bump type (major)', () => {
|
|
237
|
+
+ setupTestEnvironment();
|
|
238
|
+
+ try {
|
|
239
|
+
+ createChangeset('patch-change.md', 'patch', 'Patch change');
|
|
240
|
+
+ createChangeset('major-change.md', 'major', 'Major change');
|
|
241
|
+
+ createChangeset('minor-change.md', 'minor', 'Minor change');
|
|
242
|
+
+
|
|
243
|
+
+ const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, { cwd: testDir });
|
|
244
|
+
+
|
|
245
|
+
+ if (exitCode !== 0) {
|
|
246
|
+
+ throw new Error(`Merge failed: ${output}`);
|
|
247
|
+
+ }
|
|
248
|
+
+
|
|
249
|
+
+ if (!output.includes('Using highest: major')) {
|
|
250
|
+
+ throw new Error('Expected merged changeset to use major bump type');
|
|
251
|
+
+ }
|
|
252
|
+
+ } finally {
|
|
253
|
+
+ cleanupTestEnvironment();
|
|
254
|
+
+ }
|
|
255
|
+
+});
|
|
256
|
+
+
|
|
257
|
+
+// Test 8: Merge does nothing with single changeset
|
|
258
|
+
+runTest('merge-changesets.mjs skips with single changeset', () => {
|
|
259
|
+
+ setupTestEnvironment();
|
|
260
|
+
+ try {
|
|
261
|
+
+ createChangeset('only-change.md', 'patch', 'Only change');
|
|
262
|
+
+
|
|
263
|
+
+ const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, { cwd: testDir });
|
|
264
|
+
+
|
|
265
|
+
+ if (exitCode !== 0) {
|
|
266
|
+
+ throw new Error(`Script failed: ${output}`);
|
|
267
|
+
+ }
|
|
268
|
+
+
|
|
269
|
+
+ if (!output.includes('No merging needed')) {
|
|
270
|
+
+ throw new Error('Expected script to skip merging with single changeset');
|
|
271
|
+
+ }
|
|
272
|
+
+
|
|
273
|
+
+ // Verify original changeset still exists
|
|
274
|
+
+ const files = readdirSync(testChangesetDir).filter(f => f.endsWith('.md') && f !== 'README.md');
|
|
275
|
+
+ if (files.length !== 1 || files[0] !== 'only-change.md') {
|
|
276
|
+
+ throw new Error('Original changeset should not be modified');
|
|
277
|
+
+ }
|
|
278
|
+
+ } finally {
|
|
279
|
+
+ cleanupTestEnvironment();
|
|
280
|
+
+ }
|
|
281
|
+
+});
|
|
282
|
+
+
|
|
283
|
+
+// Test 9: Merge does nothing with no changesets
|
|
284
|
+
+runTest('merge-changesets.mjs skips with no changesets', () => {
|
|
285
|
+
+ setupTestEnvironment();
|
|
286
|
+
+ try {
|
|
287
|
+
+ const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, { cwd: testDir });
|
|
288
|
+
+
|
|
289
|
+
+ if (exitCode !== 0) {
|
|
290
|
+
+ throw new Error(`Script failed: ${output}`);
|
|
291
|
+
+ }
|
|
292
|
+
+
|
|
293
|
+
+ if (!output.includes('No merging needed')) {
|
|
294
|
+
+ throw new Error('Expected script to skip merging with no changesets');
|
|
295
|
+
+ }
|
|
296
|
+
+ } finally {
|
|
297
|
+
+ cleanupTestEnvironment();
|
|
298
|
+
+ }
|
|
299
|
+
+});
|
|
300
|
+
+
|
|
301
|
+
+// Test 10: Validate changeset format checking
|
|
302
|
+
+runTest('validate-changeset.mjs format validation', () => {
|
|
303
|
+
+ // This test verifies the script can be imported and has expected functions
|
|
304
|
+
+ // Actual validation is tested through integration
|
|
305
|
+
+ const { output, exitCode } = execCommand(`node --check ${validateChangesetPath}`);
|
|
306
|
+
+ if (exitCode !== 0) {
|
|
307
|
+
+ throw new Error('Script has syntax errors');
|
|
308
|
+
+ }
|
|
309
|
+
+});
|
|
310
|
+
+
|
|
311
|
+
+// Summary
|
|
312
|
+
+console.log('\n' + '='.repeat(50));
|
|
313
|
+
+console.log(`Test Results for changeset scripts:`);
|
|
314
|
+
+console.log(` ✅ Passed: ${testsPassed}`);
|
|
315
|
+
+console.log(` ❌ Failed: ${testsFailed}`);
|
|
316
|
+
+console.log('='.repeat(50));
|
|
317
|
+
+
|
|
318
|
+
+// Exit with appropriate code
|
|
319
|
+
+process.exit(testsFailed > 0 ? 1 : 0);
|
|
320
|
+
diff --git a/scripts/merge-changesets.mjs b/scripts/merge-changesets.mjs
|
|
321
|
+
new file mode 100644
|
|
322
|
+
index 00000000..a71bd5b2
|
|
323
|
+
--- /dev/null
|
|
324
|
+
+++ b/scripts/merge-changesets.mjs
|
|
325
|
+
@@ -0,0 +1,184 @@
|
|
326
|
+
+#!/usr/bin/env node
|
|
327
|
+
+
|
|
328
|
+
+/**
|
|
329
|
+
+ * Merge multiple changeset files into a single changeset
|
|
330
|
+
+ *
|
|
331
|
+
+ * Key behavior:
|
|
332
|
+
+ * - Combines all pending changesets into a single changeset file
|
|
333
|
+
+ * - Uses the highest version bump type (major > minor > patch)
|
|
334
|
+
+ * - Preserves all descriptions in chronological order (by file modification time)
|
|
335
|
+
+ * - Removes the individual changeset files after merging
|
|
336
|
+
+ * - Does nothing if there's only one or no changesets
|
|
337
|
+
+ *
|
|
338
|
+
+ * This script is run before `changeset version` to ensure a clean release
|
|
339
|
+
+ * even when multiple PRs have merged before a release cycle.
|
|
340
|
+
+ */
|
|
341
|
+
+
|
|
342
|
+
+import { readdirSync, readFileSync, writeFileSync, unlinkSync, statSync } from 'fs';
|
|
343
|
+
+import { join } from 'path';
|
|
344
|
+
+
|
|
345
|
+
+const PACKAGE_NAME = '@link-assistant/hive-mind';
|
|
346
|
+
+const CHANGESET_DIR = '.changeset';
|
|
347
|
+
+
|
|
348
|
+
+// Version bump type priority (higher number = higher priority)
|
|
349
|
+
+const BUMP_PRIORITY = {
|
|
350
|
+
+ patch: 1,
|
|
351
|
+
+ minor: 2,
|
|
352
|
+
+ major: 3,
|
|
353
|
+
+};
|
|
354
|
+
+
|
|
355
|
+
+/**
|
|
356
|
+
+ * Generate a random changeset file name (similar to what @changesets/cli does)
|
|
357
|
+
+ * @returns {string}
|
|
358
|
+
+ */
|
|
359
|
+
+function generateChangesetName() {
|
|
360
|
+
+ const adjectives = ['bright', 'calm', 'cool', 'cyan', 'dark', 'fast', 'gold', 'good', 'green', 'happy', 'kind', 'loud', 'neat', 'nice', 'pink', 'proud', 'quick', 'red', 'rich', 'safe', 'shy', 'soft', 'sweet', 'tall', 'warm', 'wise', 'young'];
|
|
361
|
+
+ const nouns = ['apple', 'bird', 'book', 'car', 'cat', 'cloud', 'desk', 'dog', 'door', 'fish', 'flower', 'frog', 'grass', 'house', 'key', 'lake', 'leaf', 'moon', 'mouse', 'owl', 'park', 'rain', 'river', 'rock', 'sea', 'star', 'sun', 'tree', 'wave', 'wind'];
|
|
362
|
+
+
|
|
363
|
+
+ const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
364
|
+
+ const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
365
|
+
+
|
|
366
|
+
+ return `${randomAdjective}-${randomNoun}`;
|
|
367
|
+
+}
|
|
368
|
+
+
|
|
369
|
+
+/**
|
|
370
|
+
+ * Parse a changeset file and extract its metadata
|
|
371
|
+
+ * @param {string} filePath
|
|
372
|
+
+ * @returns {{type: string, description: string, mtime: Date} | null}
|
|
373
|
+
+ */
|
|
374
|
+
+function parseChangeset(filePath) {
|
|
375
|
+
+ try {
|
|
376
|
+
+ const content = readFileSync(filePath, 'utf-8');
|
|
377
|
+
+ const stats = statSync(filePath);
|
|
378
|
+
+
|
|
379
|
+
+ // Extract version type
|
|
380
|
+
+ const versionTypeRegex = new RegExp(`^['"]${PACKAGE_NAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]:\\s+(major|minor|patch)`, 'm');
|
|
381
|
+
+ const versionTypeMatch = content.match(versionTypeRegex);
|
|
382
|
+
+
|
|
383
|
+
+ if (!versionTypeMatch) {
|
|
384
|
+
+ console.warn(`Warning: Could not parse version type from ${filePath}, skipping`);
|
|
385
|
+
+ return null;
|
|
386
|
+
+ }
|
|
387
|
+
+
|
|
388
|
+
+ // Extract description
|
|
389
|
+
+ const parts = content.split('---');
|
|
390
|
+
+ const description = parts.length >= 3 ? parts.slice(2).join('---').trim() : '';
|
|
391
|
+
+
|
|
392
|
+
+ return {
|
|
393
|
+
+ type: versionTypeMatch[1],
|
|
394
|
+
+ description,
|
|
395
|
+
+ mtime: stats.mtime,
|
|
396
|
+
+ };
|
|
397
|
+
+ } catch (error) {
|
|
398
|
+
+ console.warn(`Warning: Failed to parse ${filePath}: ${error.message}`);
|
|
399
|
+
+ return null;
|
|
400
|
+
+ }
|
|
401
|
+
+}
|
|
402
|
+
+
|
|
403
|
+
+/**
|
|
404
|
+
+ * Get the highest priority bump type
|
|
405
|
+
+ * @param {string[]} types
|
|
406
|
+
+ * @returns {string}
|
|
407
|
+
+ */
|
|
408
|
+
+function getHighestBumpType(types) {
|
|
409
|
+
+ let highest = 'patch';
|
|
410
|
+
+ for (const type of types) {
|
|
411
|
+
+ if (BUMP_PRIORITY[type] > BUMP_PRIORITY[highest]) {
|
|
412
|
+
+ highest = type;
|
|
413
|
+
+ }
|
|
414
|
+
+ }
|
|
415
|
+
+ return highest;
|
|
416
|
+
+}
|
|
417
|
+
+
|
|
418
|
+
+/**
|
|
419
|
+
+ * Create a merged changeset file
|
|
420
|
+
+ * @param {string} type
|
|
421
|
+
+ * @param {string[]} descriptions
|
|
422
|
+
+ * @returns {string}
|
|
423
|
+
+ */
|
|
424
|
+
+function createMergedChangeset(type, descriptions) {
|
|
425
|
+
+ const combinedDescription = descriptions.join('\n\n');
|
|
426
|
+
+
|
|
427
|
+
+ return `---
|
|
428
|
+
+'${PACKAGE_NAME}': ${type}
|
|
429
|
+
+---
|
|
430
|
+
+
|
|
431
|
+
+${combinedDescription}
|
|
432
|
+
+`;
|
|
433
|
+
+}
|
|
434
|
+
+
|
|
435
|
+
+function main() {
|
|
436
|
+
+ console.log('Checking for multiple changesets to merge...');
|
|
437
|
+
+
|
|
438
|
+
+ // Get all changeset files
|
|
439
|
+
+ const changesetFiles = readdirSync(CHANGESET_DIR).filter(file => file.endsWith('.md') && file !== 'README.md');
|
|
440
|
+
+
|
|
441
|
+
+ console.log(`Found ${changesetFiles.length} changeset file(s)`);
|
|
442
|
+
+
|
|
443
|
+
+ // If 0 or 1 changesets, nothing to merge
|
|
444
|
+
+ if (changesetFiles.length <= 1) {
|
|
445
|
+
+ console.log('No merging needed (0 or 1 changeset found)');
|
|
446
|
+
+ return;
|
|
447
|
+
+ }
|
|
448
|
+
+
|
|
449
|
+
+ console.log('Multiple changesets found, merging...');
|
|
450
|
+
+ changesetFiles.forEach(file => console.log(` - ${file}`));
|
|
451
|
+
+
|
|
452
|
+
+ // Parse all changesets
|
|
453
|
+
+ const parsedChangesets = [];
|
|
454
|
+
+ for (const file of changesetFiles) {
|
|
455
|
+
+ const filePath = join(CHANGESET_DIR, file);
|
|
456
|
+
+ const parsed = parseChangeset(filePath);
|
|
457
|
+
+ if (parsed) {
|
|
458
|
+
+ parsedChangesets.push({
|
|
459
|
+
+ file,
|
|
460
|
+
+ filePath,
|
|
461
|
+
+ ...parsed,
|
|
462
|
+
+ });
|
|
463
|
+
+ }
|
|
464
|
+
+ }
|
|
465
|
+
+
|
|
466
|
+
+ if (parsedChangesets.length === 0) {
|
|
467
|
+
+ console.error('Error: No valid changesets could be parsed');
|
|
468
|
+
+ process.exit(1);
|
|
469
|
+
+ }
|
|
470
|
+
+
|
|
471
|
+
+ // Sort by modification time (oldest first) to preserve chronological order
|
|
472
|
+
+ parsedChangesets.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
|
473
|
+
+
|
|
474
|
+
+ // Determine the highest bump type
|
|
475
|
+
+ const bumpTypes = parsedChangesets.map(c => c.type);
|
|
476
|
+
+ const highestBumpType = getHighestBumpType(bumpTypes);
|
|
477
|
+
+
|
|
478
|
+
+ console.log(`\nMerge summary:`);
|
|
479
|
+
+ console.log(` Bump types found: ${[...new Set(bumpTypes)].join(', ')}`);
|
|
480
|
+
+ console.log(` Using highest: ${highestBumpType}`);
|
|
481
|
+
+
|
|
482
|
+
+ // Collect descriptions in chronological order
|
|
483
|
+
+ const descriptions = parsedChangesets.filter(c => c.description).map(c => c.description);
|
|
484
|
+
+
|
|
485
|
+
+ console.log(` Descriptions to merge: ${descriptions.length}`);
|
|
486
|
+
+
|
|
487
|
+
+ // Create merged changeset content
|
|
488
|
+
+ const mergedContent = createMergedChangeset(highestBumpType, descriptions);
|
|
489
|
+
+
|
|
490
|
+
+ // Generate a unique name for the merged changeset
|
|
491
|
+
+ const mergedFileName = `merged-${generateChangesetName()}.md`;
|
|
492
|
+
+ const mergedFilePath = join(CHANGESET_DIR, mergedFileName);
|
|
493
|
+
+
|
|
494
|
+
+ // Write the merged changeset
|
|
495
|
+
+ writeFileSync(mergedFilePath, mergedContent);
|
|
496
|
+
+ console.log(`\nCreated merged changeset: ${mergedFileName}`);
|
|
497
|
+
+
|
|
498
|
+
+ // Remove the original changeset files
|
|
499
|
+
+ console.log('\nRemoving original changeset files:');
|
|
500
|
+
+ for (const changeset of parsedChangesets) {
|
|
501
|
+
+ unlinkSync(changeset.filePath);
|
|
502
|
+
+ console.log(` Removed: ${changeset.file}`);
|
|
503
|
+
+ }
|
|
504
|
+
+
|
|
505
|
+
+ console.log('\nChangeset merge completed successfully');
|
|
506
|
+
+ console.log(`\nMerged changeset content:\n${mergedContent}`);
|
|
507
|
+
+}
|
|
508
|
+
+
|
|
509
|
+
+main();
|
|
510
|
+
diff --git a/scripts/validate-changeset.mjs b/scripts/validate-changeset.mjs
|
|
511
|
+
index bb5e8d59..0240a3e3 100644
|
|
512
|
+
--- a/scripts/validate-changeset.mjs
|
|
513
|
+
+++ b/scripts/validate-changeset.mjs
|
|
514
|
+
@@ -1,81 +1,206 @@
|
|
515
|
+
#!/usr/bin/env node
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
- * Validate changeset for CI - ensures exactly one valid changeset exists
|
|
519
|
+
+ * Validate changeset for CI - ensures exactly one valid changeset is added by the PR
|
|
520
|
+
+ *
|
|
521
|
+
+ * Key behavior:
|
|
522
|
+
+ * - Only checks changeset files ADDED by the current PR (not pre-existing ones)
|
|
523
|
+
+ * - Uses git diff to compare PR head against base branch
|
|
524
|
+
+ * - Validates that the PR adds exactly one changeset with proper format
|
|
525
|
+
*/
|
|
526
|
+
|
|
527
|
+
-import { readdirSync, readFileSync } from 'fs';
|
|
528
|
+
+import { execSync } from 'child_process';
|
|
529
|
+
+import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
530
|
+
import { join } from 'path';
|
|
531
|
+
|
|
532
|
+
const PACKAGE_NAME = '@link-assistant/hive-mind';
|
|
533
|
+
|
|
534
|
+
-try {
|
|
535
|
+
- // Count changeset files (excluding README.md and config.json)
|
|
536
|
+
+/**
|
|
537
|
+
+ * Get changeset files added in the current PR using git diff
|
|
538
|
+
+ * @returns {string[]} Array of added changeset file names
|
|
539
|
+
+ */
|
|
540
|
+
+function getAddedChangesetFiles() {
|
|
541
|
+
const changesetDir = '.changeset';
|
|
542
|
+
- const changesetFiles = readdirSync(changesetDir).filter(file => file.endsWith('.md') && file !== 'README.md');
|
|
543
|
+
|
|
544
|
+
- const changesetCount = changesetFiles.length;
|
|
545
|
+
- console.log(`Found ${changesetCount} changeset file(s)`);
|
|
546
|
+
+ // Try to get PR context from environment variables (GitHub Actions)
|
|
547
|
+
+ const baseSha = process.env.GITHUB_BASE_SHA || process.env.BASE_SHA;
|
|
548
|
+
+ const headSha = process.env.GITHUB_HEAD_SHA || process.env.HEAD_SHA;
|
|
549
|
+
+
|
|
550
|
+
+ // If we have explicit SHAs, use them
|
|
551
|
+
+ if (baseSha && headSha) {
|
|
552
|
+
+ console.log(`Comparing ${baseSha}...${headSha}`);
|
|
553
|
+
+ try {
|
|
554
|
+
+ // Ensure we have the base commit
|
|
555
|
+
+ try {
|
|
556
|
+
+ execSync(`git cat-file -e ${baseSha}`, { stdio: 'ignore' });
|
|
557
|
+
+ } catch {
|
|
558
|
+
+ // Try to fetch the base commit if not available locally
|
|
559
|
+
+ console.log('Base commit not available locally, attempting fetch...');
|
|
560
|
+
+ try {
|
|
561
|
+
+ execSync(`git fetch origin ${baseSha}`, { stdio: 'inherit' });
|
|
562
|
+
+ } catch {
|
|
563
|
+
+ // If that fails, try fetching all
|
|
564
|
+
+ execSync(`git fetch origin`, { stdio: 'inherit' });
|
|
565
|
+
+ }
|
|
566
|
+
+ }
|
|
567
|
+
+
|
|
568
|
+
+ const diffOutput = execSync(`git diff --name-status ${baseSha} ${headSha}`, {
|
|
569
|
+
+ encoding: 'utf-8',
|
|
570
|
+
+ });
|
|
571
|
+
+
|
|
572
|
+
+ const addedChangesets = [];
|
|
573
|
+
+ for (const line of diffOutput.trim().split('\n')) {
|
|
574
|
+
+ if (!line) continue;
|
|
575
|
+
+ const [status, filePath] = line.split('\t');
|
|
576
|
+
+ // Only count files that were Added (A) in the changeset directory
|
|
577
|
+
+ if (status === 'A' && filePath.startsWith(`${changesetDir}/`) && filePath.endsWith('.md') && !filePath.endsWith('README.md')) {
|
|
578
|
+
+ addedChangesets.push(filePath.replace(`${changesetDir}/`, ''));
|
|
579
|
+
+ }
|
|
580
|
+
+ }
|
|
581
|
+
+ return addedChangesets;
|
|
582
|
+
+ } catch (error) {
|
|
583
|
+
+ console.log(`Git diff with explicit SHAs failed: ${error.message}`);
|
|
584
|
+
+ // Fall through to alternative detection
|
|
585
|
+
+ }
|
|
586
|
+
+ }
|
|
587
|
+
+
|
|
588
|
+
+ // Try GitHub PR context (available in pull_request events)
|
|
589
|
+
+ const prBase = process.env.GITHUB_BASE_REF;
|
|
590
|
+
+ if (prBase) {
|
|
591
|
+
+ console.log(`Comparing against base branch: ${prBase}`);
|
|
592
|
+
+ try {
|
|
593
|
+
+ // Fetch the base branch to ensure we have it
|
|
594
|
+
+ try {
|
|
595
|
+
+ execSync(`git fetch origin ${prBase}`, { stdio: 'inherit' });
|
|
596
|
+
+ } catch {
|
|
597
|
+
+ // Ignore fetch errors, we might already have it
|
|
598
|
+
+ }
|
|
599
|
+
+
|
|
600
|
+
+ const diffOutput = execSync(`git diff --name-status origin/${prBase}...HEAD`, {
|
|
601
|
+
+ encoding: 'utf-8',
|
|
602
|
+
+ });
|
|
603
|
+
+
|
|
604
|
+
+ const addedChangesets = [];
|
|
605
|
+
+ for (const line of diffOutput.trim().split('\n')) {
|
|
606
|
+
+ if (!line) continue;
|
|
607
|
+
+ const [status, filePath] = line.split('\t');
|
|
608
|
+
+ if (status === 'A' && filePath.startsWith(`${changesetDir}/`) && filePath.endsWith('.md') && !filePath.endsWith('README.md')) {
|
|
609
|
+
+ addedChangesets.push(filePath.replace(`${changesetDir}/`, ''));
|
|
610
|
+
+ }
|
|
611
|
+
+ }
|
|
612
|
+
+ return addedChangesets;
|
|
613
|
+
+ } catch (error) {
|
|
614
|
+
+ console.log(`Git diff with base ref failed: ${error.message}`);
|
|
615
|
+
+ }
|
|
616
|
+
+ }
|
|
617
|
+
+
|
|
618
|
+
+ // Fallback: If we can't determine the diff, check all changesets in directory
|
|
619
|
+
+ // This maintains backward compatibility for local development
|
|
620
|
+
+ console.log('Warning: Could not determine PR diff, checking all changesets in directory');
|
|
621
|
+
+ if (!existsSync(changesetDir)) {
|
|
622
|
+
+ return [];
|
|
623
|
+
+ }
|
|
624
|
+
+
|
|
625
|
+
+ const allChangesets = readdirSync(changesetDir).filter(file => file.endsWith('.md') && file !== 'README.md');
|
|
626
|
+
+
|
|
627
|
+
+ return allChangesets;
|
|
628
|
+
+}
|
|
629
|
+
+
|
|
630
|
+
+/**
|
|
631
|
+
+ * Validate a single changeset file
|
|
632
|
+
+ * @param {string} filePath Full path to the changeset file
|
|
633
|
+
+ * @returns {{valid: boolean, type?: string, description?: string, error?: string}}
|
|
634
|
+
+ */
|
|
635
|
+
+function validateChangesetFile(filePath) {
|
|
636
|
+
+ try {
|
|
637
|
+
+ const content = readFileSync(filePath, 'utf-8');
|
|
638
|
+
|
|
639
|
+
- // Ensure exactly one changeset file exists
|
|
640
|
+
+ // Check if changeset has a valid type (major, minor, or patch)
|
|
641
|
+
+ const versionTypeRegex = new RegExp(`^['"]${PACKAGE_NAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]:\\s+(major|minor|patch)`, 'm');
|
|
642
|
+
+ const versionTypeMatch = content.match(versionTypeRegex);
|
|
643
|
+
+
|
|
644
|
+
+ if (!versionTypeMatch) {
|
|
645
|
+
+ return {
|
|
646
|
+
+ valid: false,
|
|
647
|
+
+ error: `Changeset must specify a version type: major, minor, or patch\nExpected format:\n---\n'${PACKAGE_NAME}': patch\n---\n\nYour description here`,
|
|
648
|
+
+ };
|
|
649
|
+
+ }
|
|
650
|
+
+
|
|
651
|
+
+ // Extract description (everything after the closing ---) and check it's not empty
|
|
652
|
+
+ const parts = content.split('---');
|
|
653
|
+
+ if (parts.length < 3) {
|
|
654
|
+
+ return {
|
|
655
|
+
+ valid: false,
|
|
656
|
+
+ error: "Changeset must include a description of the changes (after the closing '---')",
|
|
657
|
+
+ };
|
|
658
|
+
+ }
|
|
659
|
+
+
|
|
660
|
+
+ const description = parts.slice(2).join('---').trim();
|
|
661
|
+
+ if (!description) {
|
|
662
|
+
+ return {
|
|
663
|
+
+ valid: false,
|
|
664
|
+
+ error: 'Changeset must include a non-empty description of the changes',
|
|
665
|
+
+ };
|
|
666
|
+
+ }
|
|
667
|
+
+
|
|
668
|
+
+ return {
|
|
669
|
+
+ valid: true,
|
|
670
|
+
+ type: versionTypeMatch[1],
|
|
671
|
+
+ description,
|
|
672
|
+
+ };
|
|
673
|
+
+ } catch (error) {
|
|
674
|
+
+ return {
|
|
675
|
+
+ valid: false,
|
|
676
|
+
+ error: `Failed to read changeset file: ${error.message}`,
|
|
677
|
+
+ };
|
|
678
|
+
+ }
|
|
679
|
+
+}
|
|
680
|
+
+
|
|
681
|
+
+try {
|
|
682
|
+
+ console.log('Validating changesets added by this PR...');
|
|
683
|
+
+
|
|
684
|
+
+ // Get changeset files added in this PR
|
|
685
|
+
+ const addedChangesetFiles = getAddedChangesetFiles();
|
|
686
|
+
+ const changesetCount = addedChangesetFiles.length;
|
|
687
|
+
+
|
|
688
|
+
+ console.log(`Found ${changesetCount} changeset file(s) added by this PR`);
|
|
689
|
+
+ if (changesetCount > 0) {
|
|
690
|
+
+ console.log('Added changesets:');
|
|
691
|
+
+ addedChangesetFiles.forEach(file => console.log(` - ${file}`));
|
|
692
|
+
+ }
|
|
693
|
+
+
|
|
694
|
+
+ // Ensure exactly one changeset file was added
|
|
695
|
+
if (changesetCount === 0) {
|
|
696
|
+
- console.error("::error::No changeset found. Please add a changeset by running 'npm run changeset' and commit the result.");
|
|
697
|
+
+ console.error("::error::No changeset found in this PR. Please add a changeset by running 'npm run changeset' and commit the result.");
|
|
698
|
+
process.exit(1);
|
|
699
|
+
} else if (changesetCount > 1) {
|
|
700
|
+
- console.error(`::error::Multiple changesets found (${changesetCount}). Each PR should have exactly ONE changeset.`);
|
|
701
|
+
- console.error('::error::Found changeset files:');
|
|
702
|
+
- changesetFiles.forEach(file => console.error(` ${file}`));
|
|
703
|
+
+ console.error(`::error::Multiple changesets found in this PR (${changesetCount}). Each PR should add exactly ONE changeset.`);
|
|
704
|
+
+ console.error('::error::Found changeset files added by this PR:');
|
|
705
|
+
+ addedChangesetFiles.forEach(file => console.error(` ${file}`));
|
|
706
|
+
+ console.error('\n::error::Please combine these into a single changeset or remove the extras.');
|
|
707
|
+
process.exit(1);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
- // Get the changeset file
|
|
711
|
+
- const changesetFile = join(changesetDir, changesetFiles[0]);
|
|
712
|
+
+ // Validate the single changeset file
|
|
713
|
+
+ const changesetFile = join('.changeset', addedChangesetFiles[0]);
|
|
714
|
+
console.log(`Validating changeset: ${changesetFile}`);
|
|
715
|
+
|
|
716
|
+
- // Read the changeset file
|
|
717
|
+
- const content = readFileSync(changesetFile, 'utf-8');
|
|
718
|
+
-
|
|
719
|
+
- // Check if changeset has a valid type (major, minor, or patch)
|
|
720
|
+
- const versionTypeRegex = new RegExp(`^['"]${PACKAGE_NAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]:\\s+(major|minor|patch)`, 'm');
|
|
721
|
+
- if (!versionTypeRegex.test(content)) {
|
|
722
|
+
- console.error('::error::Changeset must specify a version type: major, minor, or patch');
|
|
723
|
+
- console.error(`::error::Expected format in ${changesetFile}:`);
|
|
724
|
+
- console.error('::error::---');
|
|
725
|
+
- console.error(`::error::'${PACKAGE_NAME}': patch`);
|
|
726
|
+
- console.error('::error::---');
|
|
727
|
+
- console.error('::error::');
|
|
728
|
+
- console.error('::error::Your description here');
|
|
729
|
+
- console.error('\nFile content:');
|
|
730
|
+
- console.error(content);
|
|
731
|
+
- process.exit(1);
|
|
732
|
+
- }
|
|
733
|
+
+ const validation = validateChangesetFile(changesetFile);
|
|
734
|
+
|
|
735
|
+
- // Extract description (everything after the closing ---) and check it's not empty
|
|
736
|
+
- const parts = content.split('---');
|
|
737
|
+
- if (parts.length < 3) {
|
|
738
|
+
- console.error('::error::Changeset must include a description of the changes');
|
|
739
|
+
- console.error("::error::The description should appear after the closing '---' in the changeset file");
|
|
740
|
+
- console.error(`::error::Current content of ${changesetFile}:`);
|
|
741
|
+
- console.error(content);
|
|
742
|
+
+ if (!validation.valid) {
|
|
743
|
+
+ console.error(`::error::${validation.error}`);
|
|
744
|
+
+ console.error(`\nFile content of ${changesetFile}:`);
|
|
745
|
+
+ try {
|
|
746
|
+
+ console.error(readFileSync(changesetFile, 'utf-8'));
|
|
747
|
+
+ } catch {
|
|
748
|
+
+ console.error('(could not read file)');
|
|
749
|
+
+ }
|
|
750
|
+
process.exit(1);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
- const description = parts.slice(2).join('---').trim();
|
|
754
|
+
- if (!description) {
|
|
755
|
+
- console.error('::error::Changeset must include a description of the changes');
|
|
756
|
+
- console.error("::error::The description should appear after the closing '---' in the changeset file");
|
|
757
|
+
- console.error(`::error::Current content of ${changesetFile}:`);
|
|
758
|
+
- console.error(content);
|
|
759
|
+
- process.exit(1);
|
|
760
|
+
- }
|
|
761
|
+
-
|
|
762
|
+
- // Extract version type
|
|
763
|
+
- const versionTypeMatch = content.match(versionTypeRegex);
|
|
764
|
+
- const versionType = versionTypeMatch ? versionTypeMatch[1] : 'unknown';
|
|
765
|
+
-
|
|
766
|
+
console.log('Changeset validation passed');
|
|
767
|
+
- console.log(` Type: ${versionType}`);
|
|
768
|
+
- console.log(` Description: ${description}`);
|
|
769
|
+
+ console.log(` Type: ${validation.type}`);
|
|
770
|
+
+ console.log(` Description: ${validation.description}`);
|
|
771
|
+
} catch (error) {
|
|
772
|
+
console.error('Error during changeset validation:', error.message);
|
|
773
|
+
if (process.env.DEBUG) {
|