ira-review 2.0.1 → 3.0.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/README.github.md +7 -6
- package/README.md +16 -52
- package/README.npm.md +16 -52
- package/dist/{bitbucket-TQU3E6SG.js → bitbucket-TXGRB3VV.js} +2 -2
- package/dist/{chunk-AHCFDKK4.js → chunk-AFLVYFZ2.js} +37 -1
- package/dist/{chunk-NIAFIXU4.js → chunk-SC7RXB4Y.js} +25 -10
- package/dist/{chunk-XOR6EFZE.js → chunk-ZKADAXVW.js} +23 -10
- package/dist/cli.js +325 -85
- package/dist/{github-Q46RU3UR.js → github-65TBQVFD.js} +2 -2
- package/dist/index.cjs +398 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +391 -82
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.github.md
CHANGED
|
@@ -39,7 +39,7 @@ IRA runs a 13-step pipeline for each review. Every step after step 1 is designed
|
|
|
39
39
|
7. Fetch source files for changed files (for full-file context)
|
|
40
40
|
8. Run AI review on each file/issue (concurrent, configurable model)
|
|
41
41
|
9. Calculate risk score (0-100) from issue severity, complexity, and security signals
|
|
42
|
-
10.
|
|
42
|
+
10. Automatically validate or generate JIRA acceptance criteria (if configured)
|
|
43
43
|
11. Deduplicate: skip issues already commented on in previous runs
|
|
44
44
|
12. Post summary + inline comments to the PR
|
|
45
45
|
13. Send Slack/Teams notification (if configured, respects risk threshold)
|
|
@@ -98,7 +98,7 @@ flowchart LR
|
|
|
98
98
|
|
|
99
99
|
```
|
|
100
100
|
src/
|
|
101
|
-
ai/ AI provider abstraction (OpenAI, Anthropic, Azure, Ollama)
|
|
101
|
+
ai/ AI provider abstraction (OpenAI, Anthropic, Azure, Ollama, AMP)
|
|
102
102
|
core/ Review engine, risk scorer, acceptance validator, test generator
|
|
103
103
|
scm/ GitHub and Bitbucket clients (diff, comments, labels, build status)
|
|
104
104
|
integrations/ JIRA client, Slack/Teams notifier
|
|
@@ -182,7 +182,7 @@ Each rule has a `message` (what to tell the developer), a `severity` (BLOCKER, C
|
|
|
182
182
|
}
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Rules without `paths` apply to all files. Rules with `paths` are only checked against matching files. The file is validated at load time: invalid severity values and missing required fields are skipped with a warning. Maximum
|
|
185
|
+
Rules without `paths` apply to all files. Rules with `paths` are only checked against matching files. The file is validated at load time: invalid severity values and missing required fields are skipped with a warning. Maximum 100 rules per file. IRA rules are for nuanced, context-dependent standards that linters cannot express. Deterministic checks (naming conventions, import order, formatting) belong in ESLint.
|
|
186
186
|
|
|
187
187
|
Rules are enforced in all review surfaces (CLI, CI/CD, VS Code extension) with no license gating. In the VS Code extension, run `IRA: Init Rules File` from the command palette to scaffold an empty `.ira-rules.json`. The extension ships a JSON Schema for the file, so you get autocomplete and validation as you edit.
|
|
188
188
|
|
|
@@ -210,7 +210,7 @@ IRA is not a SaaS product. There is no hosted service, no telemetry, no analytic
|
|
|
210
210
|
| | CLI | VS Code Extension |
|
|
211
211
|
|---|---|---|
|
|
212
212
|
| **Use case** | CI pipelines, scripting, headless environments | Interactive development |
|
|
213
|
-
| **AI default** | OpenAI (requires API key) | GitHub Copilot (zero config) |
|
|
213
|
+
| **AI default** | OpenAI (requires API key) | GitHub Copilot (zero config), AMP CLI also supported |
|
|
214
214
|
| **Auth** | Environment variables or CLI flags | VS Code OAuth + OS keychain |
|
|
215
215
|
| **Output** | Terminal + PR comments | Inline diagnostics, CodeLens, TreeView, risk badge |
|
|
216
216
|
| **JIRA/Sonar** | CLI flags or env vars | VS Code settings |
|
|
@@ -282,7 +282,7 @@ Suggested Fix: Use parameterized queries:
|
|
|
282
282
|
3. `Cmd+Shift+P` > `IRA: Review Current PR`
|
|
283
283
|
4. Enter your PR number
|
|
284
284
|
|
|
285
|
-
If you have GitHub Copilot, that is all you need. No API keys, no configuration.
|
|
285
|
+
If you have GitHub Copilot, that is all you need. No API keys, no configuration. Alternatively, set the AI provider to `amp` if you have the AMP CLI installed (`amp login`).
|
|
286
286
|
|
|
287
287
|
### CLI
|
|
288
288
|
|
|
@@ -393,6 +393,7 @@ npx ira-review review \
|
|
|
393
393
|
| Provider | Notes |
|
|
394
394
|
|---|---|
|
|
395
395
|
| GitHub Copilot | VS Code only, zero config, uses existing session |
|
|
396
|
+
| AMP CLI | VS Code only, requires `amp` CLI installed and authenticated (`amp login`) |
|
|
396
397
|
| OpenAI | Default for CLI |
|
|
397
398
|
| Azure OpenAI | Requires `--ai-base-url` and `--ai-deployment` |
|
|
398
399
|
| Anthropic | Pass key with `--ai-api-key` |
|
|
@@ -420,7 +421,7 @@ CLI flags override environment variables, which override the config file. Token
|
|
|
420
421
|
## Requirements
|
|
421
422
|
|
|
422
423
|
- Node.js 18+
|
|
423
|
-
- An AI provider API key (or Ollama running locally, or GitHub Copilot for the VS Code extension)
|
|
424
|
+
- An AI provider API key (or Ollama running locally, or GitHub Copilot / AMP CLI for the VS Code extension)
|
|
424
425
|
- A GitHub or Bitbucket repo with an open PR
|
|
425
426
|
|
|
426
427
|
## License
|
package/README.md
CHANGED
|
@@ -15,55 +15,19 @@ No install required. Drop `--dry-run` to post comments directly on the PR. For B
|
|
|
15
15
|
## What You Get
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Fix: BEFORE: `db.query(`SELECT * FROM users WHERE name = ${username}`)`
|
|
32
|
-
→ AFTER: `db.query('SELECT * FROM users WHERE name = $1', [username])`
|
|
33
|
-
|
|
34
|
-
────────────────────────────────────────────────────────────
|
|
35
|
-
📄 src/middleware/cors.ts:8
|
|
36
|
-
Rule: IRA/error-handling (MAJOR)
|
|
37
|
-
Message: Empty catch block swallows CORS validation errors
|
|
38
|
-
Explain: fetch() failure in CORS preflight is caught and ignored,
|
|
39
|
-
leaving the request in an undefined state.
|
|
40
|
-
Impact: Silent CORS failures in production with no logging.
|
|
41
|
-
Fix: BEFORE: `} catch {}`
|
|
42
|
-
→ AFTER: `} catch (err) { logger.error('CORS preflight failed', err); throw err; }`
|
|
43
|
-
|
|
44
|
-
# 🔍 IRA Review Summary
|
|
45
|
-
|
|
46
|
-
## 🟡 Risk: MEDIUM (38/100)
|
|
47
|
-
|
|
48
|
-
| Metric | Value |
|
|
49
|
-
|---------------|----------|
|
|
50
|
-
| Review mode | AI-only |
|
|
51
|
-
| Total issues | 3 |
|
|
52
|
-
| Reviewed (AI) | 3 |
|
|
53
|
-
| Framework | react |
|
|
54
|
-
|
|
55
|
-
## ✅ Requirements: AUTH-234 — 83% Complete (5/6)
|
|
56
|
-
|
|
57
|
-
✅ OAuth2 login flow implemented with Google provider
|
|
58
|
-
✅ JWT tokens generated on successful authentication
|
|
59
|
-
✅ Refresh token rotation with 7-day expiry
|
|
60
|
-
❌ Input validation on login endpoint — no email format check
|
|
61
|
-
✅ Logout endpoint clears session and revokes token
|
|
62
|
-
✅ Rate limiting on login attempts
|
|
63
|
-
|
|
64
|
-
⚠️ Edge Cases Not Covered
|
|
65
|
-
- What happens when Google OAuth is unreachable?
|
|
66
|
-
- Token refresh during concurrent requests?
|
|
18
|
+
IRA: Found 3 issues (Risk: MEDIUM - 47/100)
|
|
19
|
+
|
|
20
|
+
src/routes/todos.ts
|
|
21
|
+
[BLOCKER] SQL injection risk - user input passed directly to query
|
|
22
|
+
[MAJOR] Missing database index on frequently queried column
|
|
23
|
+
|
|
24
|
+
src/middleware/auth.ts
|
|
25
|
+
[CRITICAL] JWT secret hardcoded - move to environment variable
|
|
26
|
+
|
|
27
|
+
JIRA AC Validation (PROJ-1234):
|
|
28
|
+
AC 1: User can create a todo item COVERED
|
|
29
|
+
AC 2: Input is validated before save NOT COVERED
|
|
30
|
+
AC 3: Error returns 422 with details COVERED
|
|
67
31
|
```
|
|
68
32
|
|
|
69
33
|
Each issue is posted as an inline comment on the exact PR line with explanation, impact, and a minimal BEFORE → AFTER fix.
|
|
@@ -114,7 +78,7 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
|
|
|
114
78
|
**Rules:**
|
|
115
79
|
- `message` + `severity` required. `bad`/`good` examples and `paths` are optional.
|
|
116
80
|
- Rules without `paths` apply to all files. Rules with `paths` match only those directories.
|
|
117
|
-
- Maximum
|
|
81
|
+
- Maximum 100 rules. Deterministic checks (naming, formatting) belong in ESLint.
|
|
118
82
|
- Invalid rules are skipped with a warning, not a crash.
|
|
119
83
|
- No license gating. Works in CLI, CI/CD, and VS Code extension.
|
|
120
84
|
|
|
@@ -208,12 +172,12 @@ CLI flags override env vars, which override the config file. Token fields are bl
|
|
|
208
172
|
|
|
209
173
|
**SCM:** GitHub, GitHub Enterprise, Bitbucket Cloud, Bitbucket Server/Data Center
|
|
210
174
|
|
|
211
|
-
**AI:** OpenAI (default), Azure OpenAI, Anthropic, Ollama (local, no key needed)
|
|
175
|
+
**AI:** OpenAI (default), Azure OpenAI, Anthropic, Ollama (local, no key needed), AMP CLI (VS Code extension)
|
|
212
176
|
|
|
213
177
|
## Requirements
|
|
214
178
|
|
|
215
179
|
- Node.js 18+
|
|
216
|
-
- An AI provider API key (or Ollama running locally)
|
|
180
|
+
- An AI provider API key (or Ollama running locally, or AMP CLI / GitHub Copilot for the VS Code extension)
|
|
217
181
|
|
|
218
182
|
## Security
|
|
219
183
|
|
package/README.npm.md
CHANGED
|
@@ -15,55 +15,19 @@ No install required. Drop `--dry-run` to post comments directly on the PR. For B
|
|
|
15
15
|
## What You Get
|
|
16
16
|
|
|
17
17
|
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Fix: BEFORE: `db.query(`SELECT * FROM users WHERE name = ${username}`)`
|
|
32
|
-
→ AFTER: `db.query('SELECT * FROM users WHERE name = $1', [username])`
|
|
33
|
-
|
|
34
|
-
────────────────────────────────────────────────────────────
|
|
35
|
-
📄 src/middleware/cors.ts:8
|
|
36
|
-
Rule: IRA/error-handling (MAJOR)
|
|
37
|
-
Message: Empty catch block swallows CORS validation errors
|
|
38
|
-
Explain: fetch() failure in CORS preflight is caught and ignored,
|
|
39
|
-
leaving the request in an undefined state.
|
|
40
|
-
Impact: Silent CORS failures in production with no logging.
|
|
41
|
-
Fix: BEFORE: `} catch {}`
|
|
42
|
-
→ AFTER: `} catch (err) { logger.error('CORS preflight failed', err); throw err; }`
|
|
43
|
-
|
|
44
|
-
# 🔍 IRA Review Summary
|
|
45
|
-
|
|
46
|
-
## 🟡 Risk: MEDIUM (38/100)
|
|
47
|
-
|
|
48
|
-
| Metric | Value |
|
|
49
|
-
|---------------|----------|
|
|
50
|
-
| Review mode | AI-only |
|
|
51
|
-
| Total issues | 3 |
|
|
52
|
-
| Reviewed (AI) | 3 |
|
|
53
|
-
| Framework | react |
|
|
54
|
-
|
|
55
|
-
## ✅ Requirements: AUTH-234 — 83% Complete (5/6)
|
|
56
|
-
|
|
57
|
-
✅ OAuth2 login flow implemented with Google provider
|
|
58
|
-
✅ JWT tokens generated on successful authentication
|
|
59
|
-
✅ Refresh token rotation with 7-day expiry
|
|
60
|
-
❌ Input validation on login endpoint — no email format check
|
|
61
|
-
✅ Logout endpoint clears session and revokes token
|
|
62
|
-
✅ Rate limiting on login attempts
|
|
63
|
-
|
|
64
|
-
⚠️ Edge Cases Not Covered
|
|
65
|
-
- What happens when Google OAuth is unreachable?
|
|
66
|
-
- Token refresh during concurrent requests?
|
|
18
|
+
IRA: Found 3 issues (Risk: MEDIUM - 47/100)
|
|
19
|
+
|
|
20
|
+
src/routes/todos.ts
|
|
21
|
+
[BLOCKER] SQL injection risk - user input passed directly to query
|
|
22
|
+
[MAJOR] Missing database index on frequently queried column
|
|
23
|
+
|
|
24
|
+
src/middleware/auth.ts
|
|
25
|
+
[CRITICAL] JWT secret hardcoded - move to environment variable
|
|
26
|
+
|
|
27
|
+
JIRA AC Validation (PROJ-1234):
|
|
28
|
+
AC 1: User can create a todo item COVERED
|
|
29
|
+
AC 2: Input is validated before save NOT COVERED
|
|
30
|
+
AC 3: Error returns 422 with details COVERED
|
|
67
31
|
```
|
|
68
32
|
|
|
69
33
|
Each issue is posted as an inline comment on the exact PR line with explanation, impact, and a minimal BEFORE → AFTER fix.
|
|
@@ -114,7 +78,7 @@ Commit a `.ira-rules.json` to your repo root. Rules are injected into the AI pro
|
|
|
114
78
|
**Rules:**
|
|
115
79
|
- `message` + `severity` required. `bad`/`good` examples and `paths` are optional.
|
|
116
80
|
- Rules without `paths` apply to all files. Rules with `paths` match only those directories.
|
|
117
|
-
- Maximum
|
|
81
|
+
- Maximum 100 rules. Deterministic checks (naming, formatting) belong in ESLint.
|
|
118
82
|
- Invalid rules are skipped with a warning, not a crash.
|
|
119
83
|
- No license gating. Works in CLI, CI/CD, and VS Code extension.
|
|
120
84
|
|
|
@@ -208,12 +172,12 @@ CLI flags override env vars, which override the config file. Token fields are bl
|
|
|
208
172
|
|
|
209
173
|
**SCM:** GitHub, GitHub Enterprise, Bitbucket Cloud, Bitbucket Server/Data Center
|
|
210
174
|
|
|
211
|
-
**AI:** OpenAI (default), Azure OpenAI, Anthropic, Ollama (local, no key needed)
|
|
175
|
+
**AI:** OpenAI (default), Azure OpenAI, Anthropic, Ollama (local, no key needed), AMP CLI (VS Code extension)
|
|
212
176
|
|
|
213
177
|
## Requirements
|
|
214
178
|
|
|
215
179
|
- Node.js 18+
|
|
216
|
-
- An AI provider API key (or Ollama running locally)
|
|
180
|
+
- An AI provider API key (or Ollama running locally, or AMP CLI / GitHub Copilot for the VS Code extension)
|
|
217
181
|
|
|
218
182
|
## Security
|
|
219
183
|
|
|
@@ -88,6 +88,41 @@ async function fetchWithTimeout(url, init = {}, timeoutMs = 3e4) {
|
|
|
88
88
|
clearTimeout(timer);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
function parseApiError(status, body, provider) {
|
|
92
|
+
const statusMessages = {
|
|
93
|
+
400: "Bad request",
|
|
94
|
+
401: "Authentication failed \u2014 check your token or credentials",
|
|
95
|
+
403: "Access denied \u2014 you may not have permission for this resource",
|
|
96
|
+
404: "Not found \u2014 check the URL, project key, or PR number",
|
|
97
|
+
408: "Request timed out \u2014 try again in a moment",
|
|
98
|
+
409: "Conflict \u2014 the resource may have been modified",
|
|
99
|
+
422: "Invalid request \u2014 the server couldn't process it",
|
|
100
|
+
429: "Rate limited \u2014 too many requests, try again shortly",
|
|
101
|
+
500: "Server error \u2014 the service is having issues",
|
|
102
|
+
502: "Bad gateway \u2014 the service is temporarily unavailable",
|
|
103
|
+
503: "Service unavailable \u2014 try again in a moment",
|
|
104
|
+
504: "Gateway timeout \u2014 the service took too long to respond"
|
|
105
|
+
};
|
|
106
|
+
const friendlyStatus = statusMessages[status] ?? `HTTP ${status}`;
|
|
107
|
+
const trimmed = body.trim();
|
|
108
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
109
|
+
try {
|
|
110
|
+
const json = JSON.parse(trimmed);
|
|
111
|
+
const msg = json.message ?? json.error?.message ?? json.error ?? json.errors?.[0]?.message ?? json.detail;
|
|
112
|
+
if (typeof msg === "string" && msg.length > 0 && msg.length < 200) {
|
|
113
|
+
return `${provider} (${status}): ${msg}`;
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (trimmed.startsWith("<!") || trimmed.startsWith("<html") || trimmed.includes("<body")) {
|
|
119
|
+
return `${provider} (${status}): ${friendlyStatus}`;
|
|
120
|
+
}
|
|
121
|
+
if (trimmed.length > 0 && trimmed.length < 150) {
|
|
122
|
+
return `${provider} (${status}): ${trimmed}`;
|
|
123
|
+
}
|
|
124
|
+
return `${provider} (${status}): ${friendlyStatus}`;
|
|
125
|
+
}
|
|
91
126
|
function sleep(ms) {
|
|
92
127
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
93
128
|
}
|
|
@@ -95,5 +130,6 @@ function sleep(ms) {
|
|
|
95
130
|
export {
|
|
96
131
|
RetryableError,
|
|
97
132
|
withRetry,
|
|
98
|
-
fetchWithTimeout
|
|
133
|
+
fetchWithTimeout,
|
|
134
|
+
parseApiError
|
|
99
135
|
};
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
RetryableError,
|
|
4
4
|
fetchWithTimeout,
|
|
5
|
+
parseApiError,
|
|
5
6
|
withRetry
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AFLVYFZ2.js";
|
|
7
8
|
|
|
8
9
|
// src/scm/github.ts
|
|
9
10
|
var GitHubClient = class {
|
|
@@ -48,12 +49,26 @@ var GitHubClient = class {
|
|
|
48
49
|
if (!response.ok) {
|
|
49
50
|
const text = await response.text();
|
|
50
51
|
throw new RetryableError(
|
|
51
|
-
|
|
52
|
+
parseApiError(response.status, text, "GitHub"),
|
|
52
53
|
response.status
|
|
53
54
|
);
|
|
54
55
|
}
|
|
55
56
|
});
|
|
56
57
|
}
|
|
58
|
+
async getIssueComments(pullRequestId) {
|
|
59
|
+
const bodies = [];
|
|
60
|
+
let page = 1;
|
|
61
|
+
while (true) {
|
|
62
|
+
const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/issues/${pullRequestId}/comments?per_page=100&page=${page}`;
|
|
63
|
+
const response = await fetchWithTimeout(url, { headers: this.headers });
|
|
64
|
+
if (!response.ok) break;
|
|
65
|
+
const comments = await response.json();
|
|
66
|
+
for (const c of comments) bodies.push(c.body);
|
|
67
|
+
if (comments.length < 100) break;
|
|
68
|
+
page++;
|
|
69
|
+
}
|
|
70
|
+
return bodies;
|
|
71
|
+
}
|
|
57
72
|
async getPRState(pullRequestId) {
|
|
58
73
|
const url = `${this.baseUrl}/repos/${this.owner}/${this.repo}/pulls/${pullRequestId}`;
|
|
59
74
|
const response = await fetchWithTimeout(url, { headers: this.headers });
|
|
@@ -81,7 +96,7 @@ var GitHubClient = class {
|
|
|
81
96
|
if (!response.ok) {
|
|
82
97
|
const text = await response.text();
|
|
83
98
|
throw new RetryableError(
|
|
84
|
-
|
|
99
|
+
parseApiError(response.status, text, "GitHub"),
|
|
85
100
|
response.status
|
|
86
101
|
);
|
|
87
102
|
}
|
|
@@ -97,7 +112,7 @@ var GitHubClient = class {
|
|
|
97
112
|
const response = await fetchWithTimeout(url, { headers: this.headers });
|
|
98
113
|
if (!response.ok) {
|
|
99
114
|
const text = await response.text();
|
|
100
|
-
throw new RetryableError(
|
|
115
|
+
throw new RetryableError(parseApiError(response.status, text, "GitHub"), response.status);
|
|
101
116
|
}
|
|
102
117
|
return response.json();
|
|
103
118
|
});
|
|
@@ -125,7 +140,7 @@ ${file.patch}`;
|
|
|
125
140
|
if (!response.ok) {
|
|
126
141
|
const text = await response.text();
|
|
127
142
|
throw new RetryableError(
|
|
128
|
-
|
|
143
|
+
parseApiError(response.status, text, "GitHub"),
|
|
129
144
|
response.status
|
|
130
145
|
);
|
|
131
146
|
}
|
|
@@ -154,7 +169,7 @@ ${file.patch}`;
|
|
|
154
169
|
if (!response.ok) {
|
|
155
170
|
const text = await response.text();
|
|
156
171
|
throw new RetryableError(
|
|
157
|
-
|
|
172
|
+
parseApiError(response.status, text, "GitHub"),
|
|
158
173
|
response.status
|
|
159
174
|
);
|
|
160
175
|
}
|
|
@@ -171,7 +186,7 @@ ${file.patch}`;
|
|
|
171
186
|
if (!response.ok) {
|
|
172
187
|
const text = await response.text();
|
|
173
188
|
throw new RetryableError(
|
|
174
|
-
|
|
189
|
+
parseApiError(response.status, text, "GitHub"),
|
|
175
190
|
response.status
|
|
176
191
|
);
|
|
177
192
|
}
|
|
@@ -188,7 +203,7 @@ ${file.patch}`;
|
|
|
188
203
|
if (!response.ok) {
|
|
189
204
|
const text = await response.text();
|
|
190
205
|
throw new RetryableError(
|
|
191
|
-
|
|
206
|
+
parseApiError(response.status, text, "GitHub"),
|
|
192
207
|
response.status
|
|
193
208
|
);
|
|
194
209
|
}
|
|
@@ -221,7 +236,7 @@ ${file.patch}`;
|
|
|
221
236
|
if (!response.ok && response.status !== 422) {
|
|
222
237
|
const text = await response.text();
|
|
223
238
|
throw new RetryableError(
|
|
224
|
-
|
|
239
|
+
parseApiError(response.status, text, "GitHub"),
|
|
225
240
|
response.status
|
|
226
241
|
);
|
|
227
242
|
}
|
|
@@ -258,7 +273,7 @@ ${file.patch}`;
|
|
|
258
273
|
if (!response.ok) {
|
|
259
274
|
const text = await response.text();
|
|
260
275
|
throw new RetryableError(
|
|
261
|
-
|
|
276
|
+
parseApiError(response.status, text, "GitHub"),
|
|
262
277
|
response.status
|
|
263
278
|
);
|
|
264
279
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
RetryableError,
|
|
4
4
|
fetchWithTimeout,
|
|
5
|
+
parseApiError,
|
|
5
6
|
withRetry
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AFLVYFZ2.js";
|
|
7
8
|
|
|
8
9
|
// src/scm/bitbucket.ts
|
|
9
10
|
var BitbucketClient = class {
|
|
@@ -55,14 +56,14 @@ var BitbucketClient = class {
|
|
|
55
56
|
if (!retryResponse.ok) {
|
|
56
57
|
const retryText = await retryResponse.text();
|
|
57
58
|
throw new RetryableError(
|
|
58
|
-
|
|
59
|
+
parseApiError(retryResponse.status, retryText, "Bitbucket"),
|
|
59
60
|
retryResponse.status
|
|
60
61
|
);
|
|
61
62
|
}
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
throw new RetryableError(
|
|
65
|
-
|
|
66
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
66
67
|
response.status
|
|
67
68
|
);
|
|
68
69
|
}
|
|
@@ -82,12 +83,24 @@ var BitbucketClient = class {
|
|
|
82
83
|
if (!response.ok) {
|
|
83
84
|
const text = await response.text();
|
|
84
85
|
throw new RetryableError(
|
|
85
|
-
|
|
86
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
86
87
|
response.status
|
|
87
88
|
);
|
|
88
89
|
}
|
|
89
90
|
});
|
|
90
91
|
}
|
|
92
|
+
async getIssueComments(pullRequestId) {
|
|
93
|
+
const bodies = [];
|
|
94
|
+
let url = `${this.baseUrl}/repositories/${this.workspace}/${this.repoSlug}/pullrequests/${pullRequestId}/comments?pagelen=100`;
|
|
95
|
+
while (url) {
|
|
96
|
+
const response = await fetchWithTimeout(url, { headers: this.headers });
|
|
97
|
+
if (!response.ok) break;
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
for (const c of data.values) bodies.push(c.content.raw);
|
|
100
|
+
url = data.next;
|
|
101
|
+
}
|
|
102
|
+
return bodies;
|
|
103
|
+
}
|
|
91
104
|
async getFileContent(filePath, pullRequestId) {
|
|
92
105
|
const sourceHash = await this.getSourceHash(pullRequestId);
|
|
93
106
|
const encodedPath = filePath.split("/").map(encodeURIComponent).join("/");
|
|
@@ -99,7 +112,7 @@ var BitbucketClient = class {
|
|
|
99
112
|
if (!response.ok) {
|
|
100
113
|
const text = await response.text();
|
|
101
114
|
throw new RetryableError(
|
|
102
|
-
|
|
115
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
103
116
|
response.status
|
|
104
117
|
);
|
|
105
118
|
}
|
|
@@ -131,7 +144,7 @@ var BitbucketClient = class {
|
|
|
131
144
|
if (!response.ok) {
|
|
132
145
|
const text = await response.text();
|
|
133
146
|
throw new RetryableError(
|
|
134
|
-
|
|
147
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
135
148
|
response.status
|
|
136
149
|
);
|
|
137
150
|
}
|
|
@@ -147,7 +160,7 @@ var BitbucketClient = class {
|
|
|
147
160
|
const response = await fetchWithTimeout(nextUrl, { headers: this.headers });
|
|
148
161
|
if (!response.ok) {
|
|
149
162
|
const text = await response.text();
|
|
150
|
-
throw new RetryableError(
|
|
163
|
+
throw new RetryableError(parseApiError(response.status, text, "Bitbucket"), response.status);
|
|
151
164
|
}
|
|
152
165
|
return response.json();
|
|
153
166
|
});
|
|
@@ -166,7 +179,7 @@ var BitbucketClient = class {
|
|
|
166
179
|
const response = await fetchWithTimeout(url, { headers: this.headers }, 15e3);
|
|
167
180
|
if (!response.ok) {
|
|
168
181
|
const text = await response.text();
|
|
169
|
-
throw new RetryableError(
|
|
182
|
+
throw new RetryableError(parseApiError(response.status, text, "Bitbucket"), response.status);
|
|
170
183
|
}
|
|
171
184
|
return response.text();
|
|
172
185
|
});
|
|
@@ -189,7 +202,7 @@ var BitbucketClient = class {
|
|
|
189
202
|
if (!response.ok) {
|
|
190
203
|
const text = await response.text();
|
|
191
204
|
throw new RetryableError(
|
|
192
|
-
|
|
205
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
193
206
|
response.status
|
|
194
207
|
);
|
|
195
208
|
}
|
|
@@ -218,7 +231,7 @@ var BitbucketClient = class {
|
|
|
218
231
|
if (!response.ok) {
|
|
219
232
|
const text = await response.text();
|
|
220
233
|
throw new RetryableError(
|
|
221
|
-
|
|
234
|
+
parseApiError(response.status, text, "Bitbucket"),
|
|
222
235
|
response.status
|
|
223
236
|
);
|
|
224
237
|
}
|