gh-setup-git-identity 0.2.3 → 0.3.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # gh-setup-git-identity
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 51dceeb: Fix release notes formatting to support Major and Minor changes
8
+
9
+ Previously, the format-release-notes.mjs script only looked for "### Patch Changes" sections in release notes. This caused releases with Minor or Major changes (like v0.3.0) to not be properly formatted with PR links and npm badges.
10
+
11
+ Updated the regex pattern to handle all three change types (Major, Minor, and Patch), matching the release template from test-anywhere repository.
12
+
13
+ ## 0.3.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 8c146cc: Automatically run `gh auth login` when GitHub CLI is not authenticated, enabling single-command setup instead of requiring manual login first
18
+
3
19
  ## 0.2.3
4
20
 
5
21
  ### Patch Changes
package/README.md CHANGED
@@ -13,7 +13,7 @@ A tool to setup git identity based on current GitHub user.
13
13
  Instead of manually running:
14
14
 
15
15
  ```bash
16
- printf "\n" | gh auth login -s repo,workflow,user,read:org,gist --git-protocol https --web
16
+ printf "y" | gh auth login -h github.com -s repo,workflow,user,read:org,gist --git-protocol https --web
17
17
 
18
18
  USERNAME=$(gh api user --jq '.login')
19
19
  EMAIL=$(gh api user/emails --jq '.[] | select(.primary==true) | .email')
@@ -93,16 +93,23 @@ Options:
93
93
 
94
94
  ### First Run (Not Authenticated)
95
95
 
96
- If you haven't authenticated with GitHub CLI yet, the tool will prompt you:
96
+ If you haven't authenticated with GitHub CLI yet, the tool will automatically start the authentication process:
97
97
 
98
98
  ```
99
- GitHub CLI is not authenticated.
99
+ GitHub CLI is not authenticated. Starting authentication...
100
100
 
101
- Please run the following command to login:
101
+ Starting GitHub CLI authentication...
102
102
 
