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.
- package/LICENSE +21 -0
- package/dist/commands/__tests__/review-tui.test.js +2 -2
- package/dist/commands/__tests__/review-tui.test.js.map +1 -1
- package/dist/commands/audit.d.ts +23 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +282 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/audit.test.d.ts +10 -0
- package/dist/commands/audit.test.d.ts.map +1 -0
- package/dist/commands/audit.test.js +110 -0
- package/dist/commands/audit.test.js.map +1 -0
- package/dist/commands/dismiss.d.ts +14 -0
- package/dist/commands/dismiss.d.ts.map +1 -0
- package/dist/commands/dismiss.js +98 -0
- package/dist/commands/dismiss.js.map +1 -0
- package/dist/commands/feedback.d.ts +18 -0
- package/dist/commands/feedback.d.ts.map +1 -0
- package/dist/commands/feedback.js +122 -0
- package/dist/commands/feedback.js.map +1 -0
- package/dist/commands/login.js +3 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/login.test.js +3 -4
- package/dist/commands/login.test.js.map +1 -1
- package/dist/commands/review-commit-msg.test.js +2 -2
- package/dist/commands/review-commit-msg.test.js.map +1 -1
- package/dist/commands/review-output.test.js +30 -2
- package/dist/commands/review-output.test.js.map +1 -1
- package/dist/commands/review.d.ts +16 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +239 -3
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/review.mr-exit.test.d.ts +13 -0
- package/dist/commands/review.mr-exit.test.d.ts.map +1 -0
- package/dist/commands/review.mr-exit.test.js +190 -0
- package/dist/commands/review.mr-exit.test.js.map +1 -0
- package/dist/commands/review.postback-disclosure.test.d.ts +22 -0
- package/dist/commands/review.postback-disclosure.test.d.ts.map +1 -0
- package/dist/commands/review.postback-disclosure.test.js +192 -0
- package/dist/commands/review.postback-disclosure.test.js.map +1 -0
- package/dist/commands/review.pr-exit.test.d.ts +13 -0
- package/dist/commands/review.pr-exit.test.d.ts.map +1 -0
- package/dist/commands/review.pr-exit.test.js +162 -0
- package/dist/commands/review.pr-exit.test.js.map +1 -0
- package/dist/commands/review.pr-jenkins.test.d.ts +16 -0
- package/dist/commands/review.pr-jenkins.test.d.ts.map +1 -0
- package/dist/commands/review.pr-jenkins.test.js +106 -0
- package/dist/commands/review.pr-jenkins.test.js.map +1 -0
- package/dist/commands/review.pr-nativeid-fallback.test.d.ts +15 -0
- package/dist/commands/review.pr-nativeid-fallback.test.d.ts.map +1 -0
- package/dist/commands/review.pr-nativeid-fallback.test.js +151 -0
- package/dist/commands/review.pr-nativeid-fallback.test.js.map +1 -0
- package/dist/commands/review.test.js +48 -33
- package/dist/commands/review.test.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +15 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status.test.js +31 -10
- package/dist/commands/status.test.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +116 -23
- package/dist/index.js.map +1 -1
- package/dist/lib/cli-github-client-port.d.ts +52 -0
- package/dist/lib/cli-github-client-port.d.ts.map +1 -0
- package/dist/lib/cli-github-client-port.js +199 -0
- package/dist/lib/cli-github-client-port.js.map +1 -0
- package/dist/lib/cli-github-client-port.test.d.ts +10 -0
- package/dist/lib/cli-github-client-port.test.d.ts.map +1 -0
- package/dist/lib/cli-github-client-port.test.js +47 -0
- package/dist/lib/cli-github-client-port.test.js.map +1 -0
- package/dist/lib/cli-gitlab-client-port.d.ts +62 -0
- package/dist/lib/cli-gitlab-client-port.d.ts.map +1 -0
- package/dist/lib/cli-gitlab-client-port.js +209 -0
- package/dist/lib/cli-gitlab-client-port.js.map +1 -0
- package/dist/lib/cli-gitlab-client-port.test.d.ts +9 -0
- package/dist/lib/cli-gitlab-client-port.test.d.ts.map +1 -0
- package/dist/lib/cli-gitlab-client-port.test.js +278 -0
- package/dist/lib/cli-gitlab-client-port.test.js.map +1 -0
- package/dist/lib/config.d.ts +12 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +14 -2
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.js +22 -3
- package/dist/lib/config.test.js.map +1 -1
- package/dist/lib/forge-postback.d.ts +72 -0
- package/dist/lib/forge-postback.d.ts.map +1 -0
- package/dist/lib/forge-postback.js +39 -0
- package/dist/lib/forge-postback.js.map +1 -0
- package/dist/lib/github-api.d.ts.map +1 -1
- package/dist/lib/github-api.js +15 -6
- package/dist/lib/github-api.js.map +1 -1
- package/dist/lib/github-api.test.js +24 -1
- package/dist/lib/github-api.test.js.map +1 -1
- package/dist/lib/gitlab-api.d.ts +50 -0
- package/dist/lib/gitlab-api.d.ts.map +1 -0
- package/dist/lib/gitlab-api.js +75 -0
- package/dist/lib/gitlab-api.js.map +1 -0
- package/dist/lib/gitlab-api.test.d.ts +2 -0
- package/dist/lib/gitlab-api.test.d.ts.map +1 -0
- package/dist/lib/gitlab-api.test.js +79 -0
- package/dist/lib/gitlab-api.test.js.map +1 -0
- package/dist/lib/gitlab-e2e.manual.test.d.ts +24 -0
- package/dist/lib/gitlab-e2e.manual.test.d.ts.map +1 -0
- package/dist/lib/gitlab-e2e.manual.test.js +57 -0
- package/dist/lib/gitlab-e2e.manual.test.js.map +1 -0
- package/dist/lib/gitlab-token.d.ts +17 -0
- package/dist/lib/gitlab-token.d.ts.map +1 -0
- package/dist/lib/gitlab-token.js +24 -0
- package/dist/lib/gitlab-token.js.map +1 -0
- package/dist/lib/gitlab-token.test.d.ts +2 -0
- package/dist/lib/gitlab-token.test.d.ts.map +1 -0
- package/dist/lib/gitlab-token.test.js +38 -0
- package/dist/lib/gitlab-token.test.js.map +1 -0
- package/dist/lib/oauth.test.js +0 -1
- package/dist/lib/oauth.test.js.map +1 -1
- package/dist/lib/pr-postback.d.ts +42 -0
- package/dist/lib/pr-postback.d.ts.map +1 -0
- package/dist/lib/pr-postback.js +44 -0
- package/dist/lib/pr-postback.js.map +1 -0
- package/dist/lib/pr-postback.test.d.ts +9 -0
- package/dist/lib/pr-postback.test.d.ts.map +1 -0
- package/dist/lib/pr-postback.test.js +196 -0
- package/dist/lib/pr-postback.test.js.map +1 -0
- package/dist/lib/pr-token.d.ts +15 -0
- package/dist/lib/pr-token.d.ts.map +1 -0
- package/dist/lib/pr-token.js +22 -0
- package/dist/lib/pr-token.js.map +1 -0
- package/dist/lib/pr-token.test.d.ts +2 -0
- package/dist/lib/pr-token.test.d.ts.map +1 -0
- package/dist/lib/pr-token.test.js +25 -0
- package/dist/lib/pr-token.test.js.map +1 -0
- package/dist/lib/providers.d.ts +29 -0
- package/dist/lib/providers.d.ts.map +1 -0
- package/dist/lib/providers.js +41 -0
- package/dist/lib/providers.js.map +1 -0
- package/dist/lib/providers.test.d.ts +9 -0
- package/dist/lib/providers.test.d.ts.map +1 -0
- package/dist/lib/providers.test.js +60 -0
- package/dist/lib/providers.test.js.map +1 -0
- package/dist/ui/__tests__/format.test.js +2 -2
- package/dist/ui/__tests__/format.test.js.map +1 -1
- package/dist/ui/__tests__/theme.test.js +3 -2
- package/dist/ui/__tests__/theme.test.js.map +1 -1
- package/dist/ui/theme.d.ts.map +1 -1
- package/dist/ui/theme.js +1 -0
- package/dist/ui/theme.js.map +1 -1
- 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"}
|