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,172 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Instant version bump script for manual releases
|
|
5
|
+
* Bypasses the changeset workflow and directly updates version and changelog
|
|
6
|
+
*
|
|
7
|
+
* Usage: node scripts/instant-version-bump.mjs --bump-type <major|minor|patch> [--description <description>] [--js-root <path>]
|
|
8
|
+
*
|
|
9
|
+
* Configuration:
|
|
10
|
+
* - CLI: --js-root <path> to explicitly set JavaScript root
|
|
11
|
+
* - Environment: JS_ROOT=<path>
|
|
12
|
+
*
|
|
13
|
+
* Uses link-foundation libraries:
|
|
14
|
+
* - use-m: Dynamic package loading without package.json dependencies
|
|
15
|
+
* - command-stream: Modern shell command execution with streaming support
|
|
16
|
+
* - lino-arguments: Unified configuration from CLI args, env vars, and .lenv files
|
|
17
|
+
*
|
|
18
|
+
* Addresses issues documented in:
|
|
19
|
+
* - Issue #21: Supporting both single and multi-language repository structures
|
|
20
|
+
* - Reference: link-assistant/agent PR #112 (--legacy-peer-deps fix)
|
|
21
|
+
* - Reference: link-assistant/agent PR #114 (configurable package root)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
25
|
+
import { join } from 'path';
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
getJsRoot,
|
|
29
|
+
getPackageJsonPath,
|
|
30
|
+
needsCd,
|
|
31
|
+
parseJsRootConfig,
|
|
32
|
+
} from './js-paths.mjs';
|
|
33
|
+
|
|
34
|
+
// Load use-m dynamically
|
|
35
|
+
const { use } = eval(
|
|
36
|
+
await (await fetch('https://unpkg.com/use-m/use.js')).text()
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Import link-foundation libraries
|
|
40
|
+
const { $ } = await use('command-stream');
|
|
41
|
+
const { makeConfig } = await use('lino-arguments');
|
|
42
|
+
|
|
43
|
+
// Parse CLI arguments using lino-arguments
|
|
44
|
+
const config = makeConfig({
|
|
45
|
+
yargs: ({ yargs, getenv }) =>
|
|
46
|
+
yargs
|
|
47
|
+
.option('bump-type', {
|
|
48
|
+
type: 'string',
|
|
49
|
+
default: getenv('BUMP_TYPE', ''),
|
|
50
|
+
describe: 'Version bump type: major, minor, or patch',
|
|
51
|
+
choices: ['major', 'minor', 'patch'],
|
|
52
|
+
})
|
|
53
|
+
.option('description', {
|
|
54
|
+
type: 'string',
|
|
55
|
+
default: getenv('DESCRIPTION', ''),
|
|
56
|
+
describe: 'Description for the version bump',
|
|
57
|
+
})
|
|
58
|
+
.option('js-root', {
|
|
59
|
+
type: 'string',
|
|
60
|
+
default: getenv('JS_ROOT', ''),
|
|
61
|
+
describe:
|
|
62
|
+
'JavaScript package root directory (auto-detected if not specified)',
|
|
63
|
+
}),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Store the original working directory to restore after cd commands
|
|
67
|
+
// IMPORTANT: command-stream's cd is a virtual command that calls process.chdir()
|
|
68
|
+
const originalCwd = process.cwd();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const { bumpType, description, jsRoot: jsRootArg } = config;
|
|
72
|
+
|
|
73
|
+
// Get JavaScript package root (auto-detect or use explicit config)
|
|
74
|
+
const jsRootConfig = jsRootArg || parseJsRootConfig();
|
|
75
|
+
const jsRoot = getJsRoot({ jsRoot: jsRootConfig, verbose: true });
|
|
76
|
+
|
|
77
|
+
const finalDescription = description || `Manual ${bumpType} release`;
|
|
78
|
+
|
|
79
|
+
if (!bumpType || !['major', 'minor', 'patch'].includes(bumpType)) {
|
|
80
|
+
console.error(
|
|
81
|
+
'Usage: node scripts/instant-version-bump.mjs --bump-type <major|minor|patch> [--description <description>] [--js-root <path>]'
|
|
82
|
+
);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`\nBumping version (${bumpType})...`);
|
|
87
|
+
|
|
88
|
+
// Get current version
|
|
89
|
+
const packageJsonPath = getPackageJsonPath({ jsRoot });
|
|
90
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
91
|
+
const oldVersion = packageJson.version;
|
|
92
|
+
console.log(`Current version: ${oldVersion}`);
|
|
93
|
+
|
|
94
|
+
// Bump version using npm version (doesn't create git tag)
|
|
95
|
+
// IMPORTANT: cd is a virtual command that calls process.chdir(), so we restore after
|
|
96
|
+
if (needsCd({ jsRoot })) {
|
|
97
|
+
await $`cd ${jsRoot} && npm version ${bumpType} --no-git-tag-version`;
|
|
98
|
+
process.chdir(originalCwd);
|
|
99
|
+
} else {
|
|
100
|
+
await $`npm version ${bumpType} --no-git-tag-version`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Get new version
|
|
104
|
+
const updatedPackageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
105
|
+
const newVersion = updatedPackageJson.version;
|
|
106
|
+
console.log(`New version: ${newVersion}`);
|
|
107
|
+
|
|
108
|
+
// Update CHANGELOG.md
|
|
109
|
+
console.log('\nUpdating CHANGELOG.md...');
|
|
110
|
+
const changelogPath =
|
|
111
|
+
jsRoot === '.' ? 'CHANGELOG.md' : join(jsRoot, 'CHANGELOG.md');
|
|
112
|
+
let changelog = readFileSync(changelogPath, 'utf-8');
|
|
113
|
+
|
|
114
|
+
// Create new changelog entry
|
|
115
|
+
const newEntry = `## ${newVersion}
|
|
116
|
+
|
|
117
|
+
### ${bumpType.charAt(0).toUpperCase() + bumpType.slice(1)} Changes
|
|
118
|
+
|
|
119
|
+
- ${finalDescription}
|
|
120
|
+
|
|
121
|
+
`;
|
|
122
|
+
|
|
123
|
+
// Insert new entry after the first heading (# Changelog or similar)
|
|
124
|
+
// Look for the first ## heading and insert before it
|
|
125
|
+
const firstVersionMatch = changelog.match(/^## /m);
|
|
126
|
+
|
|
127
|
+
if (firstVersionMatch) {
|
|
128
|
+
const insertPosition = firstVersionMatch.index;
|
|
129
|
+
changelog =
|
|
130
|
+
changelog.slice(0, insertPosition) +
|
|
131
|
+
newEntry +
|
|
132
|
+
changelog.slice(insertPosition);
|
|
133
|
+
} else {
|
|
134
|
+
// If no version headings exist, append after the main heading
|
|
135
|
+
const mainHeadingMatch = changelog.match(/^# .+$/m);
|
|
136
|
+
if (mainHeadingMatch) {
|
|
137
|
+
const insertPosition =
|
|
138
|
+
mainHeadingMatch.index + mainHeadingMatch[0].length;
|
|
139
|
+
changelog = `${changelog.slice(0, insertPosition)}\n\n${newEntry}${changelog.slice(insertPosition)}`;
|
|
140
|
+
} else {
|
|
141
|
+
// If no headings at all, prepend
|
|
142
|
+
changelog = `${newEntry}\n${changelog}`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
writeFileSync(changelogPath, changelog, 'utf-8');
|
|
147
|
+
console.log('✅ CHANGELOG.md updated');
|
|
148
|
+
|
|
149
|
+
// Synchronize package-lock.json
|
|
150
|
+
console.log('\nSynchronizing package-lock.json...');
|
|
151
|
+
|
|
152
|
+
// Use --legacy-peer-deps to handle peer dependency conflicts
|
|
153
|
+
// This addresses npm ERESOLVE errors documented in issue #111 / PR #112
|
|
154
|
+
// IMPORTANT: cd is a virtual command that calls process.chdir(), so we restore after
|
|
155
|
+
if (needsCd({ jsRoot })) {
|
|
156
|
+
await $`cd ${jsRoot} && npm install --package-lock-only --legacy-peer-deps`;
|
|
157
|
+
process.chdir(originalCwd);
|
|
158
|
+
} else {
|
|
159
|
+
await $`npm install --package-lock-only --legacy-peer-deps`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log('\n✅ Instant version bump complete');
|
|
163
|
+
console.log(`Version: ${oldVersion} → ${newVersion}`);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
// Restore cwd on error
|
|
166
|
+
process.chdir(originalCwd);
|
|
167
|
+
console.error('Error during instant version bump:', error.message);
|
|
168
|
+
if (process.env.DEBUG) {
|
|
169
|
+
console.error('Stack trace:', error.stack);
|
|
170
|
+
}
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JavaScript package path detection utility
|
|
5
|
+
*
|
|
6
|
+
* Automatically detects the JavaScript package root for both:
|
|
7
|
+
* - Single-language repositories (package.json in root)
|
|
8
|
+
* - Multi-language repositories (package.json in js/ subfolder)
|
|
9
|
+
*
|
|
10
|
+
* This utility addresses the issues documented in:
|
|
11
|
+
* - Issue #21: Supporting both single and multi-language repository structures
|
|
12
|
+
* - Reference: link-assistant/agent PR #114
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import { getJsRoot, getPackageJsonPath, getChangesetDir, needsCd } from './js-paths.mjs';
|
|
16
|
+
*
|
|
17
|
+
* const jsRoot = getJsRoot(); // Returns 'js' or '.'
|
|
18
|
+
* const pkgPath = getPackageJsonPath(); // Returns 'js/package.json' or './package.json'
|
|
19
|
+
* const changesetDir = getChangesetDir(); // Returns 'js/.changeset' or './.changeset'
|
|
20
|
+
*
|
|
21
|
+
* Configuration:
|
|
22
|
+
* - CLI: --js-root <path>
|
|
23
|
+
* - Environment: JS_ROOT=<path>
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { existsSync } from 'fs';
|
|
27
|
+
import { join } from 'path';
|
|
28
|
+
|
|
29
|
+
// Cache for detected paths (computed once per process)
|
|
30
|
+
let cachedJsRoot = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detect JavaScript package root directory
|
|
34
|
+
* Checks in order:
|
|
35
|
+
* 1. ./package.json (single-language repo)
|
|
36
|
+
* 2. ./js/package.json (multi-language repo)
|
|
37
|
+
*
|
|
38
|
+
* @param {Object} options - Configuration options
|
|
39
|
+
* @param {string} [options.jsRoot] - Explicitly set JavaScript root (overrides auto-detection)
|
|
40
|
+
* @param {boolean} [options.verbose=false] - Log detection details
|
|
41
|
+
* @returns {string} The JavaScript root directory ('.' or 'js')
|
|
42
|
+
* @throws {Error} If no package.json is found in expected locations
|
|
43
|
+
*/
|
|
44
|
+
export function getJsRoot(options = {}) {
|
|
45
|
+
const { jsRoot: explicitRoot, verbose = false } = options;
|
|
46
|
+
|
|
47
|
+
// If explicitly configured, use that
|
|
48
|
+
if (explicitRoot !== undefined && explicitRoot !== '') {
|
|
49
|
+
if (verbose) {
|
|
50
|
+
console.log(
|
|
51
|
+
`Using explicitly configured JavaScript root: ${explicitRoot}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return explicitRoot;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Return cached value if already computed
|
|
58
|
+
if (cachedJsRoot !== null) {
|
|
59
|
+
return cachedJsRoot;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check for single-language repo (package.json in root)
|
|
63
|
+
if (existsSync('./package.json')) {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
console.log('Detected single-language repository (package.json in root)');
|
|
66
|
+
}
|
|
67
|
+
cachedJsRoot = '.';
|
|
68
|
+
return cachedJsRoot;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Check for multi-language repo (package.json in js/ subfolder)
|
|
72
|
+
if (existsSync('./js/package.json')) {
|
|
73
|
+
if (verbose) {
|
|
74
|
+
console.log('Detected multi-language repository (package.json in js/)');
|
|
75
|
+
}
|
|
76
|
+
cachedJsRoot = 'js';
|
|
77
|
+
return cachedJsRoot;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// No package.json found
|
|
81
|
+
throw new Error(
|
|
82
|
+
'Could not find package.json in expected locations.\n' +
|
|
83
|
+
'Searched in:\n' +
|
|
84
|
+
' - ./package.json (single-language repository)\n' +
|
|
85
|
+
' - ./js/package.json (multi-language repository)\n\n' +
|
|
86
|
+
'To fix this, either:\n' +
|
|
87
|
+
' 1. Run the script from the repository root\n' +
|
|
88
|
+
' 2. Explicitly configure the JavaScript root using --js-root option\n' +
|
|
89
|
+
' 3. Set the JS_ROOT environment variable'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the path to package.json
|
|
95
|
+
* @param {Object} options - Configuration options (passed to getJsRoot)
|
|
96
|
+
* @returns {string} Path to package.json
|
|
97
|
+
*/
|
|
98
|
+
export function getPackageJsonPath(options = {}) {
|
|
99
|
+
const jsRoot =
|
|
100
|
+
options.jsRoot !== undefined ? options.jsRoot : getJsRoot(options);
|
|
101
|
+
return jsRoot === '.' ? './package.json' : join(jsRoot, 'package.json');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the path to package-lock.json
|
|
106
|
+
* @param {Object} options - Configuration options (passed to getJsRoot)
|
|
107
|
+
* @returns {string} Path to package-lock.json
|
|
108
|
+
*/
|
|
109
|
+
export function getPackageLockPath(options = {}) {
|
|
110
|
+
const jsRoot =
|
|
111
|
+
options.jsRoot !== undefined ? options.jsRoot : getJsRoot(options);
|
|
112
|
+
return jsRoot === '.'
|
|
113
|
+
? './package-lock.json'
|
|
114
|
+
: join(jsRoot, 'package-lock.json');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get the path to .changeset directory
|
|
119
|
+
* @param {Object} options - Configuration options (passed to getJsRoot)
|
|
120
|
+
* @returns {string} Path to .changeset directory
|
|
121
|
+
*/
|
|
122
|
+
export function getChangesetDir(options = {}) {
|
|
123
|
+
const jsRoot =
|
|
124
|
+
options.jsRoot !== undefined ? options.jsRoot : getJsRoot(options);
|
|
125
|
+
return jsRoot === '.' ? './.changeset' : join(jsRoot, '.changeset');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the cd command prefix for running npm commands
|
|
130
|
+
* Returns empty string for single-language repos, 'cd js && ' for multi-language repos
|
|
131
|
+
* @param {Object} options - Configuration options (passed to getJsRoot)
|
|
132
|
+
* @returns {string} CD prefix for shell commands
|
|
133
|
+
*/
|
|
134
|
+
export function getCdPrefix(options = {}) {
|
|
135
|
+
const jsRoot =
|
|
136
|
+
options.jsRoot !== undefined ? options.jsRoot : getJsRoot(options);
|
|
137
|
+
return jsRoot === '.' ? '' : `cd ${jsRoot} && `;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if we need to change directory before running npm commands
|
|
142
|
+
* @param {Object} options - Configuration options (passed to getJsRoot)
|
|
143
|
+
* @returns {boolean} True if cd is needed
|
|
144
|
+
*/
|
|
145
|
+
export function needsCd(options = {}) {
|
|
146
|
+
const jsRoot =
|
|
147
|
+
options.jsRoot !== undefined ? options.jsRoot : getJsRoot(options);
|
|
148
|
+
return jsRoot !== '.';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Reset the cached JavaScript root (useful for testing)
|
|
153
|
+
*/
|
|
154
|
+
export function resetCache() {
|
|
155
|
+
cachedJsRoot = null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Parse JavaScript root from CLI arguments or environment
|
|
160
|
+
* Supports --js-root argument and JS_ROOT environment variable
|
|
161
|
+
* @returns {string|undefined} Configured JavaScript root or undefined for auto-detection
|
|
162
|
+
*/
|
|
163
|
+
export function parseJsRootConfig() {
|
|
164
|
+
// Check CLI arguments
|
|
165
|
+
const args = process.argv.slice(2);
|
|
166
|
+
const jsRootIndex = args.indexOf('--js-root');
|
|
167
|
+
if (jsRootIndex >= 0 && args[jsRootIndex + 1]) {
|
|
168
|
+
return args[jsRootIndex + 1];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Check environment variable
|
|
172
|
+
if (process.env.JS_ROOT) {
|
|
173
|
+
return process.env.JS_ROOT;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merge multiple changeset files into a single changeset
|
|
5
|
+
*
|
|
6
|
+
* Key behavior:
|
|
7
|
+
* - Combines all pending changesets into a single changeset file
|
|
8
|
+
* - Uses the highest version bump type (major > minor > patch)
|
|
9
|
+
* - Preserves all descriptions in chronological order (by file modification time)
|
|
10
|
+
* - Removes the individual changeset files after merging
|
|
11
|
+
* - Does nothing if there's only one or no changesets
|
|
12
|
+
*
|
|
13
|
+
* This script is run before `changeset version` to ensure a clean release
|
|
14
|
+
* even when multiple PRs have merged before a release cycle.
|
|
15
|
+
*
|
|
16
|
+
* IMPORTANT: Update the package name below to match your package.json
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
readdirSync,
|
|
21
|
+
readFileSync,
|
|
22
|
+
writeFileSync,
|
|
23
|
+
unlinkSync,
|
|
24
|
+
statSync,
|
|
25
|
+
} from 'fs';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
|
|
28
|
+
// TODO: Update this to match your package name in package.json
|
|
29
|
+
const PACKAGE_NAME = 'glab-setup-git-identity';
|
|
30
|
+
const CHANGESET_DIR = '.changeset';
|
|
31
|
+
|
|
32
|
+
// Version bump type priority (higher number = higher priority)
|
|
33
|
+
const BUMP_PRIORITY = {
|
|
34
|
+
patch: 1,
|
|
35
|
+
minor: 2,
|
|
36
|
+
major: 3,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate a random changeset file name (similar to what @changesets/cli does)
|
|
41
|
+
* @returns {string}
|
|
42
|
+
*/
|
|
43
|
+
function generateChangesetName() {
|
|
44
|
+
const adjectives = [
|
|
45
|
+
'bright',
|
|
46
|
+
'calm',
|
|
47
|
+
'cool',
|
|
48
|
+
'cyan',
|
|
49
|
+
'dark',
|
|
50
|
+
'fast',
|
|
51
|
+
'gold',
|
|
52
|
+
'good',
|
|
53
|
+
'green',
|
|
54
|
+
'happy',
|
|
55
|
+
'kind',
|
|
56
|
+
'loud',
|
|
57
|
+
'neat',
|
|
58
|
+
'nice',
|
|
59
|
+
'pink',
|
|
60
|
+
'proud',
|
|
61
|
+
'quick',
|
|
62
|
+
'red',
|
|
63
|
+
'rich',
|
|
64
|
+
'safe',
|
|
65
|
+
'shy',
|
|
66
|
+
'soft',
|
|
67
|
+
'sweet',
|
|
68
|
+
'tall',
|
|
69
|
+
'warm',
|
|
70
|
+
'wise',
|
|
71
|
+
'young',
|
|
72
|
+
];
|
|
73
|
+
const nouns = [
|
|
74
|
+
'apple',
|
|
75
|
+
'bird',
|
|
76
|
+
'book',
|
|
77
|
+
'car',
|
|
78
|
+
'cat',
|
|
79
|
+
'cloud',
|
|
80
|
+
'desk',
|
|
81
|
+
'dog',
|
|
82
|
+
'door',
|
|
83
|
+
'fish',
|
|
84
|
+
'flower',
|
|
85
|
+
'frog',
|
|
86
|
+
'grass',
|
|
87
|
+
'house',
|
|
88
|
+
'key',
|
|
89
|
+
'lake',
|
|
90
|
+
'leaf',
|
|
91
|
+
'moon',
|
|
92
|
+
'mouse',
|
|
93
|
+
'owl',
|
|
94
|
+
'park',
|
|
95
|
+
'rain',
|
|
96
|
+
'river',
|
|
97
|
+
'rock',
|
|
98
|
+
'sea',
|
|
99
|
+
'star',
|
|
100
|
+
'sun',
|
|
101
|
+
'tree',
|
|
102
|
+
'wave',
|
|
103
|
+
'wind',
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const randomAdjective =
|
|
107
|
+
adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
108
|
+
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
109
|
+
|
|
110
|
+
return `${randomAdjective}-${randomNoun}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Parse a changeset file and extract its metadata
|
|
115
|
+
* @param {string} filePath
|
|
116
|
+
* @returns {{type: string, description: string, mtime: Date} | null}
|
|
117
|
+
*/
|
|
118
|
+
function parseChangeset(filePath) {
|
|
119
|
+
try {
|
|
120
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
121
|
+
const stats = statSync(filePath);
|
|
122
|
+
|
|
123
|
+
// Extract version type - support both quoted and unquoted package names
|
|
124
|
+
const versionTypeRegex = new RegExp(
|
|
125
|
+
`^['"]?${PACKAGE_NAME.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]?:\\s+(major|minor|patch)`,
|
|
126
|
+
'm'
|
|
127
|
+
);
|
|
128
|
+
const versionTypeMatch = content.match(versionTypeRegex);
|
|
129
|
+
|
|
130
|
+
if (!versionTypeMatch) {
|
|
131
|
+
console.warn(
|
|
132
|
+
`Warning: Could not parse version type from ${filePath}, skipping`
|
|
133
|
+
);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Extract description
|
|
138
|
+
const parts = content.split('---');
|
|
139
|
+
const description =
|
|
140
|
+
parts.length >= 3 ? parts.slice(2).join('---').trim() : '';
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
type: versionTypeMatch[1],
|
|
144
|
+
description,
|
|
145
|
+
mtime: stats.mtime,
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.warn(`Warning: Failed to parse ${filePath}: ${error.message}`);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the highest priority bump type
|
|
155
|
+
* @param {string[]} types
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
function getHighestBumpType(types) {
|
|
159
|
+
let highest = 'patch';
|
|
160
|
+
for (const type of types) {
|
|
161
|
+
if (BUMP_PRIORITY[type] > BUMP_PRIORITY[highest]) {
|
|
162
|
+
highest = type;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return highest;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a merged changeset file
|
|
170
|
+
* @param {string} type
|
|
171
|
+
* @param {string[]} descriptions
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
function createMergedChangeset(type, descriptions) {
|
|
175
|
+
const combinedDescription = descriptions.join('\n\n');
|
|
176
|
+
|
|
177
|
+
return `---
|
|
178
|
+
'${PACKAGE_NAME}': ${type}
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
${combinedDescription}
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function main() {
|
|
186
|
+
console.log('Checking for multiple changesets to merge...');
|
|
187
|
+
|
|
188
|
+
// Get all changeset files
|
|
189
|
+
const changesetFiles = readdirSync(CHANGESET_DIR).filter(
|
|
190
|
+
(file) => file.endsWith('.md') && file !== 'README.md'
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
console.log(`Found ${changesetFiles.length} changeset file(s)`);
|
|
194
|
+
|
|
195
|
+
// If 0 or 1 changesets, nothing to merge
|
|
196
|
+
if (changesetFiles.length <= 1) {
|
|
197
|
+
console.log('No merging needed (0 or 1 changeset found)');
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log('Multiple changesets found, merging...');
|
|
202
|
+
changesetFiles.forEach((file) => console.log(` - ${file}`));
|
|
203
|
+
|
|
204
|
+
// Parse all changesets
|
|
205
|
+
const parsedChangesets = [];
|
|
206
|
+
for (const file of changesetFiles) {
|
|
207
|
+
const filePath = join(CHANGESET_DIR, file);
|
|
208
|
+
const parsed = parseChangeset(filePath);
|
|
209
|
+
if (parsed) {
|
|
210
|
+
parsedChangesets.push({
|
|
211
|
+
file,
|
|
212
|
+
filePath,
|
|
213
|
+
...parsed,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (parsedChangesets.length === 0) {
|
|
219
|
+
console.error('Error: No valid changesets could be parsed');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Sort by modification time (oldest first) to preserve chronological order
|
|
224
|
+
parsedChangesets.sort((a, b) => a.mtime.getTime() - b.mtime.getTime());
|
|
225
|
+
|
|
226
|
+
// Determine the highest bump type
|
|
227
|
+
const bumpTypes = parsedChangesets.map((c) => c.type);
|
|
228
|
+
const highestBumpType = getHighestBumpType(bumpTypes);
|
|
229
|
+
|
|
230
|
+
console.log(`\nMerge summary:`);
|
|
231
|
+
console.log(` Bump types found: ${[...new Set(bumpTypes)].join(', ')}`);
|
|
232
|
+
console.log(` Using highest: ${highestBumpType}`);
|
|
233
|
+
|
|
234
|
+
// Collect descriptions in chronological order
|
|
235
|
+
const descriptions = parsedChangesets
|
|
236
|
+
.filter((c) => c.description)
|
|
237
|
+
.map((c) => c.description);
|
|
238
|
+
|
|
239
|
+
console.log(` Descriptions to merge: ${descriptions.length}`);
|
|
240
|
+
|
|
241
|
+
// Create merged changeset content
|
|
242
|
+
const mergedContent = createMergedChangeset(highestBumpType, descriptions);
|
|
243
|
+
|
|
244
|
+
// Generate a unique name for the merged changeset
|
|
245
|
+
const mergedFileName = `merged-${generateChangesetName()}.md`;
|
|
246
|
+
const mergedFilePath = join(CHANGESET_DIR, mergedFileName);
|
|
247
|
+
|
|
248
|
+
// Write the merged changeset
|
|
249
|
+
writeFileSync(mergedFilePath, mergedContent);
|
|
250
|
+
console.log(`\nCreated merged changeset: ${mergedFileName}`);
|
|
251
|
+
|
|
252
|
+
// Remove the original changeset files
|
|
253
|
+
console.log('\nRemoving original changeset files:');
|
|
254
|
+
for (const changeset of parsedChangesets) {
|
|
255
|
+
unlinkSync(changeset.filePath);
|
|
256
|
+
console.log(` Removed: ${changeset.file}`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
console.log('\nChangeset merge completed successfully');
|
|
260
|
+
console.log(`\nMerged changeset content:\n${mergedContent}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
main();
|