@trim21/personal-pi-extensions 0.0.183 → 0.0.185
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/package.json +1 -1
- package/src/gh-readonly.ts +117 -11
package/package.json
CHANGED
package/src/gh-readonly.ts
CHANGED
|
@@ -206,11 +206,22 @@ const jobRunSchema = Type.Object({
|
|
|
206
206
|
name: Type.String(),
|
|
207
207
|
status: Type.String(),
|
|
208
208
|
conclusion: Type.Union([Type.String(), Type.Null()]),
|
|
209
|
+
html_url: Type.Optional(Type.String()),
|
|
209
210
|
steps: Type.Array(stepSchema),
|
|
210
211
|
});
|
|
211
212
|
|
|
212
213
|
const jobsResponseSchema = Type.Object({ jobs: Type.Array(jobRunSchema) });
|
|
213
214
|
|
|
215
|
+
const prHeadSchema = Type.Object({ headRefOid: Type.String() });
|
|
216
|
+
|
|
217
|
+
const workflowRunSchema = Type.Object({
|
|
218
|
+
id: Type.Number(),
|
|
219
|
+
name: Type.String(),
|
|
220
|
+
html_url: Type.String(),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const workflowRunsSchema = Type.Object({ workflow_runs: Type.Array(workflowRunSchema) });
|
|
224
|
+
|
|
214
225
|
function truncate(
|
|
215
226
|
text: string,
|
|
216
227
|
maxLines = 2000,
|
|
@@ -401,6 +412,15 @@ async function resolveRepo(
|
|
|
401
412
|
return nameWithOwner;
|
|
402
413
|
}
|
|
403
414
|
|
|
415
|
+
/** Job conclusions that count as "did not succeed" for CI result reporting. */
|
|
416
|
+
const FAILED_JOB_CONCLUSIONS = new Set([
|
|
417
|
+
"failure",
|
|
418
|
+
"timed_out",
|
|
419
|
+
"action_required",
|
|
420
|
+
"startup_failure",
|
|
421
|
+
"cancelled",
|
|
422
|
+
]);
|
|
423
|
+
|
|
404
424
|
export function statusIcon(conclusion: string | null): string {
|
|
405
425
|
switch (conclusion) {
|
|
406
426
|
case "success": {
|
|
@@ -1311,29 +1331,115 @@ export default function ghReadonlyTools(pi: ExtensionAPI) {
|
|
|
1311
1331
|
const args = ["pr", "checks", String(number), ...repoArgs(repo), "--watch"];
|
|
1312
1332
|
if (fail_fast) args.push("--fail-fast");
|
|
1313
1333
|
|
|
1334
|
+
// gh 的退出码语义不可靠:checks 失败时返回 exit 1(SilentError),挂起时
|
|
1335
|
+
// 返回 exit 8(PendingError),且失败详情只在非结构化的 stdout 表格里。
|
|
1336
|
+
// 因此 watch 退出后直接用 Actions API 抓取该 PR head 提交关联的所有
|
|
1337
|
+
// workflow job,以 job 的真实 conclusion 为准判断成功/失败。
|
|
1314
1338
|
const result = await runGh(args, { cwd: ctx.cwd, signal, timeout: 600_000 });
|
|
1339
|
+
if (result.killed) {
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
result.reason === "timeout"
|
|
1342
|
+
? "gh pr checks --watch timed out after 10 minutes"
|
|
1343
|
+
: "gh pr checks --watch was aborted",
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1315
1346
|
|
|
1316
|
-
const
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1347
|
+
const effectiveRepo = await resolveRepo(repo, signal, ctx.cwd, params);
|
|
1348
|
+
const prOut = await ghExec(
|
|
1349
|
+
["pr", "view", String(number), "--repo", effectiveRepo, "--json", "headRefOid"],
|
|
1350
|
+
{ cwd: ctx.cwd, signal, input: params },
|
|
1351
|
+
);
|
|
1352
|
+
const { headRefOid } = Value.Parse(prHeadSchema, JSON.parse(prOut));
|
|
1353
|
+
|
|
1354
|
+
onUpdate?.({
|
|
1355
|
+
content: [{ type: "text", text: `Fetching workflow jobs for PR #${number}...` }],
|
|
1356
|
+
details: {},
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
const runsOut = await ghExec(
|
|
1360
|
+
["api", `/repos/${effectiveRepo}/actions/runs?head_sha=${headRefOid}&per_page=100`],
|
|
1361
|
+
{ cwd: ctx.cwd, signal, input: params },
|
|
1362
|
+
);
|
|
1363
|
+
const { workflow_runs } = Value.Parse(workflowRunsSchema, JSON.parse(runsOut));
|
|
1364
|
+
|
|
1365
|
+
const failedJobs: {
|
|
1366
|
+
runId: number;
|
|
1367
|
+
runName: string;
|
|
1368
|
+
runUrl: string;
|
|
1369
|
+
jobId: number;
|
|
1370
|
+
jobName: string;
|
|
1371
|
+
conclusion: string;
|
|
1372
|
+
jobUrl?: string;
|
|
1373
|
+
}[] = [];
|
|
1374
|
+
let totalJobs = 0;
|
|
1375
|
+
|
|
1376
|
+
for (const run of workflow_runs) {
|
|
1377
|
+
const jobsOut = await ghExec(
|
|
1378
|
+
["api", `/repos/${effectiveRepo}/actions/runs/${run.id}/jobs?per_page=100`],
|
|
1379
|
+
{ cwd: ctx.cwd, signal, input: params },
|
|
1380
|
+
);
|
|
1381
|
+
const { jobs } = Value.Parse(jobsResponseSchema, JSON.parse(jobsOut));
|
|
1382
|
+
totalJobs += jobs.length;
|
|
1383
|
+
|
|
1384
|
+
for (const job of jobs) {
|
|
1385
|
+
if (!job.conclusion || FAILED_JOB_CONCLUSIONS.has(job.conclusion)) {
|
|
1386
|
+
failedJobs.push({
|
|
1387
|
+
runId: run.id,
|
|
1388
|
+
runName: run.name,
|
|
1389
|
+
runUrl: run.html_url,
|
|
1390
|
+
jobId: job.id,
|
|
1391
|
+
jobName: job.name,
|
|
1392
|
+
conclusion: job.conclusion ?? "in_progress",
|
|
1393
|
+
jobUrl: job.html_url,
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1319
1398
|
|
|
1320
|
-
|
|
1321
|
-
if (exitCode === 2) {
|
|
1399
|
+
if (totalJobs === 0) {
|
|
1322
1400
|
return {
|
|
1323
1401
|
content: [
|
|
1324
|
-
{
|
|
1402
|
+
{
|
|
1403
|
+
type: "text",
|
|
1404
|
+
text: `## PR #${number} CI Checks\n\nNo workflow runs found for head commit ${headRefOid.slice(0, 7)}.`,
|
|
1405
|
+
},
|
|
1325
1406
|
],
|
|
1326
|
-
details: { status: "
|
|
1407
|
+
details: { status: "no-jobs", totalJobs: 0, input: params },
|
|
1327
1408
|
};
|
|
1328
1409
|
}
|
|
1329
1410
|
|
|
1330
|
-
if (
|
|
1331
|
-
|
|
1411
|
+
if (failedJobs.length > 0) {
|
|
1412
|
+
const lines = failedJobs.map(
|
|
1413
|
+
(j) =>
|
|
1414
|
+
`- ${statusIcon(j.conclusion)} **${j.jobName}** (${j.conclusion}) — [job #${j.jobId}](${j.jobUrl ?? j.runUrl})\n` +
|
|
1415
|
+
` - workflow: [${j.runName} (#${j.runId})](${j.runUrl})`,
|
|
1416
|
+
);
|
|
1417
|
+
return {
|
|
1418
|
+
content: [
|
|
1419
|
+
{
|
|
1420
|
+
type: "text",
|
|
1421
|
+
text:
|
|
1422
|
+
`## PR #${number} CI Checks - FAILED\n\n` +
|
|
1423
|
+
`${failedJobs.length} of ${totalJobs} job(s) did not succeed:\n\n${lines.join("\n")}`,
|
|
1424
|
+
},
|
|
1425
|
+
],
|
|
1426
|
+
details: {
|
|
1427
|
+
status: "failure",
|
|
1428
|
+
totalJobs,
|
|
1429
|
+
failedJobs,
|
|
1430
|
+
input: params,
|
|
1431
|
+
},
|
|
1432
|
+
};
|
|
1332
1433
|
}
|
|
1333
1434
|
|
|
1334
1435
|
return {
|
|
1335
|
-
content: [
|
|
1336
|
-
|
|
1436
|
+
content: [
|
|
1437
|
+
{
|
|
1438
|
+
type: "text",
|
|
1439
|
+
text: `## PR #${number} CI Checks - PASSED\n\nAll ${totalJobs} job(s) succeeded.`,
|
|
1440
|
+
},
|
|
1441
|
+
],
|
|
1442
|
+
details: { status: "success", totalJobs, input: params },
|
|
1337
1443
|
};
|
|
1338
1444
|
},
|
|
1339
1445
|
});
|