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,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repository": "https://github.com/ProverCoderAI/effect-template",
|
|
3
|
+
"analysis_date": "2025-12-18",
|
|
4
|
+
"description": "Minimal Vite-powered TypeScript console starter using Effect",
|
|
5
|
+
"package_manager": "pnpm@10.24.0",
|
|
6
|
+
"configuration_files": {
|
|
7
|
+
"build_and_development": [
|
|
8
|
+
"vite.config.ts",
|
|
9
|
+
"vitest.config.ts",
|
|
10
|
+
"tsconfig.json"
|
|
11
|
+
],
|
|
12
|
+
"code_quality": [
|
|
13
|
+
"biome.json",
|
|
14
|
+
"eslint.config.mts",
|
|
15
|
+
"linter.config.json",
|
|
16
|
+
".jscpd.json"
|
|
17
|
+
],
|
|
18
|
+
"environment": [
|
|
19
|
+
"flake.nix",
|
|
20
|
+
"package.json",
|
|
21
|
+
"pnpm-lock.yaml",
|
|
22
|
+
".gitignore"
|
|
23
|
+
],
|
|
24
|
+
"documentation": ["AGENTS.md"],
|
|
25
|
+
"editor_config": [".vscode/settings.json"],
|
|
26
|
+
"changesets": [".changeset/config.json"]
|
|
27
|
+
},
|
|
28
|
+
"github_workflows": {
|
|
29
|
+
"check.yml": {
|
|
30
|
+
"triggers": ["workflow_dispatch", "pull_request", "push to main"],
|
|
31
|
+
"jobs": ["build", "types", "lint", "test"],
|
|
32
|
+
"timeout": "10 minutes",
|
|
33
|
+
"concurrency": "cancel-in-progress"
|
|
34
|
+
},
|
|
35
|
+
"release.yml": {
|
|
36
|
+
"triggers": ["push to main"],
|
|
37
|
+
"purpose": "Automated versioning and npm publishing via Changesets",
|
|
38
|
+
"permissions": [
|
|
39
|
+
"contents: write",
|
|
40
|
+
"id-token: write",
|
|
41
|
+
"pull-requests: write"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"snapshot.yml": {
|
|
45
|
+
"triggers": ["pull_request", "workflow_dispatch"],
|
|
46
|
+
"purpose": "Package snapshot creation for PRs"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"custom_github_actions": {
|
|
50
|
+
".github/actions/setup/action.yml": {
|
|
51
|
+
"purpose": "Standard setup and dependency installation",
|
|
52
|
+
"steps": [
|
|
53
|
+
"pnpm installation",
|
|
54
|
+
"Node.js setup with caching",
|
|
55
|
+
"dependency installation"
|
|
56
|
+
],
|
|
57
|
+
"default_node_version": "20.16.0"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"eslint_rules": {
|
|
61
|
+
"complexity_limits": {
|
|
62
|
+
"max_cyclomatic_complexity": 8,
|
|
63
|
+
"max_function_lines": 50,
|
|
64
|
+
"max_file_lines": 300,
|
|
65
|
+
"max_function_parameters": 5,
|
|
66
|
+
"max_nesting_depth": 4
|
|
67
|
+
},
|
|
68
|
+
"typescript_rules": {
|
|
69
|
+
"restrict_template_expressions": true,
|
|
70
|
+
"array_type": "generic",
|
|
71
|
+
"consistent_type_imports": true,
|
|
72
|
+
"unused_variables": "error"
|
|
73
|
+
},
|
|
74
|
+
"prohibited_patterns": {
|
|
75
|
+
"unknown_keyword": true,
|
|
76
|
+
"switch_statements": true,
|
|
77
|
+
"async_await": true,
|
|
78
|
+
"promise_constructor": true,
|
|
79
|
+
"commonjs_require": true,
|
|
80
|
+
"throw_string_literals": true
|
|
81
|
+
},
|
|
82
|
+
"formatting": {
|
|
83
|
+
"line_width": 120,
|
|
84
|
+
"quotes": "double",
|
|
85
|
+
"semicolons": "asi"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"typescript_config": {
|
|
89
|
+
"strict_options": {
|
|
90
|
+
"strict": true,
|
|
91
|
+
"alwaysStrict": true,
|
|
92
|
+
"noUnusedLocals": true,
|
|
93
|
+
"noUnusedParameters": true,
|
|
94
|
+
"noImplicitReturns": true,
|
|
95
|
+
"noFallthroughCasesInSwitch": true,
|
|
96
|
+
"exactOptionalPropertyTypes": true,
|
|
97
|
+
"noUncheckedIndexedAccess": true,
|
|
98
|
+
"noImplicitOverride": true,
|
|
99
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
100
|
+
"allowUnusedLabels": false,
|
|
101
|
+
"allowUnreachableCode": false
|
|
102
|
+
},
|
|
103
|
+
"module_system": {
|
|
104
|
+
"target": "ES2022",
|
|
105
|
+
"module": "NodeNext",
|
|
106
|
+
"moduleResolution": "NodeNext"
|
|
107
|
+
},
|
|
108
|
+
"plugins": ["@effect/language-service"]
|
|
109
|
+
},
|
|
110
|
+
"vitest_config": {
|
|
111
|
+
"environment": "node",
|
|
112
|
+
"coverage_provider": "v8",
|
|
113
|
+
"coverage_thresholds": {
|
|
114
|
+
"core_modules": {
|
|
115
|
+
"branches": 100,
|
|
116
|
+
"functions": 100,
|
|
117
|
+
"lines": 100,
|
|
118
|
+
"statements": 100
|
|
119
|
+
},
|
|
120
|
+
"global_minimum": {
|
|
121
|
+
"branches": 10,
|
|
122
|
+
"functions": 10,
|
|
123
|
+
"lines": 10,
|
|
124
|
+
"statements": 10
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"mock_management": {
|
|
128
|
+
"clearMocks": true,
|
|
129
|
+
"mockReset": true,
|
|
130
|
+
"restoreMocks": true
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"jscpd_config": {
|
|
134
|
+
"threshold": 0,
|
|
135
|
+
"minTokens": 30,
|
|
136
|
+
"minLines": 5,
|
|
137
|
+
"skipComments": true,
|
|
138
|
+
"ignored_paths": [
|
|
139
|
+
"**/node_modules/**",
|
|
140
|
+
"**/build/**",
|
|
141
|
+
"**/dist/**",
|
|
142
|
+
"**/*.min.js",
|
|
143
|
+
"**/reports/**"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
"vscode_settings": {
|
|
147
|
+
"typescript_sdk": "node_modules/typescript/lib",
|
|
148
|
+
"import_preference": "relative",
|
|
149
|
+
"format_on_save": true,
|
|
150
|
+
"eslint_formatting": true,
|
|
151
|
+
"quick_suggestions": true,
|
|
152
|
+
"suggestion_delay": "10ms"
|
|
153
|
+
},
|
|
154
|
+
"nix_flake": {
|
|
155
|
+
"purpose": "Development environment",
|
|
156
|
+
"packages": ["corepack", "nodejs_22", "python3"]
|
|
157
|
+
},
|
|
158
|
+
"key_dependencies": {
|
|
159
|
+
"effect_ecosystem": [
|
|
160
|
+
"effect",
|
|
161
|
+
"@effect/cli",
|
|
162
|
+
"@effect/cluster",
|
|
163
|
+
"@effect/platform",
|
|
164
|
+
"@effect/schema",
|
|
165
|
+
"@effect/sql",
|
|
166
|
+
"@effect/workflow"
|
|
167
|
+
],
|
|
168
|
+
"typescript_utilities": ["ts-morph", "ts-pattern"],
|
|
169
|
+
"testing": ["vitest", "@effect/vitest"],
|
|
170
|
+
"linting": [
|
|
171
|
+
"eslint",
|
|
172
|
+
"@biomejs/biome",
|
|
173
|
+
"typescript-eslint",
|
|
174
|
+
"eslint-plugin-sonarjs",
|
|
175
|
+
"eslint-plugin-unicorn"
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import prettierConfig from 'eslint-config-prettier';
|
|
3
|
+
import prettierPlugin from 'eslint-plugin-prettier';
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
js.configs.recommended,
|
|
7
|
+
prettierConfig,
|
|
8
|
+
{
|
|
9
|
+
files: ['**/*.js', '**/*.mjs'],
|
|
10
|
+
plugins: {
|
|
11
|
+
prettier: prettierPlugin,
|
|
12
|
+
},
|
|
13
|
+
languageOptions: {
|
|
14
|
+
ecmaVersion: 'latest',
|
|
15
|
+
sourceType: 'module',
|
|
16
|
+
globals: {
|
|
17
|
+
// Node.js globals
|
|
18
|
+
console: 'readonly',
|
|
19
|
+
process: 'readonly',
|
|
20
|
+
Buffer: 'readonly',
|
|
21
|
+
__dirname: 'readonly',
|
|
22
|
+
__filename: 'readonly',
|
|
23
|
+
// Node.js 18+ globals
|
|
24
|
+
fetch: 'readonly',
|
|
25
|
+
// Runtime-specific globals
|
|
26
|
+
Bun: 'readonly',
|
|
27
|
+
Deno: 'readonly',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
rules: {
|
|
31
|
+
// Prettier integration
|
|
32
|
+
'prettier/prettier': 'error',
|
|
33
|
+
|
|
34
|
+
// Code quality rules
|
|
35
|
+
'no-unused-vars': 'error',
|
|
36
|
+
'no-console': 'off', // Allow console in this project
|
|
37
|
+
'no-debugger': 'error',
|
|
38
|
+
|
|
39
|
+
// Best practices
|
|
40
|
+
eqeqeq: ['error', 'always'],
|
|
41
|
+
curly: ['error', 'all'],
|
|
42
|
+
'no-var': 'error',
|
|
43
|
+
'prefer-const': 'error',
|
|
44
|
+
'prefer-arrow-callback': 'error',
|
|
45
|
+
'no-duplicate-imports': 'error',
|
|
46
|
+
|
|
47
|
+
// ES6+ features
|
|
48
|
+
'arrow-body-style': ['error', 'as-needed'],
|
|
49
|
+
'object-shorthand': ['error', 'always'],
|
|
50
|
+
'prefer-template': 'error',
|
|
51
|
+
|
|
52
|
+
// Async/await
|
|
53
|
+
'no-async-promise-executor': 'error',
|
|
54
|
+
'require-await': 'warn',
|
|
55
|
+
|
|
56
|
+
// Comments and documentation
|
|
57
|
+
'spaced-comment': ['error', 'always', { markers: ['/'] }],
|
|
58
|
+
|
|
59
|
+
// Complexity rules - reasonable thresholds for maintainability
|
|
60
|
+
complexity: ['warn', 15], // Cyclomatic complexity - allow more complex logic than strict 8
|
|
61
|
+
'max-depth': ['warn', 5], // Maximum nesting depth - slightly more lenient than strict 4
|
|
62
|
+
'max-lines-per-function': [
|
|
63
|
+
'warn',
|
|
64
|
+
{
|
|
65
|
+
max: 150, // More reasonable than strict 50 lines per function
|
|
66
|
+
skipBlankLines: true,
|
|
67
|
+
skipComments: true,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
'max-params': ['warn', 6], // Maximum function parameters - slightly more lenient than strict 5
|
|
71
|
+
'max-statements': ['warn', 60], // Maximum statements per function - reasonable limit for orchestration functions
|
|
72
|
+
'max-lines': ['error', 1500], // Maximum lines per file - counts all lines including blank lines and comments
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
// Test files have different requirements
|
|
77
|
+
files: ['tests/**/*.js', '**/*.test.js'],
|
|
78
|
+
rules: {
|
|
79
|
+
'require-await': 'off', // Async functions without await are common in tests
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
ignores: [
|
|
84
|
+
'node_modules/**',
|
|
85
|
+
'coverage/**',
|
|
86
|
+
'dist/**',
|
|
87
|
+
'*.min.js',
|
|
88
|
+
'.eslintcache',
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic usage example for glab-setup-git-identity
|
|
3
|
+
* Demonstrates how to use the package
|
|
4
|
+
*
|
|
5
|
+
* Prerequisites:
|
|
6
|
+
* - GitLab CLI (glab) installed
|
|
7
|
+
* - Authenticated with GitLab via `glab auth login`
|
|
8
|
+
*
|
|
9
|
+
* Run with any runtime:
|
|
10
|
+
* - Bun: bun examples/basic-usage.js
|
|
11
|
+
* - Node.js: node examples/basic-usage.js
|
|
12
|
+
* - Deno: deno run --allow-run examples/basic-usage.js
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
defaultAuthOptions,
|
|
17
|
+
isGlabAuthenticated,
|
|
18
|
+
getGitLabUserInfo,
|
|
19
|
+
verifyGitIdentity,
|
|
20
|
+
setupGitIdentity,
|
|
21
|
+
} from '../src/index.js';
|
|
22
|
+
|
|
23
|
+
console.log('glab-setup-git-identity - Basic Usage Example\n');
|
|
24
|
+
|
|
25
|
+
// Show default options
|
|
26
|
+
console.log('Default auth options:');
|
|
27
|
+
console.log(` Hostname: ${defaultAuthOptions.hostname}`);
|
|
28
|
+
console.log(` Git protocol: ${defaultAuthOptions.gitProtocol}`);
|
|
29
|
+
console.log(` Use keyring: ${defaultAuthOptions.useKeyring}`);
|
|
30
|
+
|
|
31
|
+
// Check current git identity
|
|
32
|
+
console.log('\nCurrent git identity (global):');
|
|
33
|
+
const currentIdentity = await verifyGitIdentity({ scope: 'global' });
|
|
34
|
+
console.log(` user.name: ${currentIdentity.username || '(not set)'}`);
|
|
35
|
+
console.log(` user.email: ${currentIdentity.email || '(not set)'}`);
|
|
36
|
+
|
|
37
|
+
// Check if glab is authenticated
|
|
38
|
+
console.log('\nChecking GitLab CLI authentication...');
|
|
39
|
+
const authenticated = await isGlabAuthenticated();
|
|
40
|
+
|
|
41
|
+
if (!authenticated) {
|
|
42
|
+
console.log(' GitLab CLI is not authenticated.');
|
|
43
|
+
console.log(' Please run: glab auth login');
|
|
44
|
+
console.log('\nExiting example.');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log(' GitLab CLI is authenticated!');
|
|
49
|
+
|
|
50
|
+
// Get GitLab user info
|
|
51
|
+
console.log('\nFetching GitLab user information...');
|
|
52
|
+
try {
|
|
53
|
+
const { username, email } = await getGitLabUserInfo();
|
|
54
|
+
console.log(` GitLab username: ${username}`);
|
|
55
|
+
console.log(` GitLab email: ${email}`);
|
|
56
|
+
|
|
57
|
+
// Setup git identity (dry run)
|
|
58
|
+
console.log('\nDry run - would configure git as:');
|
|
59
|
+
await setupGitIdentity({ dryRun: true, verbose: false });
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(` Error: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('\nExample completed!');
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test suite for changeset-related scripts
|
|
5
|
+
* Tests validate-changeset.mjs and merge-changesets.mjs functionality
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import {
|
|
12
|
+
mkdirSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
rmSync,
|
|
15
|
+
existsSync,
|
|
16
|
+
readdirSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
} from 'fs';
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = dirname(__filename);
|
|
22
|
+
const projectRoot = join(__dirname, '..');
|
|
23
|
+
const validateChangesetPath = join(
|
|
24
|
+
projectRoot,
|
|
25
|
+
'scripts',
|
|
26
|
+
'validate-changeset.mjs'
|
|
27
|
+
);
|
|
28
|
+
const mergeChangesetsPath = join(
|
|
29
|
+
projectRoot,
|
|
30
|
+
'scripts',
|
|
31
|
+
'merge-changesets.mjs'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
let testsPassed = 0;
|
|
35
|
+
let testsFailed = 0;
|
|
36
|
+
|
|
37
|
+
function runTest(name, testFn) {
|
|
38
|
+
process.stdout.write(`Testing ${name}... `);
|
|
39
|
+
try {
|
|
40
|
+
testFn();
|
|
41
|
+
console.log('PASSED');
|
|
42
|
+
testsPassed++;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.log(`FAILED: ${error.message}`);
|
|
45
|
+
testsFailed++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function execCommand(command, options = {}) {
|
|
50
|
+
try {
|
|
51
|
+
return {
|
|
52
|
+
output: execSync(command, {
|
|
53
|
+
encoding: 'utf8',
|
|
54
|
+
stdio: 'pipe',
|
|
55
|
+
cwd: projectRoot,
|
|
56
|
+
...options,
|
|
57
|
+
}),
|
|
58
|
+
exitCode: 0,
|
|
59
|
+
};
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return {
|
|
62
|
+
output: (error.stdout || '') + (error.stderr || ''),
|
|
63
|
+
exitCode: error.status || 1,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ==========================================
|
|
69
|
+
// Tests for validate-changeset.mjs
|
|
70
|
+
// ==========================================
|
|
71
|
+
|
|
72
|
+
// Test 1: Script exists and is executable
|
|
73
|
+
runTest('validate-changeset.mjs exists', () => {
|
|
74
|
+
if (!existsSync(validateChangesetPath)) {
|
|
75
|
+
throw new Error('validate-changeset.mjs not found');
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Test 2: Syntax check
|
|
80
|
+
runTest('validate-changeset.mjs syntax check', () => {
|
|
81
|
+
const { output, exitCode } = execCommand(
|
|
82
|
+
`node --check ${validateChangesetPath}`
|
|
83
|
+
);
|
|
84
|
+
if (exitCode !== 0) {
|
|
85
|
+
throw new Error(`Syntax error: ${output}`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Test 3: Script runs without crashing (fallback mode)
|
|
90
|
+
runTest('validate-changeset.mjs runs in fallback mode', () => {
|
|
91
|
+
// Without git diff context, it falls back to checking all changesets
|
|
92
|
+
const { output } = execCommand(`node ${validateChangesetPath}`);
|
|
93
|
+
// Should either pass (if there's exactly one changeset) or fail (if not)
|
|
94
|
+
// But should not crash with an exception
|
|
95
|
+
if (
|
|
96
|
+
output.includes('Error during changeset validation') &&
|
|
97
|
+
output.includes('Cannot read')
|
|
98
|
+
) {
|
|
99
|
+
throw new Error('Script crashed unexpectedly');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ==========================================
|
|
104
|
+
// Tests for merge-changesets.mjs
|
|
105
|
+
// ==========================================
|
|
106
|
+
|
|
107
|
+
// Test 4: Script exists
|
|
108
|
+
runTest('merge-changesets.mjs exists', () => {
|
|
109
|
+
if (!existsSync(mergeChangesetsPath)) {
|
|
110
|
+
throw new Error('merge-changesets.mjs not found');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Test 5: Syntax check
|
|
115
|
+
runTest('merge-changesets.mjs syntax check', () => {
|
|
116
|
+
const { output, exitCode } = execCommand(
|
|
117
|
+
`node --check ${mergeChangesetsPath}`
|
|
118
|
+
);
|
|
119
|
+
if (exitCode !== 0) {
|
|
120
|
+
throw new Error(`Syntax error: ${output}`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// ==========================================
|
|
125
|
+
// Unit tests using mock changeset directories
|
|
126
|
+
// ==========================================
|
|
127
|
+
|
|
128
|
+
const testDir = join(projectRoot, 'experiments', 'test-changesets-temp');
|
|
129
|
+
const testChangesetDir = join(testDir, '.changeset');
|
|
130
|
+
|
|
131
|
+
function setupTestEnvironment() {
|
|
132
|
+
// Clean up if exists
|
|
133
|
+
if (existsSync(testDir)) {
|
|
134
|
+
rmSync(testDir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
mkdirSync(testChangesetDir, { recursive: true });
|
|
137
|
+
|
|
138
|
+
// Create README.md (should be ignored)
|
|
139
|
+
writeFileSync(join(testChangesetDir, 'README.md'), '# Changesets\n');
|
|
140
|
+
|
|
141
|
+
// Create config.json (should be ignored)
|
|
142
|
+
writeFileSync(join(testChangesetDir, 'config.json'), '{}');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function cleanupTestEnvironment() {
|
|
146
|
+
if (existsSync(testDir)) {
|
|
147
|
+
rmSync(testDir, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function createChangeset(filename, type, description) {
|
|
152
|
+
const content = `---
|
|
153
|
+
'my-package': ${type}
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
${description}
|
|
157
|
+
`;
|
|
158
|
+
writeFileSync(join(testChangesetDir, filename), content);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Test 6: Merge changesets correctly combines multiple changesets
|
|
162
|
+
runTest('merge-changesets.mjs combines multiple changesets', () => {
|
|
163
|
+
setupTestEnvironment();
|
|
164
|
+
try {
|
|
165
|
+
// Create two changesets
|
|
166
|
+
createChangeset('first-change.md', 'patch', 'First change description');
|
|
167
|
+
|
|
168
|
+
// Wait a bit to ensure different mtime
|
|
169
|
+
execSync('sleep 0.1');
|
|
170
|
+
|
|
171
|
+
createChangeset('second-change.md', 'minor', 'Second change description');
|
|
172
|
+
|
|
173
|
+
// Run merge script in test directory
|
|
174
|
+
const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, {
|
|
175
|
+
cwd: testDir,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (exitCode !== 0) {
|
|
179
|
+
throw new Error(`Merge failed: ${output}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check that merged changeset was created
|
|
183
|
+
const files = readdirSync(testChangesetDir).filter(
|
|
184
|
+
(f) => f.endsWith('.md') && f !== 'README.md'
|
|
185
|
+
);
|
|
186
|
+
if (files.length !== 1) {
|
|
187
|
+
throw new Error(`Expected 1 merged changeset, found ${files.length}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check that it uses the higher bump type (minor)
|
|
191
|
+
if (!output.includes('Using highest: minor')) {
|
|
192
|
+
throw new Error('Expected merged changeset to use minor bump type');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check that both descriptions are included
|
|
196
|
+
const mergedContent = readFileSync(
|
|
197
|
+
join(testChangesetDir, files[0]),
|
|
198
|
+
'utf-8'
|
|
199
|
+
);
|
|
200
|
+
if (
|
|
201
|
+
!mergedContent.includes('First change description') ||
|
|
202
|
+
!mergedContent.includes('Second change description')
|
|
203
|
+
) {
|
|
204
|
+
throw new Error('Merged changeset should contain both descriptions');
|
|
205
|
+
}
|
|
206
|
+
} finally {
|
|
207
|
+
cleanupTestEnvironment();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Test 7: Merge changesets uses major if any is major
|
|
212
|
+
runTest('merge-changesets.mjs uses highest bump type (major)', () => {
|
|
213
|
+
setupTestEnvironment();
|
|
214
|
+
try {
|
|
215
|
+
createChangeset('patch-change.md', 'patch', 'Patch change');
|
|
216
|
+
createChangeset('major-change.md', 'major', 'Major change');
|
|
217
|
+
createChangeset('minor-change.md', 'minor', 'Minor change');
|
|
218
|
+
|
|
219
|
+
const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, {
|
|
220
|
+
cwd: testDir,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (exitCode !== 0) {
|
|
224
|
+
throw new Error(`Merge failed: ${output}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!output.includes('Using highest: major')) {
|
|
228
|
+
throw new Error('Expected merged changeset to use major bump type');
|
|
229
|
+
}
|
|
230
|
+
} finally {
|
|
231
|
+
cleanupTestEnvironment();
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Test 8: Merge does nothing with single changeset
|
|
236
|
+
runTest('merge-changesets.mjs skips with single changeset', () => {
|
|
237
|
+
setupTestEnvironment();
|
|
238
|
+
try {
|
|
239
|
+
createChangeset('only-change.md', 'patch', 'Only change');
|
|
240
|
+
|
|
241
|
+
const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, {
|
|
242
|
+
cwd: testDir,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (exitCode !== 0) {
|
|
246
|
+
throw new Error(`Script failed: ${output}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!output.includes('No merging needed')) {
|
|
250
|
+
throw new Error('Expected script to skip merging with single changeset');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Verify original changeset still exists
|
|
254
|
+
const files = readdirSync(testChangesetDir).filter(
|
|
255
|
+
(f) => f.endsWith('.md') && f !== 'README.md'
|
|
256
|
+
);
|
|
257
|
+
if (files.length !== 1 || files[0] !== 'only-change.md') {
|
|
258
|
+
throw new Error('Original changeset should not be modified');
|
|
259
|
+
}
|
|
260
|
+
} finally {
|
|
261
|
+
cleanupTestEnvironment();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Test 9: Merge does nothing with no changesets
|
|
266
|
+
runTest('merge-changesets.mjs skips with no changesets', () => {
|
|
267
|
+
setupTestEnvironment();
|
|
268
|
+
try {
|
|
269
|
+
const { output, exitCode } = execCommand(`node ${mergeChangesetsPath}`, {
|
|
270
|
+
cwd: testDir,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (exitCode !== 0) {
|
|
274
|
+
throw new Error(`Script failed: ${output}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!output.includes('No merging needed')) {
|
|
278
|
+
throw new Error('Expected script to skip merging with no changesets');
|
|
279
|
+
}
|
|
280
|
+
} finally {
|
|
281
|
+
cleanupTestEnvironment();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Test 10: Validate changeset format checking
|
|
286
|
+
runTest('validate-changeset.mjs format validation', () => {
|
|
287
|
+
// This test verifies the script can be imported and has expected functions
|
|
288
|
+
// Actual validation is tested through integration
|
|
289
|
+
const { exitCode } = execCommand(`node --check ${validateChangesetPath}`);
|
|
290
|
+
if (exitCode !== 0) {
|
|
291
|
+
throw new Error('Script has syntax errors');
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Summary
|
|
296
|
+
console.log(`\n${'='.repeat(50)}`);
|
|
297
|
+
console.log(`Test Results for changeset scripts:`);
|
|
298
|
+
console.log(` Passed: ${testsPassed}`);
|
|
299
|
+
console.log(` Failed: ${testsFailed}`);
|
|
300
|
+
console.log('='.repeat(50));
|
|
301
|
+
|
|
302
|
+
// Exit with appropriate code
|
|
303
|
+
process.exit(testsFailed > 0 ? 1 : 0);
|