Skip to content

Commit 5f1fb9d

Browse files
PR feedback
1 parent 7bbb51c commit 5f1fb9d

File tree

5 files changed

+61
-10
lines changed

5 files changed

+61
-10
lines changed

src/bootstrap/repair.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ export async function runRepair(
3535
healthResult: HealthCheckResult,
3636
_projectKey?: string,
3737
organization?: string,
38-
): Promise<void> {
39-
let token = '';
38+
): Promise<string | undefined> {
39+
let newToken: string | undefined;
4040

4141
// Fix token if invalid
4242
if (!healthResult.tokenValid) {
@@ -50,21 +50,23 @@ export async function runRepair(
5050
}
5151

5252
// Generate new token
53-
token = await generateTokenViaBrowser(serverURL);
53+
newToken = await generateTokenViaBrowser(serverURL);
5454

5555
// Validate new token
56-
const valid = await validateToken(serverURL, token);
56+
const valid = await validateToken(serverURL, newToken);
5757
if (!valid) {
5858
throw new Error('Generated token is invalid');
5959
}
6060

6161
// Save to keychain
62-
await saveToken(serverURL, token, organization);
62+
await saveToken(serverURL, newToken, organization);
6363
success('Token saved to keychain');
6464
}
6565

6666
// Install sonar-secrets hooks for secret scanning
6767
text('Installing secret scanning hooks...');
6868
await installSecretScanningHooks(projectRoot);
6969
success('Secret scanning hooks installed');
70+
71+
return newToken;
7072
}

src/commands/integrate.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { getAllCredentials } from '../lib/keychain.js';
2929
import { installSecretScanningHooks } from '../bootstrap/hooks.js';
3030
import { loadState, saveState, markAgentConfigured, addInstalledHook } from '../lib/state-manager.js';
3131
import { runCommand } from '../lib/run-command.js';
32-
import { version as VERSION } from '../../package.json';
32+
import { VERSION } from '../version.js';
3333
import logger from '../lib/logger.js';
3434
import { SONARCLOUD_URL, SONARCLOUD_HOSTNAME } from '../lib/config-constants.js';
3535
import { ENV_TOKEN, ENV_SERVER } from '../lib/auth-resolver.js';
@@ -255,6 +255,7 @@ async function runHealthCheckAndRepair(
255255
token: string | undefined,
256256
organization: string | undefined,
257257
skipHooks: boolean | undefined,
258+
nonInteractive?: boolean,
258259
): Promise<string | undefined> {
259260
text('\nPhase 2/3: Health Check & Repair');
260261
blank();
@@ -279,18 +280,26 @@ async function runHealthCheckAndRepair(
279280
text(` - ${msg}`);
280281
}
281282

283+
if (nonInteractive && !healthResult.tokenValid) {
284+
// Can't repair token without browser interaction — install hooks and continue
285+
if (!skipHooks) {
286+
await installSecretScanningHooks(projectInfo.root);
287+
}
288+
return token;
289+
}
290+
282291
// Repair (part of Phase 2)
283292
text('\n Running repair...');
284293

285-
await runRepair(
294+
const repairedToken = await runRepair(
286295
serverURL,
287296
projectInfo.root,
288297
healthResult,
289298
projectKey,
290299
organization,
291300
);
292301

293-
return token;
302+
return repairedToken ?? token;
294303
}
295304

296305
/**
@@ -403,6 +412,8 @@ export async function integrateCommand(agent: string, options: OnboardAgentOptio
403412
if (!config.projectKey) {
404413
text('\nNo project key configured.');
405414
text('Installing secret scanning hooks only.');
415+
text('\nPhase 2/3: Health Check & Repair — skipped (no project key)');
416+
text('Phase 3/3: Final Verification — skipped (no project key)');
406417
if (!options.skipHooks) {
407418
await installSecretScanningHooks(projectInfo.root);
408419
await updateStateAfterConfiguration(true);
@@ -417,6 +428,11 @@ export async function integrateCommand(agent: string, options: OnboardAgentOptio
417428
// Ensure token is available
418429
let token = await ensureToken(config.token, serverURL, config.organization);
419430

431+
// When both env vars are set the caller is in a CI/automated context — treat as
432+
// non-interactive so that a bad token never triggers browser-based repair.
433+
const envBasedAuth = !!(process.env[ENV_TOKEN] && process.env[ENV_SERVER]);
434+
const effectiveNonInteractive = options.nonInteractive || envBasedAuth;
435+
420436
// Phase 2 & 3: Health Check and Repair
421437
if (token) {
422438
token = await runHealthCheckAndRepair(
@@ -426,6 +442,7 @@ export async function integrateCommand(agent: string, options: OnboardAgentOptio
426442
token,
427443
config.organization,
428444
options.skipHooks,
445+
effectiveNonInteractive,
429446
);
430447

431448
if (token) {
@@ -445,6 +462,16 @@ export async function integrateCommand(agent: string, options: OnboardAgentOptio
445462

446463
// If no token, run repair to generate one
447464
if (!token) {
465+
if (effectiveNonInteractive) {
466+
// Can't generate token without browser — install hooks only
467+
if (!options.skipHooks) {
468+
await installSecretScanningHooks(projectInfo.root);
469+
}
470+
await updateStateAfterConfiguration(!options.skipHooks);
471+
outro('Setup complete!', 'success');
472+
return;
473+
}
474+
448475
token = await runRepairWithoutToken(
449476
serverURL,
450477
projectKey,

src/commands/secret-scan.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ function handleScanFailure(
325325
exitCode: number
326326
): void {
327327
blank();
328-
error('Scan failed');
328+
error('Scan found secrets');
329329
logger.error(`Scan failed with exit code: ${exitCode}`);
330330
text(` Exit code: ${exitCode}`);
331331
text(` Duration: ${scanDurationMs}ms`);

src/version.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SonarQube CLI
3+
* Copyright (C) 2026 SonarSource Sàrl
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
// Auto-generated from package.json — do not edit manually
22+
export const VERSION = '0.4.2';

tests/unit/secret-scan.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ describe('secretCheckCommand: scan failures', () => {
251251

252252
expect(mockExit).toHaveBeenCalledWith(51);
253253
const errors = getMockUiCalls().filter(c => c.method === 'error').map(c => String(c.args[0]));
254-
expect(errors.some(m => m.includes('Scan failed'))).toBe(true);
254+
expect(errors.some(m => m.includes('Scan found secrets'))).toBe(true);
255255
});
256256

257257
it('exits 1 when binary exits 1 (error, not secrets found)', async () => {

0 commit comments

Comments
 (0)