103
- printf "\n" | gh auth login -s repo,workflow,user,read:org,gist --git-protocol https --web
103
+ ! First copy your one-time code: XXXX-XXXX
104
+ Press Enter to open github.com in your browser...
105
+ ```
106
+
107
+ The tool runs `gh auth login` automatically with the required scopes (`repo,workflow,user,read:org,gist`). Follow the browser-based authentication flow to complete login.
108
+
109
+ If automatic authentication fails, you can run the command manually:
104
110
 
105
- After logging in, run gh-setup-git-identity again.
111
+ ```bash
112
+ printf "y" | gh auth login -h github.com -s repo,workflow,user,read:org,gist --git-protocol https --web
106
113
  ```
107
114
 
108
115
  ### Successful Run
@@ -159,6 +166,16 @@ Check if GitHub CLI is authenticated.
159
166
 
160
167
  **Returns:** `Promise<boolean>`
161
168
 
169
+ #### `runGhAuthLogin(options?)`
170
+
171
+ Run `gh auth login` interactively with the required scopes.
172
+
173
+ **Parameters:**
174
+ - `options.verbose` - Enable verbose logging (default: `false`)
175
+ - `options.logger` - Custom logger (default: `console`)
176
+
177
+ **Returns:** `Promise<boolean>` - `true` if login was successful
178
+
162
179
  #### `getGitHubUserInfo(options?)`
163
180
 
164
181
  Get GitHub user information (username and primary email).
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test script to verify the release notes regex pattern
5
+ * works correctly for Major, Minor, and Patch changes
6
+ */
7
+
8
+ // Test cases representing different release note formats
9
+ const testCases = [
10
+ {
11
+ name: 'Minor Changes with commit hash',
12
+ body: `### Minor Changes
13
+
14
+ - 8c146cc: Automatically run \`gh auth login\` when GitHub CLI is not authenticated, enabling single-command setup instead of requiring manual login first`,
15
+ expectedType: 'Minor',
16
+ expectedHash: '8c146cc',
17
+ expectedDesc: 'Automatically run `gh auth login` when GitHub CLI is not authenticated, enabling single-command setup instead of requiring manual login first',
18
+ },
19
+ {
20
+ name: 'Patch Changes with commit hash',
21
+ body: `### Patch Changes
22
+
23
+ - abc1234: Fix a bug in the login flow`,
24
+ expectedType: 'Patch',
25
+ expectedHash: 'abc1234',
26
+ expectedDesc: 'Fix a bug in the login flow',
27
+ },
28
+ {
29
+ name: 'Major Changes with commit hash',
30
+ body: `### Major Changes
31
+
32
+ - deadbeef: Breaking change - new API`,
33
+ expectedType: 'Major',
34
+ expectedHash: 'deadbeef',
35
+ expectedDesc: 'Breaking change - new API',
36
+ },
37
+ {
38
+ name: 'Minor Changes without commit hash',
39
+ body: `### Minor Changes
40
+
41
+ - Add new feature`,
42
+ expectedType: 'Minor',
43
+ expectedHash: null,
44
+ expectedDesc: 'Add new feature',
45
+ },
46
+ ];
47
+
48
+ // The regex pattern from the updated format-release-notes.mjs
49
+ const changesPattern =
50
+ /### (Major|Minor|Patch) Changes\s*\n\s*-\s+(?:([a-f0-9]+):\s+)?(.+?)$/s;
51
+
52
+ console.log('Testing release notes regex pattern\n');
53
+ console.log('='.repeat(60));
54
+
55
+ let passed = 0;
56
+ let failed = 0;
57
+
58
+ for (const testCase of testCases) {
59
+ console.log(`\nTest: ${testCase.name}`);
60
+ console.log('-'.repeat(40));
61
+
62
+ const match = testCase.body.match(changesPattern);
63
+
64
+ if (!match) {
65
+ console.log('❌ FAILED: No match found');
66
+ failed++;
67
+ continue;
68
+ }
69
+
70
+ const [, changeType, commitHash, rawDescription] = match;
71
+
72
+ let allPassed = true;
73
+
74
+ if (changeType !== testCase.expectedType) {
75
+ console.log(`❌ changeType: expected '${testCase.expectedType}', got '${changeType}'`);
76
+ allPassed = false;
77
+ } else {
78
+ console.log(`✅ changeType: ${changeType}`);
79
+ }
80
+
81
+ const expectedHash = testCase.expectedHash || null;
82
+ const actualHash = commitHash || null;
83
+ if (actualHash !== expectedHash) {
84
+ console.log(`❌ commitHash: expected '${expectedHash}', got '${actualHash}'`);
85
+ allPassed = false;
86
+ } else {
87
+ console.log(`✅ commitHash: ${actualHash}`);
88
+ }
89
+
90
+ if (rawDescription !== testCase.expectedDesc) {
91
+ console.log(`❌ description: expected '${testCase.expectedDesc}', got '${rawDescription}'`);
92
+ allPassed = false;
93
+ } else {
94
+ console.log(`✅ description: ${rawDescription.substring(0, 50)}...`);
95
+ }
96
+
97
+ if (allPassed) {
98
+ passed++;
99
+ console.log('✅ TEST PASSED');
100
+ } else {
101
+ failed++;
102
+ console.log('❌ TEST FAILED');
103
+ }
104
+ }
105
+
106
+ console.log('\n' + '='.repeat(60));
107
+ console.log(`\nResults: ${passed} passed, ${failed} failed`);
108
+
109
+ if (failed > 0) {
110
+ process.exit(1);
111
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-setup-git-identity",
3
- "version": "0.2.3",
3
+ "version": "0.3.1",
4
4
  "description": "A tool to setup git identity based on current gh user",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -86,28 +86,32 @@ try {
86
86
  process.exit(0);
87
87
  }
88
88
 
89
- // Extract the patch changes section
90
- // This regex handles two formats:
91
- // 1. With commit hash: "- abc1234: Description"
92
- // 2. Without commit hash: "- Description"
93
- const patchChangesMatchWithHash = currentBody.match(
94
- /### Patch Changes\s*\n\s*-\s+([a-f0-9]+):\s+(.+?)$/s
95
- );
96
- const patchChangesMatchNoHash = currentBody.match(
97
- /### Patch Changes\s*\n\s*-\s+(.+?)$/s
98
- );
89
+ // Extract the changes section (Major, Minor, or Patch)
90
+ // This regex handles formats:
91
+ // 1. With commit hash: "### Major Changes\n- abc1234: Description"
92
+ // 2. Without commit hash: "### Minor Changes\n- Description"
93
+ const changesPattern =
94
+ /### (Major|Minor|Patch) Changes\s*\n\s*-\s+(?:([a-f0-9]+):\s+)?(.+?)$/s;
95
+ const changesMatch = currentBody.match(changesPattern);
99
96
 
100
97
  let commitHash = null;
101
98
  let rawDescription = null;
99
+ let changeType = null;
100
+
101
+ if (changesMatch) {
102
+ [, changeType, commitHash, rawDescription] = changesMatch;
103
+ console.log(`Found ${changeType} Changes section`);
102
104
 
103
- if (patchChangesMatchWithHash) {
104
- // Format: - abc1234: Description
105
- [, commitHash, rawDescription] = patchChangesMatchWithHash;
106
- } else if (patchChangesMatchNoHash) {
107
- // Format: - Description (no commit hash)
108
- [, rawDescription] = patchChangesMatchNoHash;
105
+ // Extract commit hash if embedded in description (fallback)
106
+ if (!commitHash && rawDescription) {
107
+ const descWithHashMatch = rawDescription.match(/^([a-f0-9]+):\s+(.+)$/s);
108
+ if (descWithHashMatch) {
109
+ [, commitHash, rawDescription] = descWithHashMatch;
110
+ }
111
+ }
109
112
  } else {
110
- console.log('Could not parse patch changes from release notes');
113
+ console.log('Could not parse changes from release notes');
114
+ console.log(' Looking for pattern: ### [Major|Minor|Patch] Changes');
111
115
  process.exit(0);
112
116
  }
113
117
 
package/src/cli.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { makeConfig } from 'lino-arguments';
10
- import { setupGitIdentity, isGhAuthenticated } from './index.js';
10
+ import { setupGitIdentity, isGhAuthenticated, runGhAuthLogin } from './index.js';
11
11
 
12
12
  // Parse command-line arguments with environment variable and .lenv support
13
13
  const config = makeConfig({
@@ -67,14 +67,22 @@ async function main() {
67
67
 
68
68
  if (!authenticated) {
69
69
  console.log('');
70
- console.log('GitHub CLI is not authenticated.');
70
+ console.log('GitHub CLI is not authenticated. Starting authentication...');
71
71
  console.log('');
72
- console.log('Please run the following command to login:');
73
- console.log('');
74
- console.log(' printf "\\n" | gh auth login -s repo,workflow,user,read:org,gist --git-protocol https --web');
72
+
73
+ // Automatically run gh auth login
74
+ const loginSuccess = await runGhAuthLogin({ verbose: config.verbose });
75
+
76
+ if (!loginSuccess) {
77
+ console.log('');
78
+ console.log('Authentication failed. Please try running manually:');
79
+ console.log('');
80
+ console.log(' printf "y" | gh auth login -h github.com -s repo,workflow,user,read:org,gist --git-protocol https --web');
81
+ console.log('');
82
+ process.exit(1);
83
+ }
84
+
75
85
  console.log('');
76
- console.log('After logging in, run gh-setup-git-identity again.');
77
- process.exit(1);
78
86
  }
79
87
 
80
88
  // Prepare options
package/src/index.js CHANGED
@@ -41,11 +41,12 @@ function createDefaultLogger(options = {}) {
41
41
  *
42
42
  * @param {string} command - The command to execute
43
43
  * @param {string[]} args - The command arguments
44
+ * @param {Object} options - Spawn options
44
45
  * @returns {Promise<{stdout: string, stderr: string, exitCode: number}>}
45
46
  */
46
- function execCommand(command, args = []) {
47
+ function execCommand(command, args = [], options = {}) {
47
48
  return new Promise((resolve) => {
48
- const child = spawn(command, args, { stdio: 'pipe', shell: false });
49
+ const child = spawn(command, args, { stdio: 'pipe', shell: false, ...options });
49
50
 
50
51
  let stdout = '';
51
52
  let stderr = '';
@@ -76,6 +77,78 @@ function execCommand(command, args = []) {
76
77
  });
77
78
  }
78
79
 
80
+ /**
81
+ * Execute an interactive command (inheriting stdio)
82
+ *
83
+ * @param {string} command - The command to execute
84
+ * @param {string[]} args - The command arguments
85
+ * @param {Object} options - Options
86
+ * @param {string} options.input - Optional input to pipe to stdin
87
+ * @returns {Promise<{exitCode: number}>}
88
+ */
89
+ function execInteractiveCommand(command, args = [], options = {}) {
90
+ return new Promise((resolve) => {
91
+ const { input } = options;
92
+ const child = spawn(command, args, {
93
+ stdio: input ? ['pipe', 'inherit', 'inherit'] : 'inherit',
94
+ shell: false
95
+ });
96
+
97
+ if (input && child.stdin) {
98
+ child.stdin.write(input);
99
+ child.stdin.end();
100
+ }
101
+
102
+ child.on('close', (exitCode) => {
103
+ resolve({
104
+ exitCode: exitCode || 0
105
+ });
106
+ });
107
+
108
+ child.on('error', (error) => {
109
+ console.error(`Failed to execute command: ${error.message}`);
110
+ resolve({
111
+ exitCode: 1
112
+ });
113
+ });
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Run gh auth login interactively
119
+ *
120
+ * @param {Object} options - Options
121
+ * @param {boolean} options.verbose - Enable verbose logging
122
+ * @param {Object} options.logger - Custom logger
123
+ * @returns {Promise<boolean>} True if login was successful
124
+ */
125
+ export async function runGhAuthLogin(options = {}) {
126
+ const { verbose = false, logger = console } = options;
127
+ const log = createDefaultLogger({ verbose, logger });
128
+
129
+ log(() => 'Starting GitHub CLI authentication...');
130
+ log(() => '');
131
+
132
+ // Run gh auth login with the required scopes
133
+ // Use 'y' as input to confirm default account selection
134
+ const result = await execInteractiveCommand('gh', [
135
+ 'auth', 'login',
136
+ '-h', 'github.com',
137
+ '-s', 'repo,workflow,user,read:org,gist',
138
+ '--git-protocol', 'https',
139
+ '--web'
140
+ ], { input: 'y\n' });
141
+
142
+ if (result.exitCode !== 0) {
143
+ log.error(() => 'GitHub CLI authentication failed');
144
+ return false;
145
+ }
146
+
147
+ log(() => '');
148
+ log(() => 'GitHub CLI authentication successful!');
149
+ return true;
150
+ }
151
+
79
152
  /**
80
153
  * Check if GitHub CLI is authenticated
81
154
  *
@@ -302,6 +375,7 @@ export async function verifyGitIdentity(options = {}) {
302
375
 
303
376
  export default {
304
377
  isGhAuthenticated,
378
+ runGhAuthLogin,
305
379
  getGitHubUsername,
306
380
  getGitHubEmail,
307
381
  getGitHubUserInfo,
@@ -5,6 +5,7 @@
5
5
  import { test, assert } from 'test-anywhere';
6
6
  import {
7
7
  isGhAuthenticated,
8
+ runGhAuthLogin,
8
9
  getGitHubUsername,
9
10
  getGitHubEmail,
10
11
  getGitHubUserInfo,
@@ -83,11 +84,17 @@ test('getGitHubUserInfo - returns user info when authenticated', async () => {
83
84
  assert.ok(info.email.includes('@'));
84
85
  });
85
86
 
87
+ // Test: runGhAuthLogin function exists and is a function
88
+ test('runGhAuthLogin - is exported as a function', async () => {
89
+ assert.equal(typeof runGhAuthLogin, 'function');
90
+ });
91
+
86
92
  // Test: module exports
87
93
  test('module exports all expected functions', async () => {
88
94
  const module = await import('../src/index.js');
89
95
 
90
96
  assert.ok(typeof module.isGhAuthenticated === 'function');
97
+ assert.ok(typeof module.runGhAuthLogin === 'function');
91
98
  assert.ok(typeof module.getGitHubUsername === 'function');
92
99
  assert.ok(typeof module.getGitHubEmail === 'function');
93
100
  assert.ok(typeof module.getGitHubUserInfo === 'function');
@@ -104,6 +111,7 @@ test('default export contains all functions', async () => {
104
111
 
105
112
  assert.ok(typeof defaultExport === 'object');
106
113
  assert.ok(typeof defaultExport.isGhAuthenticated === 'function');
114
+ assert.ok(typeof defaultExport.runGhAuthLogin === 'function');
107
115
  assert.ok(typeof defaultExport.getGitHubUsername === 'function');
108
116
  assert.ok(typeof defaultExport.getGitHubEmail === 'function');
109
117
  assert.ok(typeof defaultExport.getGitHubUserInfo === 'function');