@helmiq/crew-git 0.1.0 → 0.1.1
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/dist/git-tools.test.js +77 -1
- package/dist/github-client.d.ts +19 -0
- package/dist/github-client.d.ts.map +1 -1
- package/dist/github-client.js +38 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/tool-get-pr-checks.d.ts +22 -0
- package/dist/tool-get-pr-checks.d.ts.map +1 -0
- package/dist/tool-get-pr-checks.js +31 -0
- package/package.json +1 -1
package/dist/git-tools.test.js
CHANGED
|
@@ -8,7 +8,7 @@ import { gitBranch } from './tool-git-branch.js';
|
|
|
8
8
|
import { gitCommit } from './tool-git-commit.js';
|
|
9
9
|
import { gitDiff } from './tool-git-diff.js';
|
|
10
10
|
import { gitLog } from './tool-git-log.js';
|
|
11
|
-
import { createGithubPr, GitHubClientError } from './github-client.js';
|
|
11
|
+
import { createGithubPr, getPrCheckRuns, GitHubClientError } from './github-client.js';
|
|
12
12
|
describe('Git Tools', () => {
|
|
13
13
|
let repoDir;
|
|
14
14
|
beforeEach(async () => {
|
|
@@ -143,6 +143,82 @@ describe('Git Tools', () => {
|
|
|
143
143
|
base: 'main',
|
|
144
144
|
}, 'ghp_test_token')).rejects.toThrow(/PR_CREATION_FAILED|422/);
|
|
145
145
|
});
|
|
146
|
+
it('fetches check runs for a commit ref', async () => {
|
|
147
|
+
const mockResponse = {
|
|
148
|
+
total_count: 2,
|
|
149
|
+
check_runs: [
|
|
150
|
+
{ id: 1, name: 'lint', status: 'completed', conclusion: 'success' },
|
|
151
|
+
{ id: 2, name: 'test', status: 'completed', conclusion: 'success' },
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
155
|
+
ok: true,
|
|
156
|
+
json: () => Promise.resolve(mockResponse),
|
|
157
|
+
}));
|
|
158
|
+
const result = await getPrCheckRuns({ owner: 'test', repo: 'repo', ref: 'abc123' }, 'ghp_test_token');
|
|
159
|
+
expect(result.totalCount).toBe(2);
|
|
160
|
+
expect(result.checkRuns).toHaveLength(2);
|
|
161
|
+
expect(result.allCompleted).toBe(true);
|
|
162
|
+
expect(result.allPassed).toBe(true);
|
|
163
|
+
expect(result.failed).toHaveLength(0);
|
|
164
|
+
const fetchCalls = vi.mocked(fetch).mock.calls;
|
|
165
|
+
expect(fetchCalls[0][0]).toContain('/commits/abc123/check-runs');
|
|
166
|
+
});
|
|
167
|
+
it('reports failed check runs', async () => {
|
|
168
|
+
const mockResponse = {
|
|
169
|
+
total_count: 2,
|
|
170
|
+
check_runs: [
|
|
171
|
+
{ id: 1, name: 'lint', status: 'completed', conclusion: 'success' },
|
|
172
|
+
{ id: 2, name: 'test', status: 'completed', conclusion: 'failure' },
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
176
|
+
ok: true,
|
|
177
|
+
json: () => Promise.resolve(mockResponse),
|
|
178
|
+
}));
|
|
179
|
+
const result = await getPrCheckRuns({ owner: 'test', repo: 'repo', ref: 'abc123' }, 'ghp_test_token');
|
|
180
|
+
expect(result.allCompleted).toBe(true);
|
|
181
|
+
expect(result.allPassed).toBe(false);
|
|
182
|
+
expect(result.failed).toHaveLength(1);
|
|
183
|
+
expect(result.failed[0].name).toBe('test');
|
|
184
|
+
});
|
|
185
|
+
it('reports pending check runs as not completed', async () => {
|
|
186
|
+
const mockResponse = {
|
|
187
|
+
total_count: 2,
|
|
188
|
+
check_runs: [
|
|
189
|
+
{ id: 1, name: 'lint', status: 'completed', conclusion: 'success' },
|
|
190
|
+
{ id: 2, name: 'test', status: 'in_progress', conclusion: null },
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
194
|
+
ok: true,
|
|
195
|
+
json: () => Promise.resolve(mockResponse),
|
|
196
|
+
}));
|
|
197
|
+
const result = await getPrCheckRuns({ owner: 'test', repo: 'repo', ref: 'abc123' }, 'ghp_test_token');
|
|
198
|
+
expect(result.allCompleted).toBe(false);
|
|
199
|
+
expect(result.allPassed).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
it('throws CHECK_RUNS_QUERY_FAILED on non-ok response', async () => {
|
|
202
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
203
|
+
ok: false,
|
|
204
|
+
status: 404,
|
|
205
|
+
text: () => Promise.resolve('{"message":"Not Found"}'),
|
|
206
|
+
}));
|
|
207
|
+
await expect(getPrCheckRuns({ owner: 'test', repo: 'repo', ref: 'abc123' }, 'ghp_test_token')).rejects.toThrow(/CHECK_RUNS_QUERY_FAILED|404/);
|
|
208
|
+
});
|
|
209
|
+
it('treats skipped checks as passing', async () => {
|
|
210
|
+
const mockResponse = {
|
|
211
|
+
total_count: 1,
|
|
212
|
+
check_runs: [{ id: 1, name: 'deploy', status: 'completed', conclusion: 'skipped' }],
|
|
213
|
+
};
|
|
214
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
215
|
+
ok: true,
|
|
216
|
+
json: () => Promise.resolve(mockResponse),
|
|
217
|
+
}));
|
|
218
|
+
const result = await getPrCheckRuns({ owner: 'test', repo: 'repo', ref: 'abc123' }, 'ghp_test_token');
|
|
219
|
+
expect(result.allPassed).toBe(true);
|
|
220
|
+
expect(result.failed).toHaveLength(0);
|
|
221
|
+
});
|
|
146
222
|
it('sends labels in a separate request when provided', async () => {
|
|
147
223
|
const prResponse = {
|
|
148
224
|
number: 10,
|
package/dist/github-client.d.ts
CHANGED
|
@@ -19,6 +19,25 @@ export interface CreatePrResult {
|
|
|
19
19
|
htmlUrl: string;
|
|
20
20
|
}
|
|
21
21
|
export declare function createGithubPr(params: CreatePrParams, token?: string): Promise<CreatePrResult>;
|
|
22
|
+
export interface GetPrCheckRunsParams {
|
|
23
|
+
owner: string;
|
|
24
|
+
repo: string;
|
|
25
|
+
ref: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CheckRun {
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
status: 'queued' | 'in_progress' | 'completed';
|
|
31
|
+
conclusion: string | null;
|
|
32
|
+
}
|
|
33
|
+
export interface GetPrCheckRunsResult {
|
|
34
|
+
totalCount: number;
|
|
35
|
+
checkRuns: CheckRun[];
|
|
36
|
+
allCompleted: boolean;
|
|
37
|
+
allPassed: boolean;
|
|
38
|
+
failed: CheckRun[];
|
|
39
|
+
}
|
|
40
|
+
export declare function getPrCheckRuns(params: GetPrCheckRunsParams, token?: string): Promise<GetPrCheckRunsResult>;
|
|
22
41
|
export declare class GitHubClientError extends Error {
|
|
23
42
|
readonly code: string;
|
|
24
43
|
readonly statusCode?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-client.d.ts","sourceRoot":"","sources":["../src/github-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,CAAC,CAoDzB;
|
|
1
|
+
{"version":3,"file":"github-client.d.ts","sourceRoot":"","sources":["../src/github-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,CAAC,CAoDzB;AAoCD,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,WAAW,CAAC;IAC/C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,oBAAoB,EAC5B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,oBAAoB,CAAC,CA4D/B;AAMD,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAM/D"}
|
package/dist/github-client.js
CHANGED
|
@@ -62,6 +62,44 @@ async function addLabels(owner, repo, issueNumber, labels, token) {
|
|
|
62
62
|
throw new GitHubClientError(`Failed to add labels (${response.status})`, 'PR_LABELS_FAILED', response.status);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
+
export async function getPrCheckRuns(params, token) {
|
|
66
|
+
const authToken = token ?? process.env['GITHUB_TOKEN'];
|
|
67
|
+
if (!authToken) {
|
|
68
|
+
throw new GitHubClientError('GITHUB_TOKEN environment variable is not set. Required for check-run queries.', 'GITHUB_TOKEN_MISSING');
|
|
69
|
+
}
|
|
70
|
+
const response = await fetch(`${GITHUB_API}/repos/${params.owner}/${params.repo}/commits/${params.ref}/check-runs`, {
|
|
71
|
+
method: 'GET',
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `Bearer ${authToken}`,
|
|
74
|
+
Accept: 'application/vnd.github+json',
|
|
75
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
const errorBody = await response.text();
|
|
80
|
+
throw new GitHubClientError(`GitHub check-runs query failed (${response.status}): ${errorBody}`, 'CHECK_RUNS_QUERY_FAILED', response.status);
|
|
81
|
+
}
|
|
82
|
+
const data = (await response.json());
|
|
83
|
+
const checkRuns = data.check_runs.map((cr) => ({
|
|
84
|
+
id: cr.id,
|
|
85
|
+
name: cr.name,
|
|
86
|
+
status: cr.status,
|
|
87
|
+
conclusion: cr.conclusion,
|
|
88
|
+
}));
|
|
89
|
+
const allCompleted = checkRuns.length > 0 && checkRuns.every((cr) => cr.status === 'completed');
|
|
90
|
+
const failed = checkRuns.filter((cr) => cr.status === 'completed' && cr.conclusion !== 'success' && cr.conclusion !== 'skipped');
|
|
91
|
+
const allPassed = allCompleted && failed.length === 0;
|
|
92
|
+
return {
|
|
93
|
+
totalCount: data.total_count,
|
|
94
|
+
checkRuns,
|
|
95
|
+
allCompleted,
|
|
96
|
+
allPassed,
|
|
97
|
+
failed,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Errors
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
65
103
|
export class GitHubClientError extends Error {
|
|
66
104
|
code;
|
|
67
105
|
statusCode;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { validateBranchName, BranchConventionError } from './branch-conventions.js';
|
|
2
|
-
export { createGithubPr, GitHubClientError } from './github-client.js';
|
|
3
|
-
export type { CreatePrParams, CreatePrResult } from './github-client.js';
|
|
2
|
+
export { createGithubPr, getPrCheckRuns, GitHubClientError } from './github-client.js';
|
|
3
|
+
export type { CreatePrParams, CreatePrResult, CheckRun } from './github-client.js';
|
|
4
|
+
export { getPrChecks } from './tool-get-pr-checks.js';
|
|
5
|
+
export type { GetPrChecksParams, GetPrChecksResult } from './tool-get-pr-checks.js';
|
|
4
6
|
export { gitBranch, GitToolError } from './tool-git-branch.js';
|
|
5
7
|
export type { GitBranchParams, GitBranchResult } from './tool-git-branch.js';
|
|
6
8
|
export { gitCommit } from './tool-git-commit.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvF,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACpF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { validateBranchName, BranchConventionError } from './branch-conventions.js';
|
|
2
|
-
export { createGithubPr, GitHubClientError } from './github-client.js';
|
|
2
|
+
export { createGithubPr, getPrCheckRuns, GitHubClientError } from './github-client.js';
|
|
3
|
+
export { getPrChecks } from './tool-get-pr-checks.js';
|
|
3
4
|
export { gitBranch, GitToolError } from './tool-git-branch.js';
|
|
4
5
|
export { gitCommit } from './tool-git-commit.js';
|
|
5
6
|
export { gitPush } from './tool-git-push.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GetPrCheckRunsParams, GetPrCheckRunsResult } from './github-client.js';
|
|
2
|
+
export type { GetPrCheckRunsParams, GetPrCheckRunsResult };
|
|
3
|
+
export interface GetPrChecksParams {
|
|
4
|
+
owner: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
ref: string;
|
|
7
|
+
/** Maximum seconds to wait for all checks to complete. Defaults to 120. */
|
|
8
|
+
timeoutSeconds?: number;
|
|
9
|
+
/** Seconds between poll attempts. Defaults to 15. */
|
|
10
|
+
intervalSeconds?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface GetPrChecksResult extends GetPrCheckRunsResult {
|
|
13
|
+
timedOut: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Poll GitHub check runs for a commit ref until all complete or timeout.
|
|
17
|
+
*
|
|
18
|
+
* Returns the final status, including whether polling timed out and
|
|
19
|
+
* which check runs (if any) failed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getPrChecks(params: GetPrChecksParams, token?: string): Promise<GetPrChecksResult>;
|
|
22
|
+
//# sourceMappingURL=tool-get-pr-checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-get-pr-checks.d.ts","sourceRoot":"","sources":["../src/tool-get-pr-checks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAErF,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAkB,SAAQ,oBAAoB;IAC7D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAKD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,iBAAiB,EACzB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC,CAsB5B"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getPrCheckRuns } from './github-client.js';
|
|
2
|
+
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
3
|
+
const DEFAULT_INTERVAL_SECONDS = 15;
|
|
4
|
+
/**
|
|
5
|
+
* Poll GitHub check runs for a commit ref until all complete or timeout.
|
|
6
|
+
*
|
|
7
|
+
* Returns the final status, including whether polling timed out and
|
|
8
|
+
* which check runs (if any) failed.
|
|
9
|
+
*/
|
|
10
|
+
export async function getPrChecks(params, token) {
|
|
11
|
+
const timeout = (params.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS) * 1000;
|
|
12
|
+
const interval = (params.intervalSeconds ?? DEFAULT_INTERVAL_SECONDS) * 1000;
|
|
13
|
+
const deadline = Date.now() + timeout;
|
|
14
|
+
const apiParams = {
|
|
15
|
+
owner: params.owner,
|
|
16
|
+
repo: params.repo,
|
|
17
|
+
ref: params.ref,
|
|
18
|
+
};
|
|
19
|
+
let result = await getPrCheckRuns(apiParams, token);
|
|
20
|
+
while (!result.allCompleted && Date.now() < deadline) {
|
|
21
|
+
await sleep(Math.min(interval, deadline - Date.now()));
|
|
22
|
+
result = await getPrCheckRuns(apiParams, token);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
...result,
|
|
26
|
+
timedOut: !result.allCompleted,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function sleep(ms) {
|
|
30
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
|
+
}
|