next-issue 2.3.2 → 2.4.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/README.md +6 -2
- package/dist/args.mjs +2 -1
- package/dist/cli.mjs +6 -1
- package/dist/github.mjs +6 -4
- package/dist/pipeline.mjs +9 -3
- package/dist/serve.mjs +12 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -284,7 +284,7 @@ does not know all stop the run, so a typo cannot pass without a word.
|
|
|
284
284
|
| Field | Default | Effect |
|
|
285
285
|
| --- | --- | --- |
|
|
286
286
|
| `remote` | `origin` | The git remote for the repository and the branches |
|
|
287
|
-
| `issueLimit` | `100` | The maximum number of open issues to read |
|
|
287
|
+
| `issueLimit` | `100` | The maximum number of open issues to read. With `labels.ready` set, only issues with the ready, in-progress or in-review label count |
|
|
288
288
|
| `maxCiFixes` | `5` | The budget for check fixes per issue |
|
|
289
289
|
| `maxCommitFixes` | `10` | The budget for fixes of a commit that a git hook stopped, per issue |
|
|
290
290
|
| `maxReviewRounds` | `5` | The budget for review rounds per issue |
|
|
@@ -357,6 +357,7 @@ that a label still holds back stays as it was.
|
|
|
357
357
|
| --- | --- |
|
|
358
358
|
| `init` | Write the config file with the defaults |
|
|
359
359
|
| `serve` | Run the worker that pi extensions talk to, see [Worker](#worker) |
|
|
360
|
+
| `stop` | Stop the worker, see [Worker](#worker) |
|
|
360
361
|
| `--force` | Replace an existing config file, with `init` |
|
|
361
362
|
| `--once` | Stop after the first handled issue |
|
|
362
363
|
| `--max <n>` | Handle at most n issues |
|
|
@@ -423,7 +424,10 @@ to it. The worker:
|
|
|
423
424
|
- stops every agent and command and exits on `SIGTERM` or `SIGINT`, and
|
|
424
425
|
removes its socket and lock files.
|
|
425
426
|
|
|
426
|
-
Stop it
|
|
427
|
+
Stop it with `next-issue stop`. This sends `SIGTERM` to the process in the
|
|
428
|
+
lock file, so the worker stops at once and a run that is going resumes from
|
|
429
|
+
its saved state next time. When no worker is running, the command says so and
|
|
430
|
+
exits with 0.
|
|
427
431
|
|
|
428
432
|
## Development
|
|
429
433
|
|
package/dist/args.mjs
CHANGED
|
@@ -3,6 +3,7 @@ export const USAGE = `next-issue [command] [options]
|
|
|
3
3
|
|
|
4
4
|
init write ${CONFIG_FILE} with the defaults
|
|
5
5
|
serve run the worker that pi extensions talk to
|
|
6
|
+
stop stop the worker
|
|
6
7
|
|
|
7
8
|
--once stop after the first handled issue
|
|
8
9
|
--max <n> handle at most n issues
|
|
@@ -30,7 +31,7 @@ export function parseArgs(argv) {
|
|
|
30
31
|
for (let index = 0; index < argv.length; index += 1) {
|
|
31
32
|
const arg = argv[index];
|
|
32
33
|
if (index === 0 && !arg.startsWith("-")) {
|
|
33
|
-
if (arg !== "init" && arg !== "serve") {
|
|
34
|
+
if (arg !== "init" && arg !== "serve" && arg !== "stop") {
|
|
34
35
|
throw new Error(`Unknown command: ${arg}`);
|
|
35
36
|
}
|
|
36
37
|
args.command = arg;
|
package/dist/cli.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { ensureIgnored, repoRoot, WORKTREE_DIR } from "./git.mjs";
|
|
|
5
5
|
import { listenForInterrupts } from "./interrupt.mjs";
|
|
6
6
|
import { message } from "./observe.mjs";
|
|
7
7
|
import { runRepo } from "./run.mjs";
|
|
8
|
-
import { serve } from "./serve.mjs";
|
|
8
|
+
import { serve, socketPaths, stopWorker } from "./serve.mjs";
|
|
9
9
|
import { STATE_DIR } from "./state.mjs";
|
|
10
10
|
import { formatSummary } from "./summary.mjs";
|
|
11
11
|
function write(stream, text) {
|
|
@@ -22,6 +22,11 @@ async function main() {
|
|
|
22
22
|
if (args.command === "serve") {
|
|
23
23
|
return serve();
|
|
24
24
|
}
|
|
25
|
+
if (args.command === "stop") {
|
|
26
|
+
const pid = await stopWorker(socketPaths().lockPath);
|
|
27
|
+
await write(process.stdout, pid === undefined ? "next-issue: no worker is running\n" : `next-issue: stopped the worker with process id ${pid}\n`);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
25
30
|
const root = await repoRoot(process.cwd());
|
|
26
31
|
if (args.command === "init") {
|
|
27
32
|
return init(root, args.force);
|
package/dist/github.mjs
CHANGED
|
@@ -44,14 +44,15 @@ function toIssue(item) {
|
|
|
44
44
|
labels: item.labels.map((label) => label.name),
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
|
-
export async function openIssues(repo, limit) {
|
|
47
|
+
export async function openIssues(repo, limit, labels) {
|
|
48
|
+
const filter = labels.length === 0 ? "" : ` label:${labels.map((label) => `"${label}"`).join(",")}`;
|
|
48
49
|
const raw = await gh(repo, [
|
|
49
50
|
"issue",
|
|
50
51
|
"list",
|
|
51
52
|
"--state",
|
|
52
53
|
"open",
|
|
53
54
|
"--search",
|
|
54
|
-
|
|
55
|
+
`sort:created-asc${filter}`,
|
|
55
56
|
"--limit",
|
|
56
57
|
String(limit),
|
|
57
58
|
"--json",
|
|
@@ -61,8 +62,9 @@ export async function openIssues(repo, limit) {
|
|
|
61
62
|
return parsed.map(toIssue).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
62
63
|
}
|
|
63
64
|
export async function viewIssue(repo, issue) {
|
|
64
|
-
const raw = await gh(repo, ["issue", "view", String(issue), "--json", ISSUE_FIELDS]);
|
|
65
|
-
|
|
65
|
+
const raw = await gh(repo, ["issue", "view", String(issue), "--json", `${ISSUE_FIELDS},state`]);
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
return { ...toIssue(parsed), open: parsed.state === "OPEN" };
|
|
66
68
|
}
|
|
67
69
|
export async function issueComments(repo, issue) {
|
|
68
70
|
const raw = await gh(repo, ["issue", "view", String(issue), "--json", "comments"]);
|
package/dist/pipeline.mjs
CHANGED
|
@@ -60,15 +60,21 @@ export function skipReason(issue, config, resuming) {
|
|
|
60
60
|
}
|
|
61
61
|
export async function pendingIssues(context) {
|
|
62
62
|
const { repo, config, ports } = context;
|
|
63
|
-
const
|
|
63
|
+
const { ready, inProgress, inReview } = config.labels;
|
|
64
|
+
const labels = ready.length === 0 ? [] : [ready, inProgress, inReview];
|
|
65
|
+
const issues = await ports.openIssues(repo, config.issueLimit, labels);
|
|
64
66
|
const listed = new Set(issues.map((issue) => issue.number));
|
|
65
67
|
for (const number of await savedIssues(repo)) {
|
|
66
68
|
if (listed.has(number)) {
|
|
67
69
|
continue;
|
|
68
70
|
}
|
|
69
71
|
const saved = await readState(repo, number).catch(() => undefined);
|
|
70
|
-
if (saved
|
|
71
|
-
|
|
72
|
+
if (saved === undefined) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const { open, ...issue } = await ports.viewIssue(repo, number);
|
|
76
|
+
if (open || (saved.pr !== undefined && (await ports.prMerged(repo, saved.pr)))) {
|
|
77
|
+
issues.push(issue);
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
return issues.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
package/dist/serve.mjs
CHANGED
|
@@ -36,6 +36,17 @@ function alive(pid) {
|
|
|
36
36
|
return error.code === "EPERM";
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
async function holder(lockPath) {
|
|
40
|
+
const pid = Number((await readFile(lockPath, "utf8").catch(() => "")).trim());
|
|
41
|
+
return pid > 0 && alive(pid) ? pid : undefined;
|
|
42
|
+
}
|
|
43
|
+
export async function stopWorker(lockPath) {
|
|
44
|
+
const pid = await holder(lockPath);
|
|
45
|
+
if (pid !== undefined) {
|
|
46
|
+
process.kill(pid, "SIGTERM");
|
|
47
|
+
}
|
|
48
|
+
return pid;
|
|
49
|
+
}
|
|
39
50
|
async function lock(path) {
|
|
40
51
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
41
52
|
try {
|
|
@@ -46,8 +57,7 @@ async function lock(path) {
|
|
|
46
57
|
if (error.code !== "EEXIST") {
|
|
47
58
|
throw error;
|
|
48
59
|
}
|
|
49
|
-
|
|
50
|
-
if (pid > 0 && alive(pid)) {
|
|
60
|
+
if ((await holder(path)) !== undefined) {
|
|
51
61
|
return false;
|
|
52
62
|
}
|
|
53
63
|
await rm(path, { force: true });
|