next-issue 2.4.6 → 2.4.8
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.md +8 -4
- package/dist/github.mjs +14 -0
- package/dist/pipeline.mjs +17 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,10 @@ and exits with code 1. For each issue:
|
|
|
51
51
|
while no check failed counts as such a read, not as a red build. A repository that reports no
|
|
52
52
|
check keeps asking for `checkGraceSeconds`, because a new pull request often
|
|
53
53
|
shows no check yet, and only then goes straight to the review. The later
|
|
54
|
-
gates of the same issue ask once.
|
|
54
|
+
gates of the same issue ask once. GitHub runs no check on a pull request
|
|
55
|
+
that conflicts with its base, so no check and a conflict hand the issue to
|
|
56
|
+
a person. While GitHub still works out whether the pull request can merge,
|
|
57
|
+
the run asks again until `checkTimeoutMinutes`.
|
|
55
58
|
7. On a red build, give the failed job logs to the fixer agent, then go to step
|
|
56
59
|
6 again. The budget is `maxCiFixes`.
|
|
57
60
|
8. Let the reviewer agent judge the diff, at most `diffMaxChars` of it. The
|
|
@@ -97,7 +100,8 @@ and exits with code 1. For each issue:
|
|
|
97
100
|
## Stops for a loop
|
|
98
101
|
|
|
99
102
|
The harness hands the issue to a person, with the `status:needs-human` label,
|
|
100
|
-
when
|
|
103
|
+
and comments the reason on the pull request, or on the issue when there is no
|
|
104
|
+
pull request yet, when:
|
|
101
105
|
|
|
102
106
|
- the check budget or the review budget runs out;
|
|
103
107
|
- a fixer round adds no commit and gives no reason, so there is no progress;
|
|
@@ -132,8 +136,8 @@ when:
|
|
|
132
136
|
- the setup command fails, or does not finish in `setupTimeoutMinutes`;
|
|
133
137
|
- a step of the run fails with an error. A `gh` call that is safe to repeat,
|
|
134
138
|
such as a read or a label edit, is tried up to three times first when the
|
|
135
|
-
network fails. A `gh pr create
|
|
136
|
-
reply never makes a duplicate.
|
|
139
|
+
network fails. A `gh pr create`, `gh pr comment` or `gh issue comment` is
|
|
140
|
+
tried once, so a lost reply never makes a duplicate.
|
|
137
141
|
|
|
138
142
|
The fixer sees the findings of all earlier rounds, not only the last one, with
|
|
139
143
|
its own disputes next to them. From round two, the reviewer judges only the
|
package/dist/github.mjs
CHANGED
|
@@ -330,6 +330,17 @@ export async function mergePr(repo, pr) {
|
|
|
330
330
|
const result = await run('gh', ['pr', 'merge', String(pr), '--squash', '--delete-branch', '--repo', slug(repo)], { cwd: repo.root });
|
|
331
331
|
return result.code === 0;
|
|
332
332
|
}
|
|
333
|
+
export async function prConflicts(repo, pr, options, clock = REAL_CLOCK) {
|
|
334
|
+
const deadline = clock.now() + options.timeoutMs;
|
|
335
|
+
for (;;) {
|
|
336
|
+
const mergeable = (await gh(repo, ['pr', 'view', String(pr), '--json', 'mergeable', '--jq', '.mergeable'])).trim();
|
|
337
|
+
const rest = deadline - clock.now();
|
|
338
|
+
if (mergeable !== 'UNKNOWN' || rest <= 0) {
|
|
339
|
+
return mergeable === 'CONFLICTING';
|
|
340
|
+
}
|
|
341
|
+
await clock.sleep(Math.min(options.intervalSeconds * 1000, rest));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
333
344
|
export async function prMerged(repo, pr) {
|
|
334
345
|
return ((await gh(repo, ['pr', 'view', String(pr), '--json', 'state', '--jq', '.state'])).trim() ===
|
|
335
346
|
'MERGED');
|
|
@@ -352,6 +363,9 @@ export async function mergeCommit(repo, pr, options, clock = REAL_CLOCK) {
|
|
|
352
363
|
export async function reopenIssue(repo, issue) {
|
|
353
364
|
await gh(repo, ['issue', 'reopen', String(issue)]);
|
|
354
365
|
}
|
|
366
|
+
export async function commentOnIssue(repo, issue, body) {
|
|
367
|
+
await ghOnce(repo, ['issue', 'comment', String(issue), '--body-file', '-'], body);
|
|
368
|
+
}
|
|
355
369
|
export async function commentOnPr(repo, pr, body) {
|
|
356
370
|
await ghOnce(repo, ['pr', 'comment', String(pr), '--body-file', '-'], body);
|
|
357
371
|
}
|
package/dist/pipeline.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { CODING, REVIEWING, runAgent } from './agents.mjs';
|
|
|
2
2
|
import { managedLabels } from './config.mjs';
|
|
3
3
|
import { run } from './exec.mjs';
|
|
4
4
|
import { addWorktree, branchName, commitAll, deleteBranch, diff, discardChanges, disputeBranches, findWorktree, HookError, isDirty, push, removeWorktree, resetToBase, revision, setAside, } from './git.mjs';
|
|
5
|
-
import { commentOnPr, createPr, failedBaseLogs, failedCheckLogs, findPr, issueComments, markPrReady, mergeCommit, mergePr, openBlockers, openIssues, prMerged, reopenIssue, setLabel, viewIssue, waitForBaseChecks, waitForChecks, } from './github.mjs';
|
|
5
|
+
import { commentOnIssue, commentOnPr, createPr, failedBaseLogs, failedCheckLogs, findPr, issueComments, markPrReady, mergeCommit, mergePr, openBlockers, openIssues, prConflicts, prMerged, reopenIssue, setLabel, viewIssue, waitForBaseChecks, waitForChecks, } from './github.mjs';
|
|
6
6
|
import { message } from './observe.mjs';
|
|
7
7
|
import { capDiff, fixPrompt, hookPrompt, implementPrompt, parseCommitSubject, parseUnrelated, reviewPrompt, } from './prompts.mjs';
|
|
8
8
|
import { detectSetupCommand } from './setup.mjs';
|
|
@@ -10,6 +10,7 @@ import { dropState, readState, resetState, savedIssues, writeState, } from './st
|
|
|
10
10
|
import { blockingFindings, fingerprint, formatFindings, formatVerdict, openFindings, readVerdict, VERDICT_SCHEMA, } from './verdict.mjs';
|
|
11
11
|
export const PORTS = {
|
|
12
12
|
addWorktree,
|
|
13
|
+
commentOnIssue,
|
|
13
14
|
commentOnPr,
|
|
14
15
|
commitAll,
|
|
15
16
|
createPr,
|
|
@@ -29,6 +30,7 @@ export const PORTS = {
|
|
|
29
30
|
mergePr,
|
|
30
31
|
openBlockers,
|
|
31
32
|
openIssues,
|
|
33
|
+
prConflicts,
|
|
32
34
|
prMerged,
|
|
33
35
|
push,
|
|
34
36
|
removeWorktree,
|
|
@@ -292,6 +294,14 @@ async function checkGate(job) {
|
|
|
292
294
|
await escalate(job, 'The checks did not finish in time.');
|
|
293
295
|
return { done: true, report: await finish(job, 'needs-human', 'checks timeout') };
|
|
294
296
|
}
|
|
297
|
+
if (checks === 'none' &&
|
|
298
|
+
(await ports.prConflicts(repo, state.pr, {
|
|
299
|
+
intervalSeconds: config.checkIntervalSeconds,
|
|
300
|
+
timeoutMs: config.checkTimeoutMinutes * 60_000,
|
|
301
|
+
}))) {
|
|
302
|
+
await escalate(job, 'The pull request conflicts with its base, so no checks ran.');
|
|
303
|
+
return { done: true, report: await finish(job, 'needs-human', 'merge conflict') };
|
|
304
|
+
}
|
|
295
305
|
if (checks !== 'fail') {
|
|
296
306
|
return { done: false, fixed: false };
|
|
297
307
|
}
|
|
@@ -607,8 +617,12 @@ async function handOver(context, log, issue, state, reason) {
|
|
|
607
617
|
log.event('hand-over', { reason }, 'quiet');
|
|
608
618
|
await writeState(context.repo, state);
|
|
609
619
|
await setStatus(context, issue, state.pr, context.config.labels.needsHuman);
|
|
610
|
-
|
|
611
|
-
|
|
620
|
+
const body = `next-issue needs help: ${reason}`;
|
|
621
|
+
if (state.pr === undefined) {
|
|
622
|
+
await context.ports.commentOnIssue(context.repo, issue, body);
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
await context.ports.commentOnPr(context.repo, state.pr, body);
|
|
612
626
|
}
|
|
613
627
|
}
|
|
614
628
|
async function setStatus(context, issue, pr, label) {
|