sonar-sweep 0.1.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.
Files changed (47) hide show
  1. package/README.md +70 -0
  2. package/dist/cli/commands/issue-accept.d.ts +13 -0
  3. package/dist/cli/commands/issue-accept.js +49 -0
  4. package/dist/cli/commands/issue-accept.js.map +1 -0
  5. package/dist/cli/commands/pr-coverage.d.ts +16 -0
  6. package/dist/cli/commands/pr-coverage.js +79 -0
  7. package/dist/cli/commands/pr-coverage.js.map +1 -0
  8. package/dist/cli/commands/pr-issues.d.ts +15 -0
  9. package/dist/cli/commands/pr-issues.js +78 -0
  10. package/dist/cli/commands/pr-issues.js.map +1 -0
  11. package/dist/cli/commands/pr-report.d.ts +13 -0
  12. package/dist/cli/commands/pr-report.js +94 -0
  13. package/dist/cli/commands/pr-report.js.map +1 -0
  14. package/dist/cli/commands/pr-review.d.ts +16 -0
  15. package/dist/cli/commands/pr-review.js +93 -0
  16. package/dist/cli/commands/pr-review.js.map +1 -0
  17. package/dist/cli/index.d.ts +1 -0
  18. package/dist/cli/index.js +28 -0
  19. package/dist/cli/index.js.map +1 -0
  20. package/dist/cli/project-key.d.ts +1 -0
  21. package/dist/cli/project-key.js +32 -0
  22. package/dist/cli/project-key.js.map +1 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +8 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/index.js +7 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/service/issue-transition.d.ts +12 -0
  30. package/dist/service/issue-transition.js +20 -0
  31. package/dist/service/issue-transition.js.map +1 -0
  32. package/dist/service/pr-coverage.d.ts +22 -0
  33. package/dist/service/pr-coverage.js +48 -0
  34. package/dist/service/pr-coverage.js.map +1 -0
  35. package/dist/service/pr-issues.d.ts +29 -0
  36. package/dist/service/pr-issues.js +45 -0
  37. package/dist/service/pr-issues.js.map +1 -0
  38. package/dist/service/pr-report.d.ts +27 -0
  39. package/dist/service/pr-report.js +59 -0
  40. package/dist/service/pr-report.js.map +1 -0
  41. package/dist/service/pr-review.d.ts +39 -0
  42. package/dist/service/pr-review.js +81 -0
  43. package/dist/service/pr-review.js.map +1 -0
  44. package/dist/service/sonarcloud-client.d.ts +100 -0
  45. package/dist/service/sonarcloud-client.js +112 -0
  46. package/dist/service/sonarcloud-client.js.map +1 -0
  47. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # sonar-sweep-cli
