ghagga 2.8.1 → 3.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 (146) hide show
  1. package/LICENSE +21 -0
  2. package/dist/commands/__tests__/review-tui.test.js +2 -2
  3. package/dist/commands/__tests__/review-tui.test.js.map +1 -1
  4. package/dist/commands/audit.d.ts +23 -0
  5. package/dist/commands/audit.d.ts.map +1 -0
  6. package/dist/commands/audit.js +282 -0
  7. package/dist/commands/audit.js.map +1 -0
  8. package/dist/commands/audit.test.d.ts +10 -0
  9. package/dist/commands/audit.test.d.ts.map +1 -0
  10. package/dist/commands/audit.test.js +110 -0
  11. package/dist/commands/audit.test.js.map +1 -0
  12. package/dist/commands/dismiss.d.ts +14 -0
  13. package/dist/commands/dismiss.d.ts.map +1 -0
  14. package/dist/commands/dismiss.js +98 -0
  15. package/dist/commands/dismiss.js.map +1 -0
  16. package/dist/commands/feedback.d.ts +18 -0
  17. package/dist/commands/feedback.d.ts.map +1 -0
  18. package/dist/commands/feedback.js +122 -0
  19. package/dist/commands/feedback.js.map +1 -0
  20. package/dist/commands/login.js +3 -3
  21. package/dist/commands/login.js.map +1 -1
  22. package/dist/commands/login.test.js +3 -4
  23. package/dist/commands/login.test.js.map +1 -1
  24. package/dist/commands/review-commit-msg.test.js +2 -2
  25. package/dist/commands/review-commit-msg.test.js.map +1 -1
  26. package/dist/commands/review-output.test.js +30 -2
  27. package/dist/commands/review-output.test.js.map +1 -1
  28. package/dist/commands/review.d.ts +16 -0
  29. package/dist/commands/review.d.ts.map +1 -1
  30. package/dist/commands/review.js +239 -3
  31. package/dist/commands/review.js.map +1 -1
  32. package/dist/commands/review.mr-exit.test.d.ts +13 -0
  33. package/dist/commands/review.mr-exit.test.d.ts.map +1 -0
  34. package/dist/commands/review.mr-exit.test.js +190 -0
  35. package/dist/commands/review.mr-exit.test.js.map +1 -0
  36. package/dist/commands/review.postback-disclosure.test.d.ts +22 -0
  37. package/dist/commands/review.postback-disclosure.test.d.ts.map +1 -0
  38. package/dist/commands/review.postback-disclosure.test.js +192 -0
  39. package/dist/commands/review.postback-disclosure.test.js.map +1 -0
  40. package/dist/commands/review.pr-exit.test.d.ts +13 -0
  41. package/dist/commands/review.pr-exit.test.d.ts.map +1 -0
  42. package/dist/commands/review.pr-exit.test.js +162 -0
  43. package/dist/commands/review.pr-exit.test.js.map +1 -0
  44. package/dist/commands/review.pr-jenkins.test.d.ts +16 -0
  45. package/dist/commands/review.pr-jenkins.test.d.ts.map +1 -0
  46. package/dist/commands/review.pr-jenkins.test.js +106 -0
  47. package/dist/commands/review.pr-jenkins.test.js.map +1 -0
  48. package/dist/commands/review.pr-nativeid-fallback.test.d.ts +15 -0
  49. package/dist/commands/review.pr-nativeid-fallback.test.d.ts.map +1 -0
  50. package/dist/commands/review.pr-nativeid-fallback.test.js +151 -0
  51. package/dist/commands/review.pr-nativeid-fallback.test.js.map +1 -0
  52. package/dist/commands/review.test.js +48 -33
  53. package/dist/commands/review.test.js.map +1 -1
  54. package/dist/commands/status.d.ts.map +1 -1
  55. package/dist/commands/status.js +15 -2
  56. package/dist/commands/status.js.map +1 -1
  57. package/dist/commands/status.test.js +31 -10
  58. package/dist/commands/status.test.js.map +1 -1
  59. package/dist/index.d.ts +3 -3
  60. package/dist/index.js +116 -23
  61. package/dist/index.js.map +1 -1
  62. package/dist/lib/cli-github-client-port.d.ts +52 -0
  63. package/dist/lib/cli-github-client-port.d.ts.map +1 -0
  64. package/dist/lib/cli-github-client-port.js +199 -0
  65. package/dist/lib/cli-github-client-port.js.map +1 -0
  66. package/dist/lib/cli-github-client-port.test.d.ts +10 -0
  67. package/dist/lib/cli-github-client-port.test.d.ts.map +1 -0
  68. package/dist/lib/cli-github-client-port.test.js +47 -0
  69. package/dist/lib/cli-github-client-port.test.js.map +1 -0
  70. package/dist/lib/cli-gitlab-client-port.d.ts +62 -0
  71. package/dist/lib/cli-gitlab-client-port.d.ts.map +1 -0
  72. package/dist/lib/cli-gitlab-client-port.js +209 -0
  73. package/dist/lib/cli-gitlab-client-port.js.map +1 -0
  74. package/dist/lib/cli-gitlab-client-port.test.d.ts +9 -0
  75. package/dist/lib/cli-gitlab-client-port.test.d.ts.map +1 -0
  76. package/dist/lib/cli-gitlab-client-port.test.js +278 -0
  77. package/dist/lib/cli-gitlab-client-port.test.js.map +1 -0
  78. package/dist/lib/config.d.ts +12 -0
  79. package/dist/lib/config.d.ts.map +1 -1
  80. package/dist/lib/config.js +14 -2
  81. package/dist/lib/config.js.map +1 -1
  82. package/dist/lib/config.test.js +22 -3
  83. package/dist/lib/config.test.js.map +1 -1
  84. package/dist/lib/forge-postback.d.ts +72 -0
  85. package/dist/lib/forge-postback.d.ts.map +1 -0
  86. package/dist/lib/forge-postback.js +39 -0
  87. package/dist/lib/forge-postback.js.map +1 -0
  88. package/dist/lib/github-api.d.ts.map +1 -1
  89. package/dist/lib/github-api.js +15 -6
  90. package/dist/lib/github-api.js.map +1 -1
  91. package/dist/lib/github-api.test.js +24 -1
  92. package/dist/lib/github-api.test.js.map +1 -1
  93. package/dist/lib/gitlab-api.d.ts +50 -0
  94. package/dist/lib/gitlab-api.d.ts.map +1 -0
  95. package/dist/lib/gitlab-api.js +75 -0
  96. package/dist/lib/gitlab-api.js.map +1 -0
  97. package/dist/lib/gitlab-api.test.d.ts +2 -0
  98. package/dist/lib/gitlab-api.test.d.ts.map +1 -0
  99. package/dist/lib/gitlab-api.test.js +79 -0
  100. package/dist/lib/gitlab-api.test.js.map +1 -0
  101. package/dist/lib/gitlab-e2e.manual.test.d.ts +24 -0
  102. package/dist/lib/gitlab-e2e.manual.test.d.ts.map +1 -0
  103. package/dist/lib/gitlab-e2e.manual.test.js +57 -0
  104. package/dist/lib/gitlab-e2e.manual.test.js.map +1 -0
  105. package/dist/lib/gitlab-token.d.ts +17 -0
  106. package/dist/lib/gitlab-token.d.ts.map +1 -0
  107. package/dist/lib/gitlab-token.js +24 -0
  108. package/dist/lib/gitlab-token.js.map +1 -0
  109. package/dist/lib/gitlab-token.test.d.ts +2 -0
  110. package/dist/lib/gitlab-token.test.d.ts.map +1 -0
  111. package/dist/lib/gitlab-token.test.js +38 -0
  112. package/dist/lib/gitlab-token.test.js.map +1 -0
  113. package/dist/lib/oauth.test.js +0 -1
  114. package/dist/lib/oauth.test.js.map +1 -1
  115. package/dist/lib/pr-postback.d.ts +42 -0
  116. package/dist/lib/pr-postback.d.ts.map +1 -0
  117. package/dist/lib/pr-postback.js +44 -0
  118. package/dist/lib/pr-postback.js.map +1 -0
  119. package/dist/lib/pr-postback.test.d.ts +9 -0
  120. package/dist/lib/pr-postback.test.d.ts.map +1 -0
  121. package/dist/lib/pr-postback.test.js +196 -0
  122. package/dist/lib/pr-postback.test.js.map +1 -0
  123. package/dist/lib/pr-token.d.ts +15 -0
  124. package/dist/lib/pr-token.d.ts.map +1 -0
  125. package/dist/lib/pr-token.js +22 -0
  126. package/dist/lib/pr-token.js.map +1 -0
  127. package/dist/lib/pr-token.test.d.ts +2 -0
  128. package/dist/lib/pr-token.test.d.ts.map +1 -0
  129. package/dist/lib/pr-token.test.js +25 -0
  130. package/dist/lib/pr-token.test.js.map +1 -0
  131. package/dist/lib/providers.d.ts +29 -0
  132. package/dist/lib/providers.d.ts.map +1 -0
  133. package/dist/lib/providers.js +41 -0
  134. package/dist/lib/providers.js.map +1 -0
  135. package/dist/lib/providers.test.d.ts +9 -0
  136. package/dist/lib/providers.test.d.ts.map +1 -0
  137. package/dist/lib/providers.test.js +60 -0
  138. package/dist/lib/providers.test.js.map +1 -0
  139. package/dist/ui/__tests__/format.test.js +2 -2
  140. package/dist/ui/__tests__/format.test.js.map +1 -1
  141. package/dist/ui/__tests__/theme.test.js +3 -2
  142. package/dist/ui/__tests__/theme.test.js.map +1 -1
  143. package/dist/ui/theme.d.ts.map +1 -1
  144. package/dist/ui/theme.js +1 -0
  145. package/dist/ui/theme.js.map +1 -1
  146. package/package.json +18 -18
