bugpatrol 0.4.4 → 0.4.5
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/bin.js +76 -13
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -24382,6 +24382,7 @@ SKIP WHAT IS ALREADY ON GITHUB
|
|
|
24382
24382
|
list_items also shows the Bugpatrol PRs and issues on GitHub. Other machines and earlier runs filed some of them.
|
|
24383
24383
|
- If one of them already covers an item (the same problem, or a PR that changes the same code for it), skip the
|
|
24384
24384
|
item. Reason: "Duplicate of #<number>".
|
|
24385
|
+
- A PR item's own issue, which list_items names, never counts: the PR closes it, so publish the item.
|
|
24385
24386
|
- If a closed PR made the same change, skip the item. Reason: "Repeats the rejected PR #<number>".
|
|
24386
24387
|
Write a plain, short summary of 2\u20135 sentences: what is wrong, for whom, and for a PR what changed and how Bugpatrol checked it.
|
|
24387
24388
|
Do not repeat the full report. Bugpatrol adds screenshots, steps, the fix, and verification. Call finish when done.
|
|
@@ -40542,6 +40543,10 @@ var rank2 = { cosmetic: 0, minor: 1, major: 2, critical: 3 };
|
|
|
40542
40543
|
function publishable(fix) {
|
|
40543
40544
|
return fix.status === "verified" || fix.status === "proposed" && !judgedRetests(fix.retests).length;
|
|
40544
40545
|
}
|
|
40546
|
+
var ownIssue = (item) => item.kind === "pr" ? item.issue.github?.number : void 0;
|
|
40547
|
+
var ownIssueNote = (item) => ownIssue(item) ? `This item's own GitHub issue is #${ownIssue(item)}. It is not a duplicate; this PR closes it.` : "";
|
|
40548
|
+
var citesOwnIssue = (item, text3) => ownIssue(item) !== void 0 && new RegExp(`#${ownIssue(item)}\\b`).test(text3);
|
|
40549
|
+
var skipHolds = (item, skip) => skip !== void 0 && !citesOwnIssue(item, skip.reason);
|
|
40545
40550
|
var result2 = (text3, isError = false) => ({ content: [{ type: "text", text: text3 }], isError });
|
|
40546
40551
|
var schema4 = (properties = {}, required = []) => ({
|
|
40547
40552
|
type: "object",
|
|
@@ -40604,6 +40609,27 @@ async function reuseFiled(gh, repo, workspace, issues, outcomes, onLog) {
|
|
|
40604
40609
|
}
|
|
40605
40610
|
return handled;
|
|
40606
40611
|
}
|
|
40612
|
+
async function recheckFalseSkips(gh, repo, items, onLog) {
|
|
40613
|
+
const kept = [];
|
|
40614
|
+
for (const item of items) {
|
|
40615
|
+
const own = ownIssue(item);
|
|
40616
|
+
if (!item.issue.publishSkipped || own === void 0) {
|
|
40617
|
+
kept.push(item);
|
|
40618
|
+
continue;
|
|
40619
|
+
}
|
|
40620
|
+
try {
|
|
40621
|
+
const { state: state2, closedByPullRequestsReferences: prs = [] } = JSON.parse(await gh(["issue", "view", String(own), "--repo", repo, "--json", "state,closedByPullRequestsReferences"]));
|
|
40622
|
+
if (state2 === "CLOSED" || prs.length) {
|
|
40623
|
+
onLog?.(`${item.issue.id} stays skipped: #${own} is ${prs.length ? `closed by PR #${prs[0].number}` : "closed"}.`);
|
|
40624
|
+
continue;
|
|
40625
|
+
}
|
|
40626
|
+
kept.push(item);
|
|
40627
|
+
} catch (error) {
|
|
40628
|
+
onLog?.(`GitHub lookup of #${own} failed, so ${item.issue.id} waits for the next run: ${String(error)}`);
|
|
40629
|
+
}
|
|
40630
|
+
}
|
|
40631
|
+
return kept;
|
|
40632
|
+
}
|
|
40607
40633
|
async function runPublisher(root2, config, deps = {}) {
|
|
40608
40634
|
if (!config.agents.github.enabled && !deps.dryRun)
|
|
40609
40635
|
return [];
|
|
@@ -40612,14 +40638,18 @@ async function runPublisher(root2, config, deps = {}) {
|
|
|
40612
40638
|
const fixes = new Map((await workspace.listFixes()).map((fix) => [fix.issueId, fix]));
|
|
40613
40639
|
const issues = (await workspace.listIssues()).filter((issue) => !deps.issueIds || deps.issueIds.includes(issue.id));
|
|
40614
40640
|
let items = issues.flatMap((issue) => {
|
|
40615
|
-
if (issue.status === "dismissed" || issue.
|
|
40641
|
+
if (issue.status === "dismissed" || issue.fixRejected)
|
|
40616
40642
|
return [];
|
|
40617
40643
|
const found = fixes.get(issue.id);
|
|
40618
40644
|
const fix = found?.external ? void 0 : found;
|
|
40619
40645
|
if (fix?.status === "rejected")
|
|
40620
40646
|
return [];
|
|
40621
|
-
if (fix && !fix.pr && publishable(fix))
|
|
40622
|
-
|
|
40647
|
+
if (fix && !fix.pr && publishable(fix)) {
|
|
40648
|
+
const item = { kind: "pr", issue, fix };
|
|
40649
|
+
return skipHolds(item, issue.publishSkipped) ? [] : [item];
|
|
40650
|
+
}
|
|
40651
|
+
if (issue.publishSkipped)
|
|
40652
|
+
return [];
|
|
40623
40653
|
if (!issue.github && !["fixed", "dismissed"].includes(issue.status) && rank2[issue.severity] >= rank2[config.agents.github.issueMinSeverity] && (!fix || ["declined", "failed"].includes(fix.status)))
|
|
40624
40654
|
return [{ kind: "issue", issue, fix }];
|
|
40625
40655
|
return [];
|
|
@@ -40639,7 +40669,7 @@ async function runPublisher(root2, config, deps = {}) {
|
|
|
40639
40669
|
}
|
|
40640
40670
|
resolved = { repo: ready.repo, defaultBranch: ready.defaultBranch };
|
|
40641
40671
|
const handled = await reuseFiled(gh, resolved.repo, workspace, [...regressions, ...items.filter((item) => item.kind === "issue").map((item) => item.issue)], outcomes, deps.onLog);
|
|
40642
|
-
items = items.filter((item) => item.kind === "pr" || !handled.has(item.issue.id));
|
|
40672
|
+
items = await recheckFalseSkips(gh, resolved.repo, items.filter((item) => item.kind === "pr" || !handled.has(item.issue.id)), deps.onLog);
|
|
40643
40673
|
if (!items.length)
|
|
40644
40674
|
return outcomes;
|
|
40645
40675
|
}
|
|
@@ -40652,6 +40682,8 @@ async function runPublisher(root2, config, deps = {}) {
|
|
|
40652
40682
|
let steps = 0;
|
|
40653
40683
|
let costUsd = 0;
|
|
40654
40684
|
let status = "finished";
|
|
40685
|
+
let ending = "finished without publishing or skipping it";
|
|
40686
|
+
const publishErrors = /* @__PURE__ */ new Map();
|
|
40655
40687
|
let assetsReady = false;
|
|
40656
40688
|
let labelsReady = false;
|
|
40657
40689
|
const get = (id2) => items.find((item) => item.issue.id === id2 && !done.has(id2));
|
|
@@ -40699,7 +40731,7 @@ async function runPublisher(root2, config, deps = {}) {
|
|
|
40699
40731
|
description: "List issues ready to publish.",
|
|
40700
40732
|
inputSchema: schema4(),
|
|
40701
40733
|
async run() {
|
|
40702
|
-
const list3 = items.filter((item) => !done.has(item.issue.id)).map((item) => `${item.issue.id} [${item.kind}, ${item.issue.severity}, fix: ${item.fix?.status ?? "none"}, retest: ${item.fix?.retests?.at(-1)?.outcome ?? "none"}]: ${item.issue.title}
|
|
40734
|
+
const list3 = items.filter((item) => !done.has(item.issue.id)).map((item) => `${item.issue.id} [${item.kind}, ${item.issue.severity}, fix: ${item.fix?.status ?? "none"}, retest: ${item.fix?.retests?.at(-1)?.outcome ?? "none"}]: ${item.issue.title} ${ownIssueNote(item)}`.trimEnd()).join("\n");
|
|
40703
40735
|
const style = recentTitles.length ? `
|
|
40704
40736
|
|
|
40705
40737
|
RECENT MERGED PR TITLES IN THIS REPO (use the same types and scopes)
|
|
@@ -40728,7 +40760,8 @@ ${item.issue.body}
|
|
|
40728
40760
|
Judgement: ${item.issue.judgement.reason}
|
|
40729
40761
|
Fix: ${item.fix?.summary ?? "(none)"}
|
|
40730
40762
|
Diff: ${item.fix?.diffStat ?? "(none)"}
|
|
40731
|
-
Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}
|
|
40763
|
+
Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}
|
|
40764
|
+
${ownIssueNote(item)}`;
|
|
40732
40765
|
const before = [...pathsFor({ ...item, fix: void 0 }, candidates)].slice(0, 4);
|
|
40733
40766
|
const after = (last?.captures?.length ? last.captures.map((shot) => shot.after) : [last?.after]).filter((path) => Boolean(path)).slice(0, 4);
|
|
40734
40767
|
const content = [
|
|
@@ -40783,7 +40816,7 @@ Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}`;
|
|
|
40783
40816
|
try {
|
|
40784
40817
|
const latestIssue = await workspace.readIssue(item.issue.id);
|
|
40785
40818
|
const latestFix = item.fix && await workspace.readFix(item.fix.id);
|
|
40786
|
-
if (!latestIssue || latestIssue.status === "dismissed" || latestIssue.publishSkipped || latestIssue.fixRejected || latestFix?.status === "rejected" || item.kind === "issue" && latestIssue.github || item.kind === "pr" && (!latestFix || latestFix.pr))
|
|
40819
|
+
if (!latestIssue || latestIssue.status === "dismissed" || skipHolds(item, latestIssue.publishSkipped) || latestIssue.fixRejected || latestFix?.status === "rejected" || item.kind === "issue" && latestIssue.github || item.kind === "pr" && (!latestFix || latestFix.pr))
|
|
40787
40820
|
return result2("This item was dismissed or already handled.", true);
|
|
40788
40821
|
Object.assign(item.issue, latestIssue);
|
|
40789
40822
|
if (item.fix && latestFix)
|
|
@@ -40842,6 +40875,10 @@ Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}`;
|
|
|
40842
40875
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
40843
40876
|
};
|
|
40844
40877
|
await workspace.saveFix(item.fix);
|
|
40878
|
+
if (item.issue.publishSkipped) {
|
|
40879
|
+
item.issue.publishSkipped = void 0;
|
|
40880
|
+
await workspace.saveIssue(item.issue);
|
|
40881
|
+
}
|
|
40845
40882
|
} else {
|
|
40846
40883
|
const opened = await createIssue(gh, {
|
|
40847
40884
|
repo: resolved.repo,
|
|
@@ -40866,6 +40903,7 @@ Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}`;
|
|
|
40866
40903
|
} catch (error) {
|
|
40867
40904
|
if (item.fix?.commit)
|
|
40868
40905
|
await workspace.saveFix(item.fix);
|
|
40906
|
+
publishErrors.set(item.issue.id, String(error));
|
|
40869
40907
|
return result2(String(error), true);
|
|
40870
40908
|
}
|
|
40871
40909
|
}
|
|
@@ -40881,8 +40919,10 @@ Retest: ${last?.outcome ?? "none"} \u2014 ${last?.reason ?? ""}`;
|
|
|
40881
40919
|
const reason2 = String(input.reason ?? "").trim();
|
|
40882
40920
|
if (!reason2)
|
|
40883
40921
|
return result2("A reason is required.", true);
|
|
40922
|
+
if (citesOwnIssue(item, reason2))
|
|
40923
|
+
return result2(`${ownIssueNote(item)} Publish it instead of skipping.`, true);
|
|
40884
40924
|
const latest = await workspace.readIssue(item.issue.id);
|
|
40885
|
-
if (!latest || latest.status === "dismissed" || latest.publishSkipped)
|
|
40925
|
+
if (!latest || latest.status === "dismissed" || skipHolds(item, latest.publishSkipped))
|
|
40886
40926
|
return result2("This item was dismissed or already handled.", true);
|
|
40887
40927
|
Object.assign(item.issue, latest);
|
|
40888
40928
|
item.issue.publishSkipped = { reason: vars.redact(reason2), at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -40922,15 +40962,29 @@ ${items.map((item) => `${item.issue.id}: ${item.issue.title}`).join("\n")}`),
|
|
|
40922
40962
|
}, session.emit);
|
|
40923
40963
|
steps = outcome2.steps;
|
|
40924
40964
|
costUsd = outcome2.costUsd;
|
|
40965
|
+
if (outcome2.stop !== "done")
|
|
40966
|
+
ending = `stopped early (${outcome2.stop})`;
|
|
40925
40967
|
} catch (error) {
|
|
40926
40968
|
status = "failed";
|
|
40969
|
+
ending = `failed: ${String(error)}`;
|
|
40927
40970
|
session.emit({ kind: "error", summary: `GitHub publishing failed: ${String(error)}` });
|
|
40928
40971
|
deps.onLog?.(`GitHub publishing failed: ${String(error)}`);
|
|
40929
40972
|
} finally {
|
|
40973
|
+
const left = session.cancelled ? [] : items.filter((item) => !done.has(item.issue.id));
|
|
40974
|
+
for (const { issue } of left) {
|
|
40975
|
+
const error = publishErrors.get(issue.id);
|
|
40976
|
+
outcomes.push({
|
|
40977
|
+
issueId: issue.id,
|
|
40978
|
+
kind: "unpublished",
|
|
40979
|
+
reason: vars.redact(`The publisher ${ending}.${error ? ` Its publish failed: ${error}` : ""}`)
|
|
40980
|
+
});
|
|
40981
|
+
}
|
|
40982
|
+
if (left.length)
|
|
40983
|
+
status = "failed";
|
|
40930
40984
|
const prs = outcomes.filter((item) => item.kind === "pr").length;
|
|
40931
40985
|
const issues2 = outcomes.filter((item) => item.kind === "issue").length;
|
|
40932
40986
|
const skipped = outcomes.filter((item) => item.kind === "skipped").length;
|
|
40933
|
-
const summary = `${deps.dryRun ? "Drafted" : "Published"} ${prs} PR(s) and ${issues2} issue(s); skipped ${skipped}`;
|
|
40987
|
+
const summary = `${deps.dryRun ? "Drafted" : "Published"} ${prs} PR(s) and ${issues2} issue(s); skipped ${skipped}${left.length ? `; ${left.length} left unpublished` : ""}`;
|
|
40934
40988
|
await workspace.endSession(record.id, { summary, status, steps, costUsd });
|
|
40935
40989
|
session.emit({ kind: "session-end", summary });
|
|
40936
40990
|
await session.idle(costUsd);
|
|
@@ -42223,14 +42277,19 @@ async function runPatrol(options) {
|
|
|
42223
42277
|
problems.push(`Cycle ${cycle}: the retest of ${fix.issueId} failed: ${last.reason}`);
|
|
42224
42278
|
}
|
|
42225
42279
|
const cycleFailed = problems.length > before;
|
|
42226
|
-
if (!interrupted && config.agents.github.enabled)
|
|
42227
|
-
await runPublisher(root2, config, {
|
|
42280
|
+
if (!interrupted && config.agents.github.enabled) {
|
|
42281
|
+
const outcomes = await runPublisher(root2, config, {
|
|
42228
42282
|
createRuntime: options.createRuntime,
|
|
42229
42283
|
onLog: options.onLog,
|
|
42230
42284
|
onSession: (session) => {
|
|
42231
42285
|
activeSession = session;
|
|
42232
42286
|
}
|
|
42233
42287
|
});
|
|
42288
|
+
for (const item of outcomes.filter((outcome2) => outcome2.kind === "unpublished")) {
|
|
42289
|
+
problems.push(`Cycle ${cycle}: ${item.issueId} was not published: ${item.reason}`);
|
|
42290
|
+
options.onLog?.(problems.at(-1));
|
|
42291
|
+
}
|
|
42292
|
+
}
|
|
42234
42293
|
if (!interrupted && config.agents.github.enabled) {
|
|
42235
42294
|
const ci = await watchCi(root2, config, {
|
|
42236
42295
|
createRuntime: options.createRuntime,
|
|
@@ -47350,11 +47409,15 @@ ${problems.map((problem) => `- ${problem.split("\n")[0]}`).join("\n")}`);
|
|
|
47350
47409
|
return;
|
|
47351
47410
|
}
|
|
47352
47411
|
for (const item of outcomes) {
|
|
47353
|
-
const kind = item.kind === "pr" ? flags.dryRun ? "PR draft" : "PR" : item.kind === "issue" ? flags.dryRun ? "issue draft" : "issue" :
|
|
47412
|
+
const kind = item.kind === "pr" ? flags.dryRun ? "PR draft" : "PR" : item.kind === "issue" ? flags.dryRun ? "issue draft" : "issue" : item.kind;
|
|
47354
47413
|
log(` ${kind.padEnd(11)} ${item.issueId} ${item.url ?? item.reason ?? ""}`);
|
|
47355
47414
|
}
|
|
47356
47415
|
const count = (kind) => outcomes.filter((item) => item.kind === kind).length;
|
|
47357
47416
|
log(`${flags.dryRun ? "Wrote" : "Opened"} ${count("pr")} PR(s) and ${count("issue")} issue(s); skipped ${count("skipped")}.`);
|
|
47417
|
+
if (count("unpublished")) {
|
|
47418
|
+
log(`${count("unpublished")} item(s) were not published. Run \`bugpatrol publish\` again after you fix the cause.`);
|
|
47419
|
+
process.exitCode = 1;
|
|
47420
|
+
}
|
|
47358
47421
|
return;
|
|
47359
47422
|
}
|
|
47360
47423
|
if (command === "review") {
|
|
@@ -49330,7 +49393,7 @@ legacyEnv(process.env);
|
|
|
49330
49393
|
var root = findProjectRoot(process.cwd());
|
|
49331
49394
|
function version() {
|
|
49332
49395
|
if (true)
|
|
49333
|
-
return "0.4.
|
|
49396
|
+
return "0.4.5";
|
|
49334
49397
|
try {
|
|
49335
49398
|
return JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
49336
49399
|
} catch {
|