korekt-cli 0.7.0 → 0.8.2
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/package.json +1 -1
- package/src/git-logic.js +11 -7
- package/src/git-logic.test.js +10 -7
- package/src/index.js +102 -54
- package/src/index.test.js +72 -104
package/package.json
CHANGED
package/src/git-logic.js
CHANGED
|
@@ -238,10 +238,12 @@ export async function runUncommittedReview(mode = 'unstaged') {
|
|
|
238
238
|
*/
|
|
239
239
|
export async function getContributors(diffRange, repoRootPath) {
|
|
240
240
|
try {
|
|
241
|
-
// Get all commit authors with email and name
|
|
242
|
-
const { stdout: authorOutput } = await execa(
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
// Get all commit authors with email and name (exclude merge commits)
|
|
242
|
+
const { stdout: authorOutput } = await execa(
|
|
243
|
+
'git',
|
|
244
|
+
['log', '--no-merges', '--format=%ae|%an', diffRange],
|
|
245
|
+
{ cwd: repoRootPath }
|
|
246
|
+
);
|
|
245
247
|
|
|
246
248
|
if (!authorOutput.trim()) {
|
|
247
249
|
return { author_email: null, author_name: null, contributors: [] };
|
|
@@ -398,9 +400,11 @@ export async function runLocalReview(targetBranch = null, ignorePatterns = null)
|
|
|
398
400
|
console.error(chalk.gray(`Analyzing commits from ${mergeBase.substring(0, 7)} to HEAD...`));
|
|
399
401
|
|
|
400
402
|
// 3. Get Commit Messages with proper delimiter
|
|
401
|
-
const { stdout: logOutput } = await execa(
|
|
402
|
-
|
|
403
|
-
|
|
403
|
+
const { stdout: logOutput } = await execa(
|
|
404
|
+
'git',
|
|
405
|
+
['log', '--no-merges', '--pretty=%B---EOC---', diffRange],
|
|
406
|
+
{ cwd: repoRootPath }
|
|
407
|
+
);
|
|
404
408
|
const commitMessages = logOutput
|
|
405
409
|
.split('---EOC---')
|
|
406
410
|
.map((msg) => msg.trim())
|
package/src/git-logic.test.js
CHANGED
|
@@ -288,7 +288,10 @@ describe('runLocalReview - fork point detection', () => {
|
|
|
288
288
|
'abc123def456 feature-branch@{0}: commit: latest\nfedcba654321 feature-branch@{1}: commit: middle\n510572bc5197788770004d0d0585822adab0128f feature-branch@{2}: branch: Created from master',
|
|
289
289
|
};
|
|
290
290
|
}
|
|
291
|
-
if (
|
|
291
|
+
if (
|
|
292
|
+
command.includes('log --no-merges --pretty=%B---EOC---') &&
|
|
293
|
+
command.includes('510572bc')
|
|
294
|
+
) {
|
|
292
295
|
return { stdout: 'feat: add feature---EOC---' };
|
|
293
296
|
}
|
|
294
297
|
if (command.includes('diff --name-status') && command.includes('510572bc')) {
|
|
@@ -338,7 +341,7 @@ describe('runLocalReview - fork point detection', () => {
|
|
|
338
341
|
if (command.includes('merge-base origin/main HEAD')) {
|
|
339
342
|
return { stdout: 'abc123' };
|
|
340
343
|
}
|
|
341
|
-
if (command.includes('log --pretty=%B---EOC---')) {
|
|
344
|
+
if (command.includes('log --no-merges --pretty=%B---EOC---')) {
|
|
342
345
|
return { stdout: 'feat: add feature---EOC---' };
|
|
343
346
|
}
|
|
344
347
|
if (command.includes('diff --name-status')) {
|
|
@@ -542,7 +545,7 @@ describe('getContributors', () => {
|
|
|
542
545
|
it('should extract single contributor with commit count', async () => {
|
|
543
546
|
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
544
547
|
const command = [cmd, ...args].join(' ');
|
|
545
|
-
if (command.includes('log --format=%ae|%an')) {
|
|
548
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
546
549
|
return {
|
|
547
550
|
stdout: 'john@example.com|John Doe\njohn@example.com|John Doe\njohn@example.com|John Doe',
|
|
548
551
|
};
|
|
@@ -565,7 +568,7 @@ describe('getContributors', () => {
|
|
|
565
568
|
it('should identify author as contributor with most commits', async () => {
|
|
566
569
|
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
567
570
|
const command = [cmd, ...args].join(' ');
|
|
568
|
-
if (command.includes('log --format=%ae|%an')) {
|
|
571
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
569
572
|
return {
|
|
570
573
|
stdout: [
|
|
571
574
|
'alice@example.com|Alice Smith',
|
|
@@ -595,7 +598,7 @@ describe('getContributors', () => {
|
|
|
595
598
|
it('should handle empty commit range', async () => {
|
|
596
599
|
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
597
600
|
const command = [cmd, ...args].join(' ');
|
|
598
|
-
if (command.includes('log --format=%ae|%an')) {
|
|
601
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
599
602
|
return { stdout: '' };
|
|
600
603
|
}
|
|
601
604
|
throw new Error(`Unmocked command: ${command}`);
|
|
@@ -623,7 +626,7 @@ describe('getContributors', () => {
|
|
|
623
626
|
it('should handle missing name in git log', async () => {
|
|
624
627
|
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
625
628
|
const command = [cmd, ...args].join(' ');
|
|
626
|
-
if (command.includes('log --format=%ae|%an')) {
|
|
629
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
627
630
|
return { stdout: 'user@example.com|' };
|
|
628
631
|
}
|
|
629
632
|
throw new Error(`Unmocked command: ${command}`);
|
|
@@ -639,7 +642,7 @@ describe('getContributors', () => {
|
|
|
639
642
|
it('should sort contributors by commit count descending', async () => {
|
|
640
643
|
vi.mocked(execa).mockImplementation(async (cmd, args) => {
|
|
641
644
|
const command = [cmd, ...args].join(' ');
|
|
642
|
-
if (command.includes('log --format=%ae|%an')) {
|
|
645
|
+
if (command.includes('log --no-merges --format=%ae|%an')) {
|
|
643
646
|
return {
|
|
644
647
|
stdout: [
|
|
645
648
|
'c@example.com|C User',
|
package/src/index.js
CHANGED
|
@@ -6,9 +6,11 @@ import chalk from 'chalk';
|
|
|
6
6
|
import readline from 'readline';
|
|
7
7
|
import ora from 'ora';
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
|
-
import {
|
|
10
|
-
import { join, dirname } from 'path';
|
|
9
|
+
import { writeFileSync, mkdtempSync, rmSync } from 'fs';
|
|
10
|
+
import { join, dirname, resolve } from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
|
+
import { spawn } from 'child_process';
|
|
13
|
+
import { tmpdir } from 'os';
|
|
12
14
|
import { runLocalReview } from './git-logic.js';
|
|
13
15
|
import { getApiKey, setApiKey, getApiEndpoint, setApiEndpoint } from './config.js';
|
|
14
16
|
import { formatReviewOutput } from './formatter.js';
|
|
@@ -71,11 +73,76 @@ async function confirmAction(message) {
|
|
|
71
73
|
output: process.stdout,
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
return new Promise((
|
|
76
|
+
return new Promise((resolvePromise) => {
|
|
75
77
|
rl.question(message, (answer) => {
|
|
76
78
|
rl.close();
|
|
77
79
|
// Default to 'yes' if the user just presses Enter
|
|
78
|
-
|
|
80
|
+
resolvePromise(
|
|
81
|
+
answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === ''
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Detect CI provider from environment variables
|
|
89
|
+
* @returns {string|null} Provider name or null if not detected
|
|
90
|
+
*/
|
|
91
|
+
export function detectCIProvider() {
|
|
92
|
+
if (process.env.GITHUB_TOKEN && process.env.GITHUB_REPOSITORY) {
|
|
93
|
+
return 'github';
|
|
94
|
+
}
|
|
95
|
+
if (process.env.SYSTEM_ACCESSTOKEN && process.env.SYSTEM_PULLREQUEST_PULLREQUESTID) {
|
|
96
|
+
return 'azure';
|
|
97
|
+
}
|
|
98
|
+
if (process.env.BITBUCKET_REPO_SLUG && process.env.BITBUCKET_PR_ID) {
|
|
99
|
+
return 'bitbucket';
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Run the CI integration script to post comments
|
|
106
|
+
* @param {string} provider - CI provider (github, azure, bitbucket)
|
|
107
|
+
* @param {Object} results - Review results from API
|
|
108
|
+
* @returns {Promise<void>}
|
|
109
|
+
*/
|
|
110
|
+
async function runCIScript(provider, results) {
|
|
111
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
112
|
+
const __dirname = dirname(__filename);
|
|
113
|
+
const scriptPath = resolve(__dirname, '..', 'scripts', `${provider}.sh`);
|
|
114
|
+
|
|
115
|
+
// Create secure temp directory and write results
|
|
116
|
+
const tempDir = mkdtempSync(join(tmpdir(), 'korekt-'));
|
|
117
|
+
const tempFile = join(tempDir, 'results.json');
|
|
118
|
+
writeFileSync(tempFile, JSON.stringify(results, null, 2));
|
|
119
|
+
|
|
120
|
+
return new Promise((resolvePromise, reject) => {
|
|
121
|
+
const cleanup = () => {
|
|
122
|
+
try {
|
|
123
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
log(chalk.yellow(`Warning: Failed to clean up temp directory: ${err.message}`));
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const child = spawn('bash', [scriptPath, tempFile], {
|
|
130
|
+
stdio: 'inherit',
|
|
131
|
+
env: process.env,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
child.on('close', (code) => {
|
|
135
|
+
cleanup();
|
|
136
|
+
if (code === 0) {
|
|
137
|
+
resolvePromise();
|
|
138
|
+
} else {
|
|
139
|
+
reject(new Error(`CI script exited with code ${code}`));
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
child.on('error', (err) => {
|
|
144
|
+
cleanup();
|
|
145
|
+
reject(err);
|
|
79
146
|
});
|
|
80
147
|
});
|
|
81
148
|
}
|
|
@@ -93,19 +160,16 @@ Examples:
|
|
|
93
160
|
$ kk stg --dry-run Preview staged changes review
|
|
94
161
|
$ kk diff Review unstaged changes
|
|
95
162
|
$ kk review main --json Output raw JSON (for CI/CD integration)
|
|
163
|
+
$ kk review main --comment Review and post comments to PR (CI/CD)
|
|
96
164
|
|
|
97
165
|
Common Options:
|
|
98
166
|
--dry-run Show payload without sending to API
|
|
99
167
|
--json Output raw API response as JSON
|
|
168
|
+
--comment Post review results as PR comments
|
|
100
169
|
|
|
101
170
|
Configuration:
|
|
102
171
|
$ kk config --key YOUR_KEY
|
|
103
172
|
$ kk config --endpoint https://api.korekt.ai/api/review
|
|
104
|
-
|
|
105
|
-
CI/CD Integration:
|
|
106
|
-
$ kk get-script github Output GitHub Actions integration script
|
|
107
|
-
$ kk get-script bitbucket Output Bitbucket Pipelines integration script
|
|
108
|
-
$ kk get-script azure Output Azure DevOps integration script
|
|
109
173
|
`
|
|
110
174
|
);
|
|
111
175
|
|
|
@@ -122,6 +186,7 @@ program
|
|
|
122
186
|
'Ignore files matching these patterns (e.g., "*.lock" "dist/*")'
|
|
123
187
|
)
|
|
124
188
|
.option('--json', 'Output raw API response as JSON')
|
|
189
|
+
.option('--comment', 'Post review results as PR comments (auto-detects CI provider)')
|
|
125
190
|
.action(async (targetBranch, options) => {
|
|
126
191
|
const reviewTarget = targetBranch ? `against '${targetBranch}'` : '(auto-detecting fork point)';
|
|
127
192
|
|
|
@@ -166,8 +231,8 @@ program
|
|
|
166
231
|
return;
|
|
167
232
|
}
|
|
168
233
|
|
|
169
|
-
// Show summary and ask for confirmation (auto-confirm in JSON mode)
|
|
170
|
-
if (!options.json) {
|
|
234
|
+
// Show summary and ask for confirmation (auto-confirm in JSON/comment mode)
|
|
235
|
+
if (!options.json && !options.comment) {
|
|
171
236
|
log(chalk.yellow('\n📋 Ready to submit for review:\n'));
|
|
172
237
|
log(` Branch: ${chalk.cyan(payload.source_branch)}`);
|
|
173
238
|
log(` Commits: ${chalk.cyan(payload.commit_messages.length)}`);
|
|
@@ -216,6 +281,32 @@ program
|
|
|
216
281
|
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
217
282
|
spinner.succeed(`Review completed in ${elapsed}s!`);
|
|
218
283
|
|
|
284
|
+
// Handle --comment flag: post results to PR
|
|
285
|
+
if (options.comment) {
|
|
286
|
+
const provider = detectCIProvider();
|
|
287
|
+
if (!provider) {
|
|
288
|
+
log(
|
|
289
|
+
chalk.red(
|
|
290
|
+
'Could not detect CI provider. Make sure required environment variables are set:'
|
|
291
|
+
)
|
|
292
|
+
);
|
|
293
|
+
log(chalk.gray(' GitHub: GITHUB_TOKEN, GITHUB_REPOSITORY, PR_NUMBER, COMMIT_HASH'));
|
|
294
|
+
log(chalk.gray(' Azure: SYSTEM_ACCESSTOKEN, SYSTEM_PULLREQUEST_PULLREQUESTID'));
|
|
295
|
+
log(chalk.gray(' Bitbucket: BITBUCKET_REPO_SLUG, BITBUCKET_PR_ID'));
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
log(chalk.blue(`Posting review comments to ${provider}...`));
|
|
300
|
+
try {
|
|
301
|
+
await runCIScript(provider, response.data);
|
|
302
|
+
log(chalk.green('Successfully posted review comments!'));
|
|
303
|
+
} catch (err) {
|
|
304
|
+
log(chalk.red(`Failed to post comments: ${err.message}`));
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
219
310
|
// Output results to stdout
|
|
220
311
|
if (options.json) {
|
|
221
312
|
output(JSON.stringify(response.data, null, 2));
|
|
@@ -424,49 +515,6 @@ program
|
|
|
424
515
|
}
|
|
425
516
|
});
|
|
426
517
|
|
|
427
|
-
program
|
|
428
|
-
.command('get-script <provider>')
|
|
429
|
-
.description('Output a CI/CD integration script for a specific provider')
|
|
430
|
-
.addHelpText(
|
|
431
|
-
'after',
|
|
432
|
-
`
|
|
433
|
-
Providers:
|
|
434
|
-
github GitHub Actions integration script
|
|
435
|
-
bitbucket Bitbucket Pipelines integration script
|
|
436
|
-
azure Azure DevOps integration script
|
|
437
|
-
|
|
438
|
-
Usage:
|
|
439
|
-
kk get-script github | bash -s results.json
|
|
440
|
-
kk get-script bitbucket > bitbucket.sh && chmod +x bitbucket.sh
|
|
441
|
-
kk get-script azure > azure.sh
|
|
442
|
-
`
|
|
443
|
-
)
|
|
444
|
-
.action((provider) => {
|
|
445
|
-
const validProviders = ['github', 'bitbucket', 'azure'];
|
|
446
|
-
|
|
447
|
-
if (!validProviders.includes(provider.toLowerCase())) {
|
|
448
|
-
console.error(chalk.red(`Invalid provider: ${provider}`));
|
|
449
|
-
console.error(chalk.gray(`Valid providers: ${validProviders.join(', ')}`));
|
|
450
|
-
process.exit(1);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
try {
|
|
454
|
-
// Get the directory where this script is located
|
|
455
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
456
|
-
const __dirname = dirname(__filename);
|
|
457
|
-
|
|
458
|
-
// Build path to the script file
|
|
459
|
-
const scriptPath = join(__dirname, '..', 'scripts', `${provider.toLowerCase()}.sh`);
|
|
460
|
-
|
|
461
|
-
// Read and output the script
|
|
462
|
-
const scriptContent = readFileSync(scriptPath, 'utf8');
|
|
463
|
-
output(scriptContent);
|
|
464
|
-
} catch (error) {
|
|
465
|
-
console.error(chalk.red(`Failed to read script: ${error.message}`));
|
|
466
|
-
process.exit(1);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
|
|
470
518
|
// Only parse arguments if this file is being run directly (not imported)
|
|
471
519
|
// In tests, we set NODE_ENV to 'test' via vitest
|
|
472
520
|
if (process.env.NODE_ENV !== 'test') {
|
package/src/index.test.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { truncateFileData, formatErrorOutput } from './index.js';
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { join, dirname } from 'path';
|
|
5
|
-
import { fileURLToPath } from 'url';
|
|
2
|
+
import { truncateFileData, formatErrorOutput, detectCIProvider } from './index.js';
|
|
6
3
|
|
|
7
4
|
describe('CLI JSON output mode', () => {
|
|
8
5
|
let stdoutSpy;
|
|
@@ -336,137 +333,108 @@ describe('CLI JSON output mode', () => {
|
|
|
336
333
|
});
|
|
337
334
|
});
|
|
338
335
|
|
|
339
|
-
describe('
|
|
340
|
-
|
|
336
|
+
describe('detectCIProvider', () => {
|
|
337
|
+
const originalEnv = process.env;
|
|
341
338
|
|
|
342
339
|
beforeEach(() => {
|
|
343
|
-
|
|
340
|
+
// Reset environment before each test
|
|
341
|
+
process.env = { ...originalEnv };
|
|
342
|
+
// Clear all CI-related env vars
|
|
343
|
+
delete process.env.GITHUB_TOKEN;
|
|
344
|
+
delete process.env.GITHUB_REPOSITORY;
|
|
345
|
+
delete process.env.SYSTEM_ACCESSTOKEN;
|
|
346
|
+
delete process.env.SYSTEM_PULLREQUEST_PULLREQUESTID;
|
|
347
|
+
delete process.env.BITBUCKET_REPO_SLUG;
|
|
348
|
+
delete process.env.BITBUCKET_PR_ID;
|
|
344
349
|
});
|
|
345
350
|
|
|
346
351
|
afterEach(() => {
|
|
347
|
-
|
|
352
|
+
process.env = originalEnv;
|
|
348
353
|
});
|
|
349
354
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
// Read actual github.sh script
|
|
355
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
356
|
-
const __dirname = dirname(__filename);
|
|
357
|
-
const scriptPath = join(__dirname, '..', 'scripts', 'github.sh');
|
|
358
|
-
const scriptContent = readFileSync(scriptPath, 'utf8');
|
|
359
|
-
|
|
360
|
-
// Simulate get-script command output
|
|
361
|
-
output(scriptContent);
|
|
362
|
-
|
|
363
|
-
// Verify script was output to stdout
|
|
364
|
-
expect(stdoutSpy).toHaveBeenCalledWith(scriptContent + '\n');
|
|
365
|
-
expect(stdoutSpy).toHaveBeenCalledTimes(1);
|
|
366
|
-
});
|
|
355
|
+
it('should detect GitHub when GITHUB_TOKEN and GITHUB_REPOSITORY are set', () => {
|
|
356
|
+
process.env.GITHUB_TOKEN = 'ghp_test123';
|
|
357
|
+
process.env.GITHUB_REPOSITORY = 'owner/repo';
|
|
367
358
|
|
|
368
|
-
|
|
369
|
-
|
|
359
|
+
expect(detectCIProvider()).toBe('github');
|
|
360
|
+
});
|
|
370
361
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const scriptPath = join(__dirname, '..', 'scripts', 'bitbucket.sh');
|
|
375
|
-
const scriptContent = readFileSync(scriptPath, 'utf8');
|
|
362
|
+
it('should detect Azure when SYSTEM_ACCESSTOKEN and SYSTEM_PULLREQUEST_PULLREQUESTID are set', () => {
|
|
363
|
+
process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
|
|
364
|
+
process.env.SYSTEM_PULLREQUEST_PULLREQUESTID = '123';
|
|
376
365
|
|
|
377
|
-
|
|
378
|
-
|
|
366
|
+
expect(detectCIProvider()).toBe('azure');
|
|
367
|
+
});
|
|
379
368
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
});
|
|
369
|
+
it('should detect Bitbucket when BITBUCKET_REPO_SLUG and BITBUCKET_PR_ID are set', () => {
|
|
370
|
+
process.env.BITBUCKET_REPO_SLUG = 'my-repo';
|
|
371
|
+
process.env.BITBUCKET_PR_ID = '456';
|
|
384
372
|
|
|
385
|
-
|
|
386
|
-
|
|
373
|
+
expect(detectCIProvider()).toBe('bitbucket');
|
|
374
|
+
});
|
|
387
375
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const scriptPath = join(__dirname, '..', 'scripts', 'azure.sh');
|
|
392
|
-
const scriptContent = readFileSync(scriptPath, 'utf8');
|
|
376
|
+
it('should return null when no CI provider env vars are set', () => {
|
|
377
|
+
expect(detectCIProvider()).toBe(null);
|
|
378
|
+
});
|
|
393
379
|
|
|
394
|
-
|
|
395
|
-
|
|
380
|
+
it('should return null when only partial GitHub env vars are set', () => {
|
|
381
|
+
process.env.GITHUB_TOKEN = 'ghp_test123';
|
|
382
|
+
// GITHUB_REPOSITORY not set
|
|
396
383
|
|
|
397
|
-
|
|
398
|
-
expect(stdoutSpy).toHaveBeenCalledWith(scriptContent + '\n');
|
|
399
|
-
expect(stdoutSpy).toHaveBeenCalledTimes(1);
|
|
400
|
-
});
|
|
384
|
+
expect(detectCIProvider()).toBe(null);
|
|
401
385
|
});
|
|
402
386
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const __dirname = dirname(__filename);
|
|
387
|
+
it('should return null when only partial Azure env vars are set', () => {
|
|
388
|
+
process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
|
|
389
|
+
// SYSTEM_PULLREQUEST_PULLREQUESTID not set
|
|
407
390
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
providers.forEach((provider) => {
|
|
412
|
-
const scriptPath = join(__dirname, '..', 'scripts', `${provider}.sh`);
|
|
413
|
-
const scriptContent = readFileSync(scriptPath, 'utf8');
|
|
391
|
+
expect(detectCIProvider()).toBe(null);
|
|
392
|
+
});
|
|
414
393
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
394
|
+
it('should return null when only partial Bitbucket env vars are set', () => {
|
|
395
|
+
process.env.BITBUCKET_REPO_SLUG = 'my-repo';
|
|
396
|
+
// BITBUCKET_PR_ID not set
|
|
418
397
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
expect(lineCount).toBeGreaterThan(400);
|
|
398
|
+
expect(detectCIProvider()).toBe(null);
|
|
399
|
+
});
|
|
422
400
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
401
|
+
it('should prioritize GitHub over other providers when multiple are set', () => {
|
|
402
|
+
// Set all providers
|
|
403
|
+
process.env.GITHUB_TOKEN = 'ghp_test123';
|
|
404
|
+
process.env.GITHUB_REPOSITORY = 'owner/repo';
|
|
405
|
+
process.env.SYSTEM_ACCESSTOKEN = 'azure_token';
|
|
406
|
+
process.env.SYSTEM_PULLREQUEST_PULLREQUESTID = '123';
|
|
407
|
+
process.env.BITBUCKET_REPO_SLUG = 'my-repo';
|
|
408
|
+
process.env.BITBUCKET_PR_ID = '456';
|
|
409
|
+
|
|
410
|
+
// GitHub should be detected first due to check order
|
|
411
|
+
expect(detectCIProvider()).toBe('github');
|
|
427
412
|
});
|
|
413
|
+
});
|
|
428
414
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const validProviders = ['github', 'bitbucket', 'azure'];
|
|
415
|
+
describe('--comment flag behavior', () => {
|
|
416
|
+
it('should skip confirmation when --comment is set', () => {
|
|
417
|
+
const options = { comment: true };
|
|
433
418
|
|
|
434
|
-
|
|
435
|
-
|
|
419
|
+
// Logic from index.js: if (!options.json && !options.comment) { confirmAction... }
|
|
420
|
+
const shouldShowConfirmation = !options.json && !options.comment;
|
|
436
421
|
|
|
437
|
-
|
|
422
|
+
expect(shouldShowConfirmation).toBe(false);
|
|
423
|
+
});
|
|
438
424
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
expect(['github', 'bitbucket', 'azure']).not.toContain(invalidProvider);
|
|
442
|
-
});
|
|
425
|
+
it('should skip confirmation when both --json and --comment are set', () => {
|
|
426
|
+
const options = { json: true, comment: true };
|
|
443
427
|
|
|
444
|
-
|
|
445
|
-
const validProviders = ['github', 'bitbucket', 'azure'];
|
|
428
|
+
const shouldShowConfirmation = !options.json && !options.comment;
|
|
446
429
|
|
|
447
|
-
|
|
448
|
-
expect(validProviders.includes('GitHub'.toLowerCase())).toBe(true);
|
|
449
|
-
expect(validProviders.includes('BITBUCKET'.toLowerCase())).toBe(true);
|
|
450
|
-
expect(validProviders.includes('Azure'.toLowerCase())).toBe(true);
|
|
451
|
-
});
|
|
430
|
+
expect(shouldShowConfirmation).toBe(false);
|
|
452
431
|
});
|
|
453
432
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
457
|
-
const __dirname = dirname(__filename);
|
|
433
|
+
it('should show confirmation when neither --json nor --comment is set', () => {
|
|
434
|
+
const options = {};
|
|
458
435
|
|
|
459
|
-
|
|
436
|
+
const shouldShowConfirmation = !options.json && !options.comment;
|
|
460
437
|
|
|
461
|
-
|
|
462
|
-
const scriptPath = join(__dirname, '..', 'scripts', `${provider}.sh`);
|
|
463
|
-
|
|
464
|
-
// Should not throw - file exists and is readable
|
|
465
|
-
expect(() => {
|
|
466
|
-
const content = readFileSync(scriptPath, 'utf8');
|
|
467
|
-
expect(content.length).toBeGreaterThan(0);
|
|
468
|
-
}).not.toThrow();
|
|
469
|
-
});
|
|
470
|
-
});
|
|
438
|
+
expect(shouldShowConfirmation).toBe(true);
|
|
471
439
|
});
|
|
472
440
|
});
|