@@ -0,0 +1,192 @@
1
+ /**
2
+ * SECURITY HYGIENE — before the CLI posts a review back to a forge (`--pr` GitHub
3
+ * PR / `--mr` GitLab MR), it discloses the DESTINATION HOST on stderr so the user
4
+ * sees where their token is going BEFORE the network POST fires.
5
+ *
6
+ * Rationale: the GitLab API base is derived from the git remote host, so a
7
+ * poisoned `origin` could exfiltrate GITLAB_TOKEN. Disclosing the REAL resolved
8
+ * host makes that visible.
9
+ *
10
+ * These tests assert:
11
+ * - the disclosure line is emitted to STDERR (console.error), NOT stdout
12
+ * (stdout is reserved for SARIF / JSON / markdown output)
13
+ * - the host is correct for --pr (api.github.com) and --mr (gitlab.com)
14
+ * - a self-hosted GitLab remote discloses THAT host (real resolution, not a
15
+ * mock) — this is the poisoned-origin-visible case
16
+ *
17
+ * For the GitLab path we deliberately use the REAL parseGitLabRemote +
18
+ * resolveGitLabApiBase (only the network seams are mocked) so the host shown is
19
+ * the genuinely-resolved destination.
20
+ */
21
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
+ vi.mock('ghagga-core', () => ({
23
+ reviewPipeline: vi.fn(),
24
+ buildSarif: vi.fn().mockReturnValue({ version: '2.1.0', runs: [] }),
25
+ formatReviewComment: vi.fn().mockReturnValue('<!-- ghagga-review -->\nbody'),
26
+ formatBoxSummary: vi.fn().mockReturnValue([]),
27
+ REVIEW_COMMENT_MARKER: '<!-- ghagga-review -->',
28
+ DEFAULT_SETTINGS: {
29
+ enableSemgrep: true,
30
+ enableTrivy: true,
31
+ enableCpd: true,
32
+ enableMemory: true,
33
+ customRules: [],
34
+ ignorePatterns: [],
35
+ reviewLevel: 'normal',
36
+ enabledTools: [],
37
+ disabledTools: [],
38
+ },
39
+ DEFAULT_MODELS: { gateway: 'auto', 'cli-bridge': 'auto', ollama: 'llama3' },
40
+ initializeDefaultTools: vi.fn(),
41
+ toolRegistry: { getAll: vi.fn().mockReturnValue([]) },
42
+ EngramMemoryStorage: { create: vi.fn() },
43
+ SqliteMemoryStorage: { create: vi.fn() },
44
+ formatReviewResult: vi.fn(),
45
+ }));
46
+ vi.mock('node:child_process', () => ({ execSync: vi.fn() }));
47
+ vi.mock('node:fs', () => ({ readFileSync: vi.fn(), existsSync: vi.fn().mockReturnValue(false) }));
48
+ // Token + post-back seams (network) are mocked; remote-parse + api-base are REAL.
49
+ vi.mock('../lib/pr-token.js', () => ({ resolvePrToken: vi.fn() }));
50
+ vi.mock('../lib/gitlab-token.js', () => ({ resolveMrToken: vi.fn() }));
51
+ vi.mock('../lib/pr-postback.js', () => ({ postSummaryComment: vi.fn() }));
52
+ vi.mock('../lib/github-api.js', () => ({
53
+ parseGitHubRemote: vi.fn().mockReturnValue({ owner: 'acme', repo: 'ci' }),
54
+ createComment: vi.fn(),
55
+ createIssue: vi.fn(),
56
+ ensureLabel: vi.fn(),
57
+ formatIssueBody: vi.fn(),
58
+ }));
59
+ // NOTE: gitlab-api.js (parseGitLabRemote) and the resolveGitLabApiBase host
60
+ // derivation are NOT mocked — we want the REAL resolved host disclosed.
61
+ vi.mock('../lib/cli-gitlab-client-port.js', async (importOriginal) => {
62
+ const actual = await importOriginal();
63
+ return {
64
+ ...actual,
65
+ createCliGitLabClientPort: vi.fn(),
66
+ resolveGitLabProjectId: vi.fn().mockResolvedValue('12345'),
67
+ };
68
+ });
69
+ vi.mock('ghagga-forge', () => ({
70
+ GitHubForgeAdapter: class {
71
+ },
72
+ GitLabForgeAdapter: class {
73
+ },
74
+ StaticTokenProvider: class {
75
+ async getToken() {
76
+ return 'tok';
77
+ }
78
+ },
79
+ }));
80
+ vi.mock('../lib/cli-github-client-port.js', () => ({ createCliGitHubClientPort: vi.fn() }));
81
+ import { execSync } from 'node:child_process';
82
+ import { reviewPipeline } from 'ghagga-core';
83
+ import { resolveMrToken } from '../lib/gitlab-token.js';
84
+ import { postSummaryComment } from '../lib/pr-postback.js';
85
+ import { resolvePrToken } from '../lib/pr-token.js';
86
+ const mockExecSync = vi.mocked(execSync);
87
+ const mockReviewPipeline = vi.mocked(reviewPipeline);
88
+ const mockResolvePrToken = vi.mocked(resolvePrToken);
89
+ const mockResolveMrToken = vi.mocked(resolveMrToken);
90
+ const mockPostSummaryComment = vi.mocked(postSummaryComment);
91
+ function defaultOptions(overrides = {}) {
92
+ return {
93
+ mode: 'simple',
94
+ provider: 'gateway',
95
+ model: 'm',
96
+ apiKey: 'k',
97
+ semgrep: true,
98
+ trivy: true,
99
+ cpd: true,
100
+ memory: false,
101
+ verbose: false,
102
+ disableTools: [],
103
+ enableTools: [],
104
+ ...overrides,
105
+ };
106
+ }
107
+ function passedResult() {
108
+ return {
109
+ status: 'PASSED',
110
+ summary: 'ok',
111
+ findings: [],
112
+ staticAnalysis: {
113
+ semgrep: { status: 'skipped', findings: [], executionTimeMs: 0 },
114
+ trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
115
+ cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
116
+ },
117
+ memoryContext: null,
118
+ metadata: {
119
+ mode: 'simple',
120
+ provider: 'gateway',
121
+ model: 'm',
122
+ tokensUsed: 1,
123
+ executionTimeMs: 1,
124
+ toolsRun: [],
125
+ toolsSkipped: [],
126
+ fileList: [],
127
+ },
128
+ };
129
+ }
130
+ describe('reviewCommand — forge post-back DESTINATION HOST disclosure (stderr)', () => {
131
+ // biome-ignore lint/suspicious/noExplicitAny: spy
132
+ let errorSpy;
133
+ // biome-ignore lint/suspicious/noExplicitAny: spy
134
+ let logSpy;
135
+ beforeEach(() => {
136
+ vi.clearAllMocks();
137
+ vi.spyOn(process, 'exit').mockImplementation((() => { }));
138
+ errorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
139
+ logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
140
+ mockReviewPipeline.mockResolvedValue(passedResult());
141
+ mockPostSummaryComment.mockResolvedValue({ createdNativeId: 77, deletedNativeIds: [] });
142
+ });
143
+ afterEach(() => {
144
+ vi.restoreAllMocks();
145
+ });
146
+ /** All console.error lines as one blob for substring assertions. */
147
+ function stderrBlob() {
148
+ return errorSpy.mock.calls.map((c) => String(c[0])).join('\n');
149
+ }
150
+ function stdoutBlob() {
151
+ return logSpy.mock.calls.map((c) => String(c[0])).join('\n');
152
+ }
153
+ it('--pr discloses api.github.com on stderr (not stdout), before the POST', async () => {
154
+ mockResolvePrToken.mockReturnValue('ghp');
155
+ mockExecSync.mockReturnValue('git@github.com:acme/ci.git');
156
+ const { reviewCommand } = await import('./review.js');
157
+ await reviewCommand('.', defaultOptions({ pr: 3, outputFormat: 'sarif' }));
158
+ expect(stderrBlob()).toContain('→ Posting PR #3 review to api.github.com');
159
+ // MUST NOT pollute stdout (SARIF lives there).
160
+ expect(stdoutBlob()).not.toContain('Posting PR');
161
+ expect(stdoutBlob()).not.toContain('api.github.com');
162
+ });
163
+ it('--mr discloses the gitlab.com host on stderr (not stdout)', async () => {
164
+ mockResolveMrToken.mockReturnValue('glpat');
165
+ mockExecSync.mockReturnValue('git@gitlab.com:acme/widgets.git');
166
+ const { reviewCommand } = await import('./review.js');
167
+ await reviewCommand('.', defaultOptions({ mr: 7, outputFormat: 'sarif' }));
168
+ expect(stderrBlob()).toContain('→ Posting MR #7 review to gitlab.com');
169
+ expect(stdoutBlob()).not.toContain('Posting MR');
170
+ expect(stdoutBlob()).not.toContain('gitlab.com');
171
+ });
172
+ it('--mr on a SELF-HOSTED GitLab discloses THAT host (poisoned-origin visible)', async () => {
173
+ mockResolveMrToken.mockReturnValue('glpat');
174
+ // The git remote points at a self-managed/poisoned host — the disclosure must
175
+ // surface the REAL resolved API host, not a hardcoded gitlab.com.
176
+ mockExecSync.mockReturnValue('git@gitlab.internal.example:team/app.git');
177
+ const { reviewCommand } = await import('./review.js');
178
+ await reviewCommand('.', defaultOptions({ mr: 42, outputFormat: 'sarif' }));
179
+ expect(stderrBlob()).toContain('→ Posting MR #42 review to gitlab.internal.example');
180
+ expect(stdoutBlob()).not.toContain('gitlab.internal.example');
181
+ });
182
+ it('the disclosure is emitted BEFORE the post (host shown even when the post throws)', async () => {
183
+ mockResolveMrToken.mockReturnValue('glpat');
184
+ mockExecSync.mockReturnValue('git@gitlab.com:acme/widgets.git');
185
+ mockPostSummaryComment.mockRejectedValue(new Error('GitLab 500'));
186
+ const { reviewCommand } = await import('./review.js');
187
+ await reviewCommand('.', defaultOptions({ mr: 7, outputFormat: 'sarif' }));
188
+ // Disclosure present even though the post failed afterwards.
189
+ expect(stderrBlob()).toContain('→ Posting MR #7 review to gitlab.com');
190
+ });
191
+ });
192
+ //# sourceMappingURL=review.postback-disclosure.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.postback-disclosure.test.js","sourceRoot":"","sources":["../../src/commands/review.postback-disclosure.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * FIX 3 (P3 4vr) — `--pr` post-back failure must be BLOCKING by default.
3
+ *
4
+ * `--pr` is an explicitly-requested side-effect. In CI (Jenkins) a post-back
5
+ * that throws — or a missing token — must FAIL the job (non-zero exit), not
6
+ * silently exit 0. `--pr-soft-fail` restores the old non-blocking behavior.
7
+ *
8
+ * We mock the seams reviewCommand composes (pipeline, git, token, post-back,
9
+ * core formatters) so these tests are deterministic and offline, and assert the
10
+ * resolved process exit code.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=review.pr-exit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.pr-exit.test.d.ts","sourceRoot":"","sources":["../../src/commands/review.pr-exit.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * FIX 3 (P3 4vr) — `--pr` post-back failure must be BLOCKING by default.
3
+ *
4
+ * `--pr` is an explicitly-requested side-effect. In CI (Jenkins) a post-back
5
+ * that throws — or a missing token — must FAIL the job (non-zero exit), not
6
+ * silently exit 0. `--pr-soft-fail` restores the old non-blocking behavior.
7
+ *
8
+ * We mock the seams reviewCommand composes (pipeline, git, token, post-back,
9
+ * core formatters) so these tests are deterministic and offline, and assert the
10
+ * resolved process exit code.
11
+ */
12
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
13
+ // ─── Mock ghagga-core: only what review.ts touches on the --pr path ──
14
+ vi.mock('ghagga-core', () => ({
15
+ reviewPipeline: vi.fn(),
16
+ buildSarif: vi.fn().mockReturnValue({ version: '2.1.0', runs: [] }),
17
+ formatReviewComment: vi.fn().mockReturnValue('<!-- ghagga-review -->\nbody'),
18
+ formatBoxSummary: vi.fn().mockReturnValue([]),
19
+ REVIEW_COMMENT_MARKER: '<!-- ghagga-review -->',
20
+ DEFAULT_SETTINGS: {
21
+ enableSemgrep: true,
22
+ enableTrivy: true,
23
+ enableCpd: true,
24
+ enableMemory: true,
25
+ customRules: [],
26
+ ignorePatterns: [],
27
+ reviewLevel: 'normal',
28
+ enabledTools: [],
29
+ disabledTools: [],
30
+ },
31
+ DEFAULT_MODELS: { gateway: 'auto', 'cli-bridge': 'auto', ollama: 'llama3' },
32
+ initializeDefaultTools: vi.fn(),
33
+ toolRegistry: { getAll: vi.fn().mockReturnValue([]) },
34
+ EngramMemoryStorage: { create: vi.fn() },
35
+ SqliteMemoryStorage: { create: vi.fn() },
36
+ formatReviewResult: vi.fn(),
37
+ }));
38
+ vi.mock('node:child_process', () => ({ execSync: vi.fn() }));
39
+ vi.mock('node:fs', () => ({ readFileSync: vi.fn(), existsSync: vi.fn().mockReturnValue(false) }));
40
+ // PR-token + remote-parse + post-back seams.
41
+ vi.mock('../lib/pr-token.js', () => ({ resolvePrToken: vi.fn() }));
42
+ vi.mock('../lib/pr-postback.js', () => ({ postSummaryComment: vi.fn() }));
43
+ vi.mock('../lib/github-api.js', () => ({
44
+ parseGitHubRemote: vi.fn().mockReturnValue({ owner: 'acme', repo: 'ci' }),
45
+ createComment: vi.fn(),
46
+ createIssue: vi.fn(),
47
+ ensureLabel: vi.fn(),
48
+ formatIssueBody: vi.fn(),
49
+ }));
50
+ // The forge adapter + client port are constructed but never exercised (post-back
51
+ // is mocked); stub them so construction is cheap.
52
+ vi.mock('ghagga-forge', () => ({
53
+ GitHubForgeAdapter: class {
54
+ },
55
+ StaticTokenProvider: class {
56
+ async getToken() {
57
+ return 'tok';
58
+ }
59
+ },
60
+ }));
61
+ vi.mock('../lib/cli-github-client-port.js', () => ({ createCliGitHubClientPort: vi.fn() }));
62
+ import { execSync } from 'node:child_process';
63
+ import { reviewPipeline } from 'ghagga-core';
64
+ import { postSummaryComment } from '../lib/pr-postback.js';
65
+ import { resolvePrToken } from '../lib/pr-token.js';
66
+ const mockExecSync = vi.mocked(execSync);
67
+ const mockReviewPipeline = vi.mocked(reviewPipeline);
68
+ const mockResolvePrToken = vi.mocked(resolvePrToken);
69
+ const mockPostSummaryComment = vi.mocked(postSummaryComment);
70
+ function defaultOptions(overrides = {}) {
71
+ return {
72
+ mode: 'simple',
73
+ provider: 'gateway',
74
+ model: 'm',
75
+ apiKey: 'k',
76
+ semgrep: true,
77
+ trivy: true,
78
+ cpd: true,
79
+ memory: false,
80
+ verbose: false,
81
+ disableTools: [],
82
+ enableTools: [],
83
+ ...overrides,
84
+ };
85
+ }
86
+ function passedResult() {
87
+ return {
88
+ status: 'PASSED',
89
+ summary: 'ok',
90
+ findings: [],
91
+ staticAnalysis: {
92
+ semgrep: { status: 'skipped', findings: [], executionTimeMs: 0 },
93
+ trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
94
+ cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
95
+ },
96
+ memoryContext: null,
97
+ metadata: {
98
+ mode: 'simple',
99
+ provider: 'gateway',
100
+ model: 'm',
101
+ tokensUsed: 1,
102
+ executionTimeMs: 1,
103
+ toolsRun: [],
104
+ toolsSkipped: [],
105
+ fileList: [],
106
+ },
107
+ };
108
+ }
109
+ describe('reviewCommand — --pr post-back exit semantics (FIX 3)', () => {
110
+ // biome-ignore lint/suspicious/noExplicitAny: spy
111
+ let exitSpy;
112
+ // biome-ignore lint/suspicious/noExplicitAny: spy
113
+ let errorSpy;
114
+ beforeEach(() => {
115
+ vi.clearAllMocks();
116
+ exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }));
117
+ errorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
118
+ vi.spyOn(console, 'log').mockImplementation(() => { });
119
+ mockExecSync.mockReturnValue('diff content');
120
+ mockReviewPipeline.mockResolvedValue(passedResult());
121
+ });
122
+ afterEach(() => {
123
+ vi.restoreAllMocks();
124
+ });
125
+ it('--pr + post-back throws → exits NON-ZERO (1) even when review PASSED', async () => {
126
+ mockResolvePrToken.mockReturnValue('gh-token');
127
+ mockPostSummaryComment.mockRejectedValue(new Error('GitHub 500'));
128
+ const { reviewCommand } = await import('./review.js');
129
+ await reviewCommand('.', defaultOptions({ pr: 42, outputFormat: 'sarif' }));
130
+ expect(exitSpy).toHaveBeenCalledWith(1);
131
+ });
132
+ it('--pr + missing token → exits NON-ZERO (1) with a clear message', async () => {
133
+ mockResolvePrToken.mockReturnValue(undefined);
134
+ const { reviewCommand } = await import('./review.js');
135
+ await reviewCommand('.', defaultOptions({ pr: 42, outputFormat: 'sarif' }));
136
+ expect(exitSpy).toHaveBeenCalledWith(1);
137
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('--pr requires a GitHub token'));
138
+ });
139
+ it('--pr-soft-fail + post-back throws → WARNS and exits 0 (review PASSED)', async () => {
140
+ mockResolvePrToken.mockReturnValue('gh-token');
141
+ mockPostSummaryComment.mockRejectedValue(new Error('GitHub 500'));
142
+ const { reviewCommand } = await import('./review.js');
143
+ await reviewCommand('.', defaultOptions({ pr: 42, prSoftFail: true, outputFormat: 'sarif' }));
144
+ expect(exitSpy).toHaveBeenCalledWith(0);
145
+ });
146
+ it('--pr success → exits 0 when review PASSED', async () => {
147
+ mockResolvePrToken.mockReturnValue('gh-token');
148
+ mockPostSummaryComment.mockResolvedValue({ createdNativeId: 7, deletedNativeIds: [] });
149
+ const { reviewCommand } = await import('./review.js');
150
+ await reviewCommand('.', defaultOptions({ pr: 42, outputFormat: 'sarif' }));
151
+ expect(exitSpy).toHaveBeenCalledWith(0);
152
+ });
153
+ it('does NOT mask a review-found-issues exit: --pr success + FAILED review → exit 1', async () => {
154
+ mockReviewPipeline.mockResolvedValue({ ...passedResult(), status: 'FAILED' });
155
+ mockResolvePrToken.mockReturnValue('gh-token');
156
+ mockPostSummaryComment.mockResolvedValue({ createdNativeId: 7, deletedNativeIds: [] });
157
+ const { reviewCommand } = await import('./review.js');
158
+ await reviewCommand('.', defaultOptions({ pr: 42, outputFormat: 'sarif' }));
159
+ expect(exitSpy).toHaveBeenCalledWith(1);
160
+ });
161
+ });
162
+ //# sourceMappingURL=review.pr-exit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.pr-exit.test.js","sourceRoot":"","sources":["../../src/commands/review.pr-exit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 3.3 — Jenkins + GitHub composition scenario.
3
+ *
4
+ * Simulates a CI/Jenkins run of `ghagga review --pr N --output sarif`:
5
+ * - GITHUB_TOKEN env is set (no interactive login) → the post-back resolves it
6
+ * and posts the summary via the forge adapter.
7
+ * - `--output sarif` still emits SARIF (buildSarif) — the two outputs coexist.
8
+ * - issue-export stays CLI-local: createIssue/ensureLabel are the SAME
9
+ * github-api.ts functions, untouched by the PR path.
10
+ *
11
+ * We assemble the exact building blocks reviewCommand composes (token resolution
12
+ * → adapter post-back, buildSarif, github-api issue fns) rather than driving the
13
+ * full LLM pipeline, so the scenario is deterministic and offline.
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=review.pr-jenkins.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.pr-jenkins.test.d.ts","sourceRoot":"","sources":["../../src/commands/review.pr-jenkins.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 3.3 — Jenkins + GitHub composition scenario.
3
+ *
4
+ * Simulates a CI/Jenkins run of `ghagga review --pr N --output sarif`:
5
+ * - GITHUB_TOKEN env is set (no interactive login) → the post-back resolves it
6
+ * and posts the summary via the forge adapter.
7
+ * - `--output sarif` still emits SARIF (buildSarif) — the two outputs coexist.
8
+ * - issue-export stays CLI-local: createIssue/ensureLabel are the SAME
9
+ * github-api.ts functions, untouched by the PR path.
10
+ *
11
+ * We assemble the exact building blocks reviewCommand composes (token resolution
12
+ * → adapter post-back, buildSarif, github-api issue fns) rather than driving the
13
+ * full LLM pipeline, so the scenario is deterministic and offline.
14
+ */
15
+ import { buildSarif, formatReviewComment, REVIEW_COMMENT_MARKER, } from 'ghagga-core';
16
+ import { GitHubForgeAdapter, StaticTokenProvider } from 'ghagga-forge';
17
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
18
+ import { createCliGitHubClientPort } from '../lib/cli-github-client-port.js';
19
+ import * as githubApi from '../lib/github-api.js';
20
+ import { postSummaryComment } from '../lib/pr-postback.js';
21
+ import { resolvePrToken } from '../lib/pr-token.js';
22
+ const mockFetch = vi.fn();
23
+ vi.stubGlobal('fetch', mockFetch);
24
+ function jsonResponse(status, body) {
25
+ return {
26
+ ok: status >= 200 && status < 300,
27
+ status,
28
+ statusText: 'OK',
29
+ text: () => Promise.resolve(JSON.stringify(body)),
30
+ json: () => Promise.resolve(body),
31
+ };
32
+ }
33
+ function makeResult() {
34
+ return {
35
+ status: 'PASSED',
36
+ summary: 'Looks good',
37
+ findings: [
38
+ { severity: 'high', category: 'security', file: 'src/a.ts', message: 'x', source: 'ai' },
39
+ ],
40
+ staticAnalysis: {
41
+ semgrep: { status: 'success', findings: [], executionTimeMs: 1 },
42
+ trivy: { status: 'skipped', findings: [], executionTimeMs: 0 },
43
+ cpd: { status: 'skipped', findings: [], executionTimeMs: 0 },
44
+ },
45
+ memoryContext: null,
46
+ metadata: {
47
+ mode: 'simple',
48
+ provider: 'gateway',
49
+ model: 'gpt-4o-mini',
50
+ tokensUsed: 1,
51
+ executionTimeMs: 1000,
52
+ toolsRun: [],
53
+ toolsSkipped: [],
54
+ fileList: ['src/a.ts'],
55
+ },
56
+ };
57
+ }
58
+ describe('3.3 Jenkins+GitHub: --pr post-back + SARIF coexist; issue-export CLI-local', () => {
59
+ const ORIGINAL_ENV = { ...process.env };
60
+ beforeEach(() => {
61
+ mockFetch.mockReset();
62
+ process.env.GITHUB_TOKEN = 'jenkins-token';
63
+ process.env.GH_TOKEN = undefined;
64
+ });
65
+ afterEach(() => {
66
+ process.env = { ...ORIGINAL_ENV };
67
+ });
68
+ it('GITHUB_TOKEN drives the adapter PR post AND SARIF still renders', async () => {
69
+ const result = makeResult();
70
+ // ── (a) Token resolution prefers the CI env var ───────────
71
+ const token = resolvePrToken();
72
+ expect(token).toBe('jenkins-token');
73
+ // ── (b) SARIF output still emits independently ────────────
74
+ const sarif = buildSarif(result, '3.0.0');
75
+ expect(sarif.runs).toBeDefined();
76
+ expect(Array.isArray(sarif.runs)).toBe(true);
77
+ // ── (c) Post-back via the forge adapter (mocked GitHub) ───
78
+ mockFetch.mockResolvedValueOnce(jsonResponse(200, [])); // find: none
79
+ mockFetch.mockResolvedValueOnce(jsonResponse(201, { id: 7777 })); // post
80
+ const provider = new StaticTokenProvider(token);
81
+ const adapter = new GitHubForgeAdapter({
82
+ client: createCliGitHubClientPort(),
83
+ token: await provider.getToken(),
84
+ owner: 'acme',
85
+ repo: 'ci',
86
+ });
87
+ const body = formatReviewComment(result, { fileList: result.metadata.fileList });
88
+ const postRes = await postSummaryComment(adapter, { repo: { kind: 'github', nativeId: 'acme/ci', path: 'acme/ci' }, iid: 88 }, body, { html: REVIEW_COMMENT_MARKER });
89
+ expect(postRes.createdNativeId).toBe(7777);
90
+ // Body carries the marker (parity with server PR comment).
91
+ const [, postInit] = mockFetch.mock.calls[1];
92
+ expect(JSON.parse(postInit.body).body).toContain(REVIEW_COMMENT_MARKER);
93
+ });
94
+ it('issue-export functions are the SAME CLI-local github-api fns (untouched)', () => {
95
+ // The PR path imports the forge adapter; issue-export remains the local
96
+ // github-api.ts createIssue/createComment/ensureLabel — verify identity.
97
+ expect(typeof githubApi.createIssue).toBe('function');
98
+ expect(typeof githubApi.createComment).toBe('function');
99
+ expect(typeof githubApi.ensureLabel).toBe('function');
100
+ // These are NOT routed through the forge adapter (they hit /issues, /labels
101
+ // directly). Confirm the module still exposes the original public surface.
102
+ expect(githubApi.createIssue.name).toBe('createIssue');
103
+ expect(githubApi.ensureLabel.name).toBe('ensureLabel');
104
+ });
105
+ });
106
+ //# sourceMappingURL=review.pr-jenkins.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.pr-jenkins.test.js","sourceRoot":"","sources":["../../src/commands/review.pr-jenkins.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * FIX 4 (4vr backlog-cleanup) — CLI `--pr` flow: when the numeric repo-id
3
+ * resolution (`GET /repos/{owner}/{repo}`) fails with a NETWORK error, the
4
+ * post-back must DEGRADE gracefully:
5
+ * - it does NOT crash,
6
+ * - it warns (non-silent),
7
+ * - it proceeds with a PATH-SHAPED, opaque `RepoRef.nativeId` (`owner/repo`).
8
+ *
9
+ * This locks in the safe-degradation documented by the RepoRef.nativeId contract
10
+ * (opaque identity string, MAY be path-shaped). We mock the seams reviewCommand
11
+ * composes so the test is deterministic + offline, and capture the `RepoRef`
12
+ * handed to the post-back to assert the fallback nativeId.
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=review.pr-nativeid-fallback.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.pr-nativeid-fallback.test.d.ts","sourceRoot":"","sources":["../../src/commands/review.pr-nativeid-fallback.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}