2
+
3
+ TypeScript CLI to fetch SonarQube Cloud pull request details for coding-agent workflows.
4
+
5
+ ## What it does
6
+
7
+ - Fetches Quality Gate status for a pull request
8
+ - Fetches new issue counts and accepted issue counts
9
+ - Fetches open issue details for new code (rule, severity, file, line, message)
10
+ - Fetches per-file new-code coverage gaps for a pull request
11
+ - Fetches issue review data with source snippets for faster triage
12
+ - Applies issue transitions (for example mark issue as accepted)
13
+ - Fetches key new-code measures (security hotspots, coverage, duplication)
14
+ - Prints either human-readable output or JSON (`--json`) for automation
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install
20
+ npm run build
21
+ ```
22
+
23
+ ## Authentication
24
+
25
+ The CLI uses SonarQube Cloud tokens via bearer auth.
26
+
27
+ You can pass the token explicitly:
28
+
29
+ ```bash
30
+ sonar-sweep pr-report <projectKey> <pullRequest> --token <token>
31
+ ```
32
+
33
+ Or set environment variables:
34
+
35
+ ```bash
36
+ SONAR_TOKEN=...
37
+ SONAR_BASE_URL=https://sonarcloud.io
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ sonar-sweep pr-report <pullRequest>
44
+ sonar-sweep pr-issues <pullRequest>
45
+ sonar-sweep pr-coverage <pullRequest>
46
+ sonar-sweep pr-review <pullRequest>
47
+ sonar-sweep issue-accept <issueKey>
48
+ ```
49
+
50
+ For `pr-*` commands, the CLI auto-detects `sonar.projectKey` from `sonar-project.properties` in the current git root. You can still override with `--projectKey`.
51
+
52
+ JSON output for agents:
53
+
54
+ ```bash
55
+ sonar-sweep pr-report <pullRequest> --json
56
+ sonar-sweep pr-issues <pullRequest> --json
57
+ sonar-sweep pr-coverage <pullRequest> --json
58
+ sonar-sweep pr-review <pullRequest> --json
59
+ sonar-sweep issue-accept <issueKey> --comment "Accepted with rationale" --json
60
+ ```
61
+
62
+ ## Example
63
+
64
+ ```bash
65
+ node dist/cli.js pr-report 2221 --projectKey sueddeutsche_app-android --json
66
+ node dist/cli.js pr-issues 2220 --projectKey sueddeutsche_app-android --json
67
+ node dist/cli.js pr-coverage 591 --projectKey sueddeutsche_szhp-pages --threshold 80 --json
68
+ node dist/cli.js pr-review 145 --projectKey sueddeutsche_szde-redirect-admin --context 4 --json
69
+ node dist/cli.js issue-accept AZx2EKMtvB1gqnjpRLA4 --comment "Reviewed and accepted" --json
70
+ ```
@@ -0,0 +1,13 @@
1
+ import type { Argv } from 'yargs';
2
+ type IssueAcceptArgs = {
3
+ token: string;
4
+ baseUrl: string;
5
+ issueKey: string;
6
+ comment?: string;
7
+ json: boolean;
8
+ };
9
+ export declare const command = "issue-accept <issueKey>";
10
+ export declare const describe = "Mark a Sonar issue as accepted (transition=accept)";
11
+ export declare function builder(yargs: Argv): Argv<IssueAcceptArgs>;
12
+ export declare function handler(args: IssueAcceptArgs): Promise<void>;
13
+ export {};
@@ -0,0 +1,49 @@
1
+ import { transitionIssue } from '../../service/issue-transition.js';
2
+ export const command = 'issue-accept <issueKey>';
3
+ export const describe = 'Mark a Sonar issue as accepted (transition=accept)';
4
+ export function builder(yargs) {
5
+ return yargs
6
+ .positional('issueKey', {
7
+ type: 'string',
8
+ demandOption: 'Provide an issue key',
9
+ coerce: (value) => String(value).trim(),
10
+ })
11
+ .option('token', {
12
+ type: 'string',
13
+ default: process.env.SONAR_TOKEN,
14
+ defaultDescription: 'SONAR_TOKEN',
15
+ demandOption: 'Provide --token or set SONAR_TOKEN',
16
+ })
17
+ .option('baseUrl', {
18
+ alias: ['base-url', 'url'],
19
+ type: 'string',
20
+ default: process.env.SONAR_BASE_URL ?? 'https://sonarcloud.io',
21
+ defaultDescription: 'SONAR_BASE_URL or https://sonarcloud.io',
22
+ coerce: (value) => String(value).replace(/\/$/, ''),
23
+ })
24
+ .option('comment', {
25
+ type: 'string',
26
+ describe: 'Optional acceptance comment',
27
+ })
28
+ .option('json', {
29
+ type: 'boolean',
30
+ default: false,
31
+ describe: 'Print raw JSON for automation/agents',
32
+ });
33
+ }
34
+ export async function handler(args) {
35
+ const result = await transitionIssue({
36
+ token: args.token,
37
+ baseUrl: args.baseUrl,
38
+ }, {
39
+ issueKey: args.issueKey,
40
+ transition: 'accept',
41
+ comment: args.comment,
42
+ });
43
+ if (args.json) {
44
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
45
+ return;
46
+ }
47
+ process.stdout.write(`Accepted issue ${result.issueKey} (${result.transition})\n`);
48
+ }
49
+ //# sourceMappingURL=issue-accept.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-accept.js","sourceRoot":"","sources":["../../../src/cli/commands/issue-accept.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AAUnE,MAAM,CAAC,MAAM,OAAO,GAAG,yBAAyB,CAAA;AAChD,MAAM,CAAC,MAAM,QAAQ,GAAG,oDAAoD,CAAA;AAE5E,MAAM,UAAU,OAAO,CAAC,KAAW;IACjC,OAAO,KAAK;SACT,UAAU,CAAC,UAAU,EAAE;QACtB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,sBAAsB;QACpC,MAAM,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE;KACjD,CAAC;SACD,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;QAChC,kBAAkB,EAAE,aAAa;QACjC,YAAY,EAAE,oCAAoC;KACnD,CAAC;SACD,MAAM,CAAC,SAAS,EAAE;QACjB,KAAK,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC;QAC1B,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,uBAAuB;QAC9D,kBAAkB,EAAE,yCAAyC;QAC7D,MAAM,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;KAC7D,CAAC;SACD,MAAM,CAAC,SAAS,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,6BAA6B;KACxC,CAAC;SACD,MAAM,CAAC,MAAM,EAAE;QACd,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,sCAAsC;KACjD,CAAC,CAAA;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAqB;IACjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAClC;QACE,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,EACD;QACE,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,QAAQ;QACpB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CACF,CAAA;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;QAC5D,OAAM;IACR,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kBAAkB,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,UAAU,KAAK,CAAC,CAAA;AACpF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Argv } from 'yargs';
2
+ type PullRequestCoverageArgs = {
3
+ token: string;
4
+ baseUrl: string;
5
+ projectKey?: string;
6
+ pullRequest: string;
7
+ threshold: number;
8
+ includePassing: boolean;
9
+ maxFiles: number;
10
+ json: boolean;
11
+ };
12
+ export declare const command = "pr-coverage <pullRequest> [projectKey]";
13
+ export declare const describe = "List files with low new-code coverage for a pull request";
14
+ export declare function builder(yargs: Argv): Argv<PullRequestCoverageArgs>;
15
+ export declare function handler(args: PullRequestCoverageArgs): Promise<void>;
16
+ export {};
@@ -0,0 +1,79 @@
1
+ import { resolveProjectKey } from '../project-key.js';
2
+ import { getPullRequestCoverage } from '../../service/pr-coverage.js';
3
+ export const command = 'pr-coverage <pullRequest> [projectKey]';
4
+ export const describe = 'List files with low new-code coverage for a pull request';
5
+ export function builder(yargs) {
6
+ return yargs
7
+ .positional('projectKey', {
8
+ type: 'string',
9
+ describe: 'Sonar project key (optional; auto-detected from sonar-project.properties)',
10
+ })
11
+ .positional('pullRequest', {
12
+ type: 'string',
13
+ demandOption: 'Provide a pull request key/id',
14
+ coerce: (value) => String(value).trim(),
15
+ })
16
+ .option('token', {
17
+ type: 'string',
18
+ default: process.env.SONAR_TOKEN,
19
+ defaultDescription: 'SONAR_TOKEN',
20
+ demandOption: 'Provide --token or set SONAR_TOKEN',
21
+ })
22
+ .option('baseUrl', {
23
+ alias: ['base-url', 'url'],
24
+ type: 'string',
25
+ default: process.env.SONAR_BASE_URL ?? 'https://sonarcloud.io',
26
+ defaultDescription: 'SONAR_BASE_URL or https://sonarcloud.io',
27
+ coerce: (value) => String(value).replace(/\/$/, ''),
28
+ })
29
+ .option('projectKey', {
30
+ alias: ['project-key', 'k'],
31
+ type: 'string',
32
+ describe: 'Sonar project key (overrides auto-detection)',
33
+ })
34
+ .option('threshold', {
35
+ type: 'number',
36
+ default: 80,
37
+ describe: 'Coverage threshold percentage',
38
+ coerce: (value) => Math.max(0, Math.min(100, Number(value))),
39
+ })
40
+ .option('includePassing', {
41
+ type: 'boolean',
42
+ default: false,
43
+ describe: 'Include files that meet threshold too',
44
+ })
45
+ .option('maxFiles', {
46
+ alias: ['max', 'limit'],
47
+ type: 'number',
48
+ default: 20,
49
+ coerce: (value) => Math.max(1, Number(value)),
50
+ })
51
+ .option('json', {
52
+ type: 'boolean',
53
+ default: false,
54
+ describe: 'Print raw JSON for automation/agents',
55
+ });
56
+ }
57
+ export async function handler(args) {
58
+ const projectKey = resolveProjectKey(args.projectKey);
59
+ const report = await getPullRequestCoverage({
60
+ token: args.token,
61
+ baseUrl: args.baseUrl,
62
+ }, {
63
+ projectKey,
64
+ pullRequest: args.pullRequest,
65
+ threshold: args.threshold,
66
+ includePassing: args.includePassing,
67
+ maxFiles: args.maxFiles,
68
+ });
69
+ if (args.json) {
70
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
71
+ return;
72
+ }
73
+ process.stdout.write(`Found ${report.files.length} file(s) below ${report.threshold.toFixed(1)}% new-code coverage\n`);
74
+ for (const file of report.files) {
75
+ process.stdout.write(`- ${file.file}: ${file.coverageOnNewCode.toFixed(1)}% (${file.uncoveredLines}/${file.linesToCover} uncovered lines)\n`);
76
+ }
77
+ process.stdout.write(`See analysis details: ${report.analysisUrl}\n`);
78
+ }
79
+ //# sourceMappingURL=pr-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-coverage.js","sourceRoot":"","sources":["../../../src/cli/commands/pr-coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Argv } from 'yargs';
2
+ type PullRequestIssuesArgs = {
3
+ token: string;
4
+ baseUrl: string;
5
+ projectKey?: string;
6
+ pullRequest: string;
7
+ page: number;
8
+ pageSize: number;
9
+ json: boolean;
10
+ };
11
+ export declare const command = "pr-issues <pullRequest> [projectKey]";
12
+ export declare const describe = "Fetch SonarQube Cloud issue details for a pull request";
13
+ export declare function builder(yargs: Argv): Argv<PullRequestIssuesArgs>;
14
+ export declare function handler(args: PullRequestIssuesArgs): Promise<void>;
15
+ export {};
@@ -0,0 +1,78 @@
1
+ import { resolveProjectKey } from '../project-key.js';
2
+ import { getPullRequestIssues } from '../../service/pr-issues.js';
3
+ export const command = 'pr-issues <pullRequest> [projectKey]';
4
+ export const describe = 'Fetch SonarQube Cloud issue details for a pull request';
5
+ export function builder(yargs) {
6
+ return yargs
7
+ .positional('projectKey', {
8
+ type: 'string',
9
+ describe: 'Sonar project key (optional; auto-detected from sonar-project.properties)',
10
+ })
11
+ .positional('pullRequest', {
12
+ type: 'string',
13
+ demandOption: 'Provide a pull request key/id',
14
+ coerce: (value) => String(value).trim(),
15
+ })
16
+ .option('token', {
17
+ type: 'string',
18
+ default: process.env.SONAR_TOKEN,
19
+ defaultDescription: 'SONAR_TOKEN',
20
+ demandOption: 'Provide --token or set SONAR_TOKEN',
21
+ })
22
+ .option('baseUrl', {
23
+ alias: ['base-url', 'url'],
24
+ type: 'string',
25
+ default: process.env.SONAR_BASE_URL ?? 'https://sonarcloud.io',
26
+ defaultDescription: 'SONAR_BASE_URL or https://sonarcloud.io',
27
+ coerce: (value) => String(value).replace(/\/$/, ''),
28
+ })
29
+ .option('projectKey', {
30
+ alias: ['project-key', 'k'],
31
+ type: 'string',
32
+ describe: 'Sonar project key (overrides auto-detection)',
33
+ })
34
+ .option('page', {
35
+ alias: 'p',
36
+ type: 'number',
37
+ default: 1,
38
+ coerce: (value) => Math.max(1, Number(value)),
39
+ })
40
+ .option('pageSize', {
41
+ alias: ['ps', 'page-size'],
42
+ type: 'number',
43
+ default: 100,
44
+ coerce: (value) => Math.min(500, Math.max(1, Number(value))),
45
+ })
46
+ .option('json', {
47
+ type: 'boolean',
48
+ default: false,
49
+ describe: 'Print raw JSON for automation/agents',
50
+ });
51
+ }
52
+ export async function handler(args) {
53
+ const projectKey = resolveProjectKey(args.projectKey);
54
+ const report = await getPullRequestIssues({
55
+ token: args.token,
56
+ baseUrl: args.baseUrl,
57
+ }, {
58
+ projectKey,
59
+ pullRequest: args.pullRequest,
60
+ page: args.page,
61
+ pageSize: args.pageSize,
62
+ });
63
+ if (args.json) {
64
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
65
+ return;
66
+ }
67
+ process.stdout.write(`Found ${report.total} open issue(s) in new code for PR ${report.pullRequest} (${report.projectKey})\n`);
68
+ if (report.issues.length === 0) {
69
+ process.stdout.write(`See analysis details: ${report.analysisUrl}\n`);
70
+ return;
71
+ }
72
+ for (const issue of report.issues) {
73
+ const location = issue.line ? `${issue.file}:${issue.line}` : issue.file;
74
+ process.stdout.write(`- [${issue.issueStatus}] ${issue.severity} ${issue.type} ${issue.rule} ${location} - ${issue.message}\n`);
75
+ }
76
+ process.stdout.write(`See analysis details: ${report.analysisUrl}\n`);
77
+ }
78
+ //# sourceMappingURL=pr-issues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-issues.js","sourceRoot":"","sources":["../../../src/cli/commands/pr-issues.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Argv } from 'yargs';
2
+ type PullRequestReportArgs = {
3
+ token: string;
4
+ baseUrl: string;
5
+ projectKey?: string;
6
+ pullRequest: string;
7
+ json: boolean;
8
+ };
9
+ export declare const command = "pr-report <pullRequest> [projectKey]";
10
+ export declare const describe = "Fetch SonarQube Cloud details for a pull request";
11
+ export declare function builder(yargs: Argv): Argv<PullRequestReportArgs>;
12
+ export declare function handler(args: PullRequestReportArgs): Promise<void>;
13
+ export {};
@@ -0,0 +1,94 @@
1
+ import { resolveProjectKey } from '../project-key.js';
2
+ import { getPullRequestReport } from '../../service/pr-report.js';
3
+ export const command = 'pr-report <pullRequest> [projectKey]';
4
+ export const describe = 'Fetch SonarQube Cloud details for a pull request';
5
+ export function builder(yargs) {
6
+ return yargs
7
+ .positional('projectKey', {
8
+ type: 'string',
9
+ describe: 'Sonar project key (optional; auto-detected from sonar-project.properties)',
10
+ })
11
+ .positional('pullRequest', {
12
+ type: 'string',
13
+ demandOption: 'Provide a pull request key/id',
14
+ coerce: (value) => String(value).trim(),
15
+ })
16
+ .option('token', {
17
+ type: 'string',
18
+ default: process.env.SONAR_TOKEN,
19
+ defaultDescription: 'SONAR_TOKEN',
20
+ demandOption: 'Provide --token or set SONAR_TOKEN',
21
+ })
22
+ .option('baseUrl', {
23
+ alias: ['base-url', 'url'],
24
+ type: 'string',
25
+ default: process.env.SONAR_BASE_URL ?? 'https://sonarcloud.io',
26
+ defaultDescription: 'SONAR_BASE_URL or https://sonarcloud.io',
27
+ coerce: (value) => String(value).replace(/\/$/, ''),
28
+ })
29
+ .option('projectKey', {
30
+ alias: ['project-key', 'k'],
31
+ type: 'string',
32
+ describe: 'Sonar project key (overrides auto-detection)',
33
+ })
34
+ .option('json', {
35
+ type: 'boolean',
36
+ default: false,
37
+ describe: 'Print raw JSON for automation/agents',
38
+ });
39
+ }
40
+ export async function handler(args) {
41
+ const projectKey = resolveProjectKey(args.projectKey);
42
+ const report = await getPullRequestReport({
43
+ token: args.token,
44
+ baseUrl: args.baseUrl,
45
+ }, {
46
+ projectKey,
47
+ pullRequest: args.pullRequest,
48
+ });
49
+ if (args.json) {
50
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
51
+ return;
52
+ }
53
+ const qualityGate = report.qualityGateStatus === 'OK'
54
+ ? 'Quality Gate passed'
55
+ : `Quality Gate failed (${report.qualityGateStatus})`;
56
+ process.stdout.write(`${qualityGate}\n\n`);
57
+ if (report.failingQualityGateConditions.length > 0) {
58
+ process.stdout.write('Quality Gate Conditions\n');
59
+ for (const condition of report.failingQualityGateConditions) {
60
+ const metric = formatMetricLabel(condition.metricKey);
61
+ const threshold = condition.errorThreshold ?? 'n/a';
62
+ const actual = condition.actualValue ?? 'n/a';
63
+ process.stdout.write(`- ${metric}: actual=${actual}, threshold=${condition.comparator} ${threshold}\n`);
64
+ }
65
+ process.stdout.write('\n');
66
+ }
67
+ process.stdout.write('Issues\n');
68
+ process.stdout.write(`- ${report.issueCounts.newIssues} New issues\n`);
69
+ process.stdout.write(`- ${report.issueCounts.acceptedIssues} Accepted issues\n\n`);
70
+ process.stdout.write('Measures\n');
71
+ process.stdout.write(`- ${report.measures.securityHotspots} Security Hotspots\n`);
72
+ process.stdout.write(`- ${report.measures.coverageOnNewCode.toFixed(1)}% Coverage on New Code\n`);
73
+ process.stdout.write(`- ${report.measures.duplicationOnNewCode.toFixed(1)}% Duplication on New Code\n\n`);
74
+ process.stdout.write(`See analysis details: ${report.analysisUrl}\n`);
75
+ }
76
+ function formatMetricLabel(metricKey) {
77
+ switch (metricKey) {
78
+ case 'new_coverage':
79
+ return 'Coverage on New Code';
80
+ case 'new_duplicated_lines_density':
81
+ return 'Duplication on New Code';
82
+ case 'new_security_hotspots_reviewed':
83
+ return 'Security Hotspots Reviewed on New Code';
84
+ case 'new_reliability_rating':
85
+ return 'Reliability Rating on New Code';
86
+ case 'new_security_rating':
87
+ return 'Security Rating on New Code';
88
+ case 'new_maintainability_rating':
89
+ return 'Maintainability Rating on New Code';
90
+ default:
91
+ return metricKey;
92
+ }
93
+ }
94
+ //# sourceMappingURL=pr-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-report.js","sourceRoot":"","sources":["../../../src/cli/commands/pr-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Argv } from 'yargs';
2
+ type PullRequestReviewArgs = {
3
+ token: string;
4
+ baseUrl: string;
5
+ projectKey?: string;
6
+ pullRequest: string;
7
+ contextLines: number;
8
+ page: number;
9
+ pageSize: number;
10
+ json: boolean;
11
+ };
12
+ export declare const command = "pr-review <pullRequest> [projectKey]";
13
+ export declare const describe = "Review Sonar pull request issues with code context snippets";
14
+ export declare function builder(yargs: Argv): Argv<PullRequestReviewArgs>;
15
+ export declare function handler(args: PullRequestReviewArgs): Promise<void>;
16
+ export {};
@@ -0,0 +1,93 @@
1
+ import { resolveProjectKey } from '../project-key.js';
2
+ import { getPullRequestReview } from '../../service/pr-review.js';
3
+ export const command = 'pr-review <pullRequest> [projectKey]';
4
+ export const describe = 'Review Sonar pull request issues with code context snippets';
5
+ export function builder(yargs) {
6
+ return yargs
7
+ .positional('projectKey', {
8
+ type: 'string',
9
+ describe: 'Sonar project key (optional; auto-detected from sonar-project.properties)',
10
+ })
11
+ .positional('pullRequest', {
12
+ type: 'string',
13
+ demandOption: 'Provide a pull request key/id',
14
+ coerce: (value) => String(value).trim(),
15
+ })
16
+ .option('token', {
17
+ type: 'string',
18
+ default: process.env.SONAR_TOKEN,
19
+ defaultDescription: 'SONAR_TOKEN',
20
+ demandOption: 'Provide --token or set SONAR_TOKEN',
21
+ })
22
+ .option('baseUrl', {
23
+ alias: ['base-url', 'url'],
24
+ type: 'string',
25
+ default: process.env.SONAR_BASE_URL ?? 'https://sonarcloud.io',
26
+ defaultDescription: 'SONAR_BASE_URL or https://sonarcloud.io',
27
+ coerce: (value) => String(value).replace(/\/$/, ''),
28
+ })
29
+ .option('projectKey', {
30
+ alias: ['project-key', 'k'],
31
+ type: 'string',
32
+ describe: 'Sonar project key (overrides auto-detection)',
33
+ })
34
+ .option('contextLines', {
35
+ alias: ['context', 'C'],
36
+ type: 'number',
37
+ default: 3,
38
+ coerce: (value) => Math.max(0, Number(value)),
39
+ })
40
+ .option('page', {
41
+ alias: 'p',
42
+ type: 'number',
43
+ default: 1,
44
+ coerce: (value) => Math.max(1, Number(value)),
45
+ })
46
+ .option('pageSize', {
47
+ alias: ['ps', 'page-size'],
48
+ type: 'number',
49
+ default: 20,
50
+ coerce: (value) => Math.min(500, Math.max(1, Number(value))),
51
+ })
52
+ .option('json', {
53
+ type: 'boolean',
54
+ default: false,
55
+ describe: 'Print raw JSON for automation/agents',
56
+ });
57
+ }
58
+ export async function handler(args) {
59
+ const projectKey = resolveProjectKey(args.projectKey);
60
+ const report = await getPullRequestReview({
61
+ token: args.token,
62
+ baseUrl: args.baseUrl,
63
+ }, {
64
+ projectKey,
65
+ pullRequest: args.pullRequest,
66
+ contextLines: args.contextLines,
67
+ page: args.page,
68
+ pageSize: args.pageSize,
69
+ });
70
+ if (args.json) {
71
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
72
+ return;
73
+ }
74
+ process.stdout.write(`Found ${report.total} open issue(s) in new code for PR ${report.pullRequest} (${report.projectKey})\n\n`);
75
+ for (const issue of report.issues) {
76
+ const location = issue.line ? `${issue.file}:${issue.line}` : issue.file;
77
+ process.stdout.write(`[${issue.issueStatus}] ${issue.severity} ${issue.type} ${issue.rule} ${location}\n${issue.message}\n`);
78
+ process.stdout.write(`Issue URL: ${issue.issueUrl}\n`);
79
+ if (issue.snippet) {
80
+ process.stdout.write('Context:\n');
81
+ for (const line of issue.snippet.lines) {
82
+ const marker = line.highlight ? '>' : ' ';
83
+ process.stdout.write(`${marker} ${line.line.toString().padStart(4, ' ')} | ${line.text}\n`);
84
+ }
85
+ }
86
+ else if (issue.sourceError) {
87
+ process.stdout.write(`Context unavailable: ${issue.sourceError}\n`);
88
+ }
89
+ process.stdout.write('\n');
90
+ }
91
+ process.stdout.write(`See analysis details: ${report.analysisUrl}\n`);
92
+ }
93
+ //# sourceMappingURL=pr-review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-review.js","sourceRoot":"","sources":["../../../src/cli/commands/pr-review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function run(argv?: string[]): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { hideBin } from 'yargs/helpers';
3
+ import yargs from 'yargs/yargs';
4
+ export async function run(argv = process.argv) {
5
+ const commandsDir = fileURLToPath(new URL('./commands', import.meta.url));
6
+ const cli = yargs(hideBin(argv))
7
+ .scriptName('sonar-sweep')
8
+ .env('SONAR')
9
+ .commandDir(commandsDir, {
10
+ extensions: ['js'],
11
+ exclude: /\.test\.(ts|js)$/,
12
+ });
13
+ await cli
14
+ .demandCommand(1, 'Provide a command')
15
+ .strict()
16
+ .help()
17
+ .fail((message, error, yargsInstance) => {
18
+ if (error) {
19
+ process.stderr.write(`${error.message}\n`);
20
+ process.exit(1);
21
+ }
22
+ process.stderr.write(`${message}\n`);
23
+ yargsInstance.showHelp();
24
+ process.exit(1);
25
+ })
26
+ .parseAsync();
27
+ }
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,aAAa,CAAA;AAE/B,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,OAAiB,OAAO,CAAC,IAAI;IACrD,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAEzE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;SAC7B,UAAU,CAAC,aAAa,CAAC;SACzB,GAAG,CAAC,OAAO,CAAC;SACZ,UAAU,CAAC,WAAW,EAAE;QACvB,UAAU,EAAE,CAAC,IAAI,CAAC;QAClB,OAAO,EAAE,kBAAkB;KAC5B,CAAC,CAAA;IAEJ,MAAM,GAAG;SACN,aAAa,CAAC,CAAC,EAAE,mBAAmB,CAAC;SACrC,MAAM,EAAE;SACR,IAAI,EAAE;SACN,IAAI,CAAC,CAAC,OAAe,EAAE,KAAwB,EAAE,aAAa,EAAE,EAAE;QACjE,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAA;YAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAA;QACpC,aAAa,CAAC,QAAQ,EAAE,CAAA;QACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAC;SACD,UAAU,EAAE,CAAA;AACjB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function resolveProjectKey(projectKey?: string): string;
@@ -0,0 +1,32 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ export function resolveProjectKey(projectKey) {
5
+ if (projectKey?.trim()) {
6
+ return projectKey.trim();
7
+ }
8
+ const gitRoot = getGitRoot();
9
+ const sonarPropsPath = join(gitRoot, 'sonar-project.properties');
10
+ if (!existsSync(sonarPropsPath)) {
11
+ throw new Error(`Missing projectKey and no sonar-project.properties found at ${sonarPropsPath}. Provide --projectKey explicitly.`);
12
+ }
13
+ const content = readFileSync(sonarPropsPath, 'utf8');
14
+ const match = content.match(/^\s*sonar\.projectKey\s*=\s*(.+)\s*$/m);
15
+ const key = match?.[1]?.trim();
16
+ if (!key) {
17
+ throw new Error(`Could not read sonar.projectKey from ${sonarPropsPath}. Provide --projectKey explicitly.`);
18
+ }
19
+ return key;
20
+ }
21
+ function getGitRoot() {
22
+ try {
23
+ return execFileSync('git', ['rev-parse', '--show-toplevel'], {
24
+ encoding: 'utf8',
25
+ stdio: ['ignore', 'pipe', 'ignore'],
26
+ }).trim();
27
+ }
28
+ catch {
29
+ return process.cwd();
30
+ }
31
+ }
32
+ //# sourceMappingURL=project-key.js.map