@joshuafolkken/kit 1.1094.0 → 1.1128.0
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/.claude/skills/workflow-commands/SKILL.md +1 -1
- package/.claude/skills/workflow-commands/background-commands.md +36 -85
- package/.claude/skills/workflow-commands/backlogrun-progress.md +29 -32
- package/.claude/skills/workflow-commands/backlogrun-steps.md +2 -2
- package/.claude/skills/workflow-commands/fullrun.md +5 -4
- package/.claude/skills/workflow-commands/halfrun.md +5 -4
- package/.claude/skills/workflow-commands/latest-gate.md +7 -15
- package/.claude/skills/workflow-commands/pre-gate-cut.md +4 -8
- package/.claude/skills/workflow-commands/split-assessment.md +4 -18
- package/.github/actions/setup-node/action.yml +33 -0
- package/.github/actions/setup-pnpm/action.yml +4 -4
- package/.github/workflows/auto-tag.yml +2 -3
- package/.github/workflows/ci.yml +7 -8
- package/.github/workflows/dependabot-auto-merge.yml +1 -0
- package/.github/workflows/pr-classification.yml +1 -3
- package/.github/workflows/production.yml +1 -0
- package/.github/workflows/publish.yml +10 -9
- package/.github/workflows/sonar-qube.yml +17 -13
- package/.pnpmfile.mjs +29 -0
- package/AGENTS.md +4 -11
- package/CLAUDE.md +24 -17
- package/GEMINI.md +4 -13
- package/README.md +8 -3
- package/SECURITY.md +1 -0
- package/cspell/index.yaml +4 -0
- package/dist/CLAUDE.basic.md +6 -6
- package/dist/CLAUDE.md +24 -17
- package/dist/CLAUDE.static.md +6 -6
- package/dist/config-merge/index.js +33 -9
- package/dist/hooks/batch-guard.js +4 -4
- package/dist/hooks/{chunk-NKOG3XAV.js → chunk-4JZDP5X5.js} +2 -2
- package/dist/hooks/{chunk-3C4YCCEF.js → chunk-5SD2YM7Z.js} +4 -4
- package/dist/hooks/{chunk-OIZUHPJI.js → chunk-77J7IEQM.js} +1 -1
- package/dist/hooks/{chunk-KHWHKJOM.js → chunk-7HVB7SZG.js} +3 -3
- package/dist/hooks/{chunk-UD24EAIL.js → chunk-AIOIOMPX.js} +1875 -104
- package/dist/hooks/{chunk-M2XDCUKD.js → chunk-M7QKEKWO.js} +88 -1783
- package/dist/hooks/{chunk-E54KGFEQ.js → chunk-MKVGBQPA.js} +1 -1
- package/dist/hooks/{chunk-7ZPFAVR7.js → chunk-NXYMNPUQ.js} +3 -3
- package/dist/hooks/{chunk-O4LL4HTI.js → chunk-QVLT52OV.js} +2 -2
- package/dist/hooks/{chunk-IL27YTPH.js → chunk-XYUIQOW3.js} +2 -2
- package/dist/hooks/codex-hook-adapter.js +12 -47
- package/dist/hooks/duplicate-read-guard.js +5 -5
- package/dist/hooks/format-edited.js +2 -2
- package/dist/hooks/inputs.json +180 -179
- package/dist/hooks/investigation-guard.js +5 -5
- package/dist/hooks/pretool-guard.js +9 -9
- package/dist/hooks/stop-guard.js +17 -12
- package/dist/josh.js +79 -30
- package/dist/version/index.js +36 -20
- package/eslint/rules/unicorn.js +3 -2
- package/package.json +18 -2
- package/pnpm-workspace.yaml +5 -7
- package/prompts/collaboration-workflow/glossary.md +17 -0
- package/prompts/collaboration-workflow/issue-template.md +1 -1
- package/prompts/collaboration-workflow/operating-rules.md +1 -1
- package/prompts/collaboration-workflow/principles.md +9 -0
- package/prompts/collaboration-workflow.md +1 -0
- package/scripts/backlog/backlog-arrival.ts +30 -10
- package/scripts/backlog/backlog-budget-cli.ts +15 -16
- package/scripts/backlog/backlog-offer-cli.ts +6 -14
- package/scripts/backlog/backlog-plan.ts +29 -18
- package/scripts/backlog/backlog-ready.ts +20 -7
- package/scripts/config-merge/parse-jsonc.ts +33 -3
- package/scripts/config-merge/patch-json-key.ts +13 -6
- package/scripts/cost-runtime/cost-cli.ts +3 -7
- package/scripts/doctor/doctor-consumer.ts +3 -5
- package/scripts/doctor/doctor-io.ts +9 -13
- package/scripts/document/backlogrun-parent-read-set.ts +12 -1
- package/scripts/document/document-byte-budget.ts +1 -0
- package/scripts/document/entry-read-budget.ts +13 -7
- package/scripts/document/entry-read-set.ts +32 -8
- package/scripts/document/lane-child-read-set.ts +7 -0
- package/scripts/document/read-set-point-of-use.ts +8 -3
- package/scripts/document/read-set-trim.ts +3 -1
- package/scripts/epic/epic-audit.ts +3 -18
- package/scripts/gh/git-gh-issue-list.ts +12 -11
- package/scripts/gh/git-gh-issue-write.ts +4 -1
- package/scripts/gh/git-gh-pr-auto-merge.ts +4 -1
- package/scripts/git/git-command.ts +6 -1
- package/scripts/git/git-spawn-sync.ts +65 -0
- package/scripts/git/git-spawn.ts +5 -4
- package/scripts/git/stash/stash-pop-args.ts +1 -6
- package/scripts/init/init-file-action.ts +54 -0
- package/scripts/init/init-logic-migrate.ts +35 -17
- package/scripts/init/init-logic-secretlint.ts +1 -1
- package/scripts/init/init-logic.ts +6 -22
- package/scripts/init/init.ts +11 -68
- package/scripts/init/kit-written-paths.ts +2 -0
- package/scripts/init/project-profile.ts +3 -6
- package/scripts/init/start-steps.ts +2 -1
- package/scripts/issue/defect-rate-cli.ts +61 -20
- package/scripts/issue/issue-backlinks.ts +7 -14
- package/scripts/issue/issue-cite.ts +12 -6
- package/scripts/issue/issue-file.ts +5 -12
- package/scripts/issue/markdown-section.ts +21 -6
- package/scripts/issue/measure-rerun-cli.ts +73 -14
- package/scripts/josh/josh-commands-project.ts +1 -1
- package/scripts/josh/josh-commands-workflow.ts +1 -1
- package/scripts/josh/run-github-prerequisite.ts +3 -6
- package/scripts/lane/lane-await.ts +43 -13
- package/scripts/lane/lane-close.ts +4 -1
- package/scripts/lane/lane-launch-cli.ts +10 -16
- package/scripts/lane/openai-review-broker.ts +4 -1
- package/scripts/lib/cli-flags.ts +31 -2
- package/scripts/lib/error-message.ts +12 -1
- package/scripts/lines/line-targets.ts +0 -11
- package/scripts/lines/lines-command.ts +5 -3
- package/scripts/propagate/propagate-git.ts +3 -8
- package/scripts/refactor/refactor-scan.ts +2 -1
- package/scripts/registry-migration/migrate-logic.ts +28 -19
- package/scripts/registry-migration/migrate.ts +6 -5
- package/scripts/release/github-release-cli.ts +14 -3
- package/scripts/release/release-publish.ts +36 -2
- package/scripts/release/release-worktree.ts +97 -2
- package/scripts/review/review-checkout.ts +3 -2
- package/scripts/review/review-record-cli.ts +9 -16
- package/scripts/rules/early-heartbeat.ts +4 -3
- package/scripts/rules/lane-park.ts +9 -0
- package/scripts/rules/stop-rules.ts +7 -4
- package/scripts/run/run-carry-args.ts +8 -12
- package/scripts/run/run-cut-args.ts +1 -6
- package/scripts/run/run-ending-cli.ts +1 -5
- package/scripts/run/run-entry-cli.ts +40 -9
- package/scripts/run/run-entry-stop.ts +104 -0
- package/scripts/run/run-event-stream-emit.ts +7 -2
- package/scripts/run/run-hold.ts +4 -1
- package/scripts/run/run-liveness-cli.ts +1 -5
- package/scripts/run/run-merge-cli.ts +2 -10
- package/scripts/run/run-parent-cut-hook.ts +4 -1
- package/scripts/run/run-prep-cli.ts +4 -1
- package/scripts/run/run-progress-args.ts +20 -4
- package/scripts/run/run-progress-cli.ts +31 -29
- package/scripts/run/run-progress-clock.ts +4 -1
- package/scripts/run/run-ship-cli.ts +8 -16
- package/scripts/run/run-ship-probe.ts +4 -1
- package/scripts/run/run-status-cli.ts +2 -15
- package/scripts/run/run-tidy-lanes.ts +4 -1
- package/scripts/run/run-wake-cli.ts +2 -10
- package/scripts/run/run-watcher-guard.ts +9 -5
- package/scripts/safe-chain/pack-hook.ts +92 -0
- package/scripts/sync/sync-ai-files.ts +4 -0
- package/scripts/test/test-declared-changed.ts +13 -16
- package/scripts/test/test-declared.ts +3 -7
- package/scripts/version/version-commands.ts +11 -1
- package/templates/CLAUDE.basic.md +6 -6
- package/templates/workflows/ci.yml +16 -82
- package/templates/workflows/dependabot-auto-merge.yml +1 -0
- package/templates/workflows/github-release.yml +1 -3
- package/tsconfig.sonar.json +4 -22
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lane_child_marker,
|
|
3
3
|
run_issue_number
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MKVGBQPA.js";
|
|
5
5
|
import {
|
|
6
6
|
PLATFORM_TEMP_ROOT,
|
|
7
7
|
cost_blocks,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
time_density_hook,
|
|
14
14
|
time_shell,
|
|
15
15
|
time_transcript_line
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4JZDP5X5.js";
|
|
17
17
|
import {
|
|
18
18
|
ENV_FILE_NAME,
|
|
19
19
|
LANE_SEAT_KEY,
|
|
@@ -21,6 +21,41 @@ import {
|
|
|
21
21
|
ports
|
|
22
22
|
} from "./chunk-RKUAD3I6.js";
|
|
23
23
|
|
|
24
|
+
// scripts/lib/bounded-pool.ts
|
|
25
|
+
function pool_width(limit, total) {
|
|
26
|
+
if (!Number.isFinite(limit)) return 1;
|
|
27
|
+
return Math.max(1, Math.min(Math.floor(limit), total));
|
|
28
|
+
}
|
|
29
|
+
function throw_first_failure(failures) {
|
|
30
|
+
if (failures.length === 0) return;
|
|
31
|
+
const [first] = failures;
|
|
32
|
+
throw first;
|
|
33
|
+
}
|
|
34
|
+
async function drain(pool, worker) {
|
|
35
|
+
for (let entry = pool.queue.shift(); entry !== void 0 && pool.failures.length === 0; entry = pool.queue.shift()) {
|
|
36
|
+
const [index, item] = entry;
|
|
37
|
+
try {
|
|
38
|
+
pool.results.push({ index, value: await worker(item, index) });
|
|
39
|
+
} catch (error) {
|
|
40
|
+
pool.failures.push(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function bounded_map(items, limit, worker) {
|
|
45
|
+
const pool = {
|
|
46
|
+
failures: [],
|
|
47
|
+
queue: items.map((item, index) => [index, item]),
|
|
48
|
+
results: []
|
|
49
|
+
};
|
|
50
|
+
const consumers = Array.from({ length: pool_width(limit, items.length) }, async () => {
|
|
51
|
+
await drain(pool, worker);
|
|
52
|
+
});
|
|
53
|
+
await Promise.all(consumers);
|
|
54
|
+
throw_first_failure(pool.failures);
|
|
55
|
+
return pool.results.toSorted((left, right) => left.index - right.index).map((result) => result.value);
|
|
56
|
+
}
|
|
57
|
+
var bounded_pool = { bounded_map, pool_width };
|
|
58
|
+
|
|
24
59
|
// scripts/rules/shell-segments.ts
|
|
25
60
|
var SEGMENT_SEPARATOR = /&&|\|\||;|\n/u;
|
|
26
61
|
function segments_of(command) {
|
|
@@ -108,8 +143,8 @@ function current_turn(tail) {
|
|
|
108
143
|
function turn_filing_count(tail) {
|
|
109
144
|
return prior_filing_count(current_turn(tail));
|
|
110
145
|
}
|
|
111
|
-
function decide(_call,
|
|
112
|
-
return prior_filing_count(time_density_hook.read_tail(
|
|
146
|
+
function decide(_call, run3) {
|
|
147
|
+
return prior_filing_count(time_density_hook.read_tail(run3.transcript)) >= CAP_REACHED;
|
|
113
148
|
}
|
|
114
149
|
var FILING_CAP_REASON = `\u26D4 filing cap: this run has already filed ${String(FILING_CAP)} Issues, the per-run ceiling \u2014 on reaching it, stop and report rather than filing more. Automatic filing is capped so a run cannot manufacture Issues faster than they can be closed (\`.claude/skills/workflow-commands/SKILL.md\` \u2192 \xA72d, \`observation-filing.md\`). A further Issue that genuinely must exist is a person's call, not this run's. **This rule fires on every filing past the cap, not once per run**, so reissuing the same filing will be refused again.`;
|
|
115
150
|
var ROW = {
|
|
@@ -149,9 +184,6 @@ var git_utilities = {
|
|
|
149
184
|
get_git_command_for_spawn
|
|
150
185
|
};
|
|
151
186
|
|
|
152
|
-
// scripts/git/git-push-transport.ts
|
|
153
|
-
import { execa as execa2 } from "execa";
|
|
154
|
-
|
|
155
187
|
// scripts/git/git-execa-error.ts
|
|
156
188
|
function get_exit_code(error) {
|
|
157
189
|
if (typeof error !== "object" || error === null) return void 0;
|
|
@@ -170,6 +202,9 @@ function create_spawn_error(command, exit_code) {
|
|
|
170
202
|
return new Error(error_message, { cause: { exit_code: exit_code_string } });
|
|
171
203
|
}
|
|
172
204
|
|
|
205
|
+
// scripts/git/git-push-transport.ts
|
|
206
|
+
import { execa as execa2 } from "execa";
|
|
207
|
+
|
|
173
208
|
// scripts/git/git-ssh-keepalive.ts
|
|
174
209
|
import { execa } from "execa";
|
|
175
210
|
var SSH_KEEPALIVE_OPTIONS = "-o ServerAliveInterval=15 -o ServerAliveCountMax=3";
|
|
@@ -300,6 +335,44 @@ var git_spawn = {
|
|
|
300
335
|
with_output_remote
|
|
301
336
|
};
|
|
302
337
|
|
|
338
|
+
// scripts/lib/spawn-exit.ts
|
|
339
|
+
function read_spawn_stdout(result) {
|
|
340
|
+
return result.stdout ?? "";
|
|
341
|
+
}
|
|
342
|
+
function read_spawn_stderr(result) {
|
|
343
|
+
return result.stderr ?? "";
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// scripts/git/git-spawn-sync.ts
|
|
347
|
+
import { execaSync } from "execa";
|
|
348
|
+
var GIT_TIMEOUT_MS = 1e4;
|
|
349
|
+
var SUCCESS_EXIT_CODE = 0;
|
|
350
|
+
var C_LOCALE = { LC_ALL: "C", LANGUAGE: "C" };
|
|
351
|
+
function run(args, options = {}) {
|
|
352
|
+
const result = execaSync(git_utilities.get_git_command_for_spawn(), args, {
|
|
353
|
+
...options,
|
|
354
|
+
extendEnv: true,
|
|
355
|
+
reject: false,
|
|
356
|
+
timeout: options.timeout ?? GIT_TIMEOUT_MS
|
|
357
|
+
});
|
|
358
|
+
return {
|
|
359
|
+
exit_code: result.exitCode,
|
|
360
|
+
stdout: read_spawn_stdout(result),
|
|
361
|
+
stderr: read_spawn_stderr(result)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function read2(args, options = {}) {
|
|
365
|
+
const result = run(args, options);
|
|
366
|
+
return result.exit_code === SUCCESS_EXIT_CODE ? result.stdout.trim() : void 0;
|
|
367
|
+
}
|
|
368
|
+
function origin_url(cwd) {
|
|
369
|
+
return read2(["config", "--get", "remote.origin.url"], { cwd });
|
|
370
|
+
}
|
|
371
|
+
function toplevel(timeout = GIT_TIMEOUT_MS) {
|
|
372
|
+
return run(["rev-parse", "--show-toplevel"], { env: C_LOCALE, timeout });
|
|
373
|
+
}
|
|
374
|
+
var git_spawn_sync = { GIT_TIMEOUT_MS, C_LOCALE, run, read: read2, origin_url, toplevel };
|
|
375
|
+
|
|
303
376
|
// scripts/git/git-command.ts
|
|
304
377
|
async function branch() {
|
|
305
378
|
return await git_spawn.read(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
@@ -309,7 +382,9 @@ async function status(directory) {
|
|
|
309
382
|
return await git_spawn.read([...location, "status", PORCELAIN_FLAG, UNTRACKED_FILES_FLAG]);
|
|
310
383
|
}
|
|
311
384
|
async function repository_root() {
|
|
312
|
-
|
|
385
|
+
const result = git_spawn_sync.toplevel();
|
|
386
|
+
if (result.exit_code !== 0) throw create_spawn_error("rev-parse", result.exit_code);
|
|
387
|
+
return result.stdout.trim();
|
|
313
388
|
}
|
|
314
389
|
async function head_commit() {
|
|
315
390
|
return await git_spawn.read(["rev-parse", "HEAD"]);
|
|
@@ -342,9 +417,9 @@ async function get_default_branch() {
|
|
|
342
417
|
return DEFAULT_BRANCH_FALLBACK;
|
|
343
418
|
}
|
|
344
419
|
var MERGE_BASE_COMMAND = "merge-base";
|
|
345
|
-
async function reference_exists(
|
|
420
|
+
async function reference_exists(reference2) {
|
|
346
421
|
try {
|
|
347
|
-
await git_spawn.read(["rev-parse", "--verify", "--quiet",
|
|
422
|
+
await git_spawn.read(["rev-parse", "--verify", "--quiet", reference2]);
|
|
348
423
|
return true;
|
|
349
424
|
} catch {
|
|
350
425
|
return false;
|
|
@@ -582,6 +657,18 @@ var git_command = {
|
|
|
582
657
|
is_upstream_not_set_error
|
|
583
658
|
};
|
|
584
659
|
|
|
660
|
+
// scripts/lib/error-message.ts
|
|
661
|
+
function message_of(error) {
|
|
662
|
+
return error instanceof Error ? error.message : String(error);
|
|
663
|
+
}
|
|
664
|
+
var DEBUG_ENV_KEY = "JOSH_DEBUG";
|
|
665
|
+
function trace_swallowed(where, error) {
|
|
666
|
+
if ((process.env[DEBUG_ENV_KEY] ?? "").trim() === "") return;
|
|
667
|
+
process.stderr.write(`josh debug: ${where}: ${message_of(error)}
|
|
668
|
+
`);
|
|
669
|
+
}
|
|
670
|
+
var error_text = { message_of, trace_swallowed };
|
|
671
|
+
|
|
585
672
|
// scripts/run/run-hold.ts
|
|
586
673
|
import { z } from "zod";
|
|
587
674
|
var HOLD_PREFIX = "josh-run-hold-";
|
|
@@ -657,7 +744,8 @@ async function is_tree_dirty(directory) {
|
|
|
657
744
|
try {
|
|
658
745
|
const status2 = await git_command.status(directory);
|
|
659
746
|
return status2.trim() !== "";
|
|
660
|
-
} catch {
|
|
747
|
+
} catch (error) {
|
|
748
|
+
error_text.trace_swallowed("run_hold.is_tree_dirty", error);
|
|
661
749
|
return true;
|
|
662
750
|
}
|
|
663
751
|
}
|
|
@@ -692,8 +780,8 @@ function stale_message(hold) {
|
|
|
692
780
|
function uncommitted_message(hold) {
|
|
693
781
|
return `This working tree still has uncommitted changes, and its run record \u2014 held by ${describe_holder(hold)} \u2014 has expired. That is a run which stopped for a person rather than one that finished, so the tree is not free: commit or stash the work, or run \`${own_release_command(hold.issue)}\` once you are done with it, then ask again.`;
|
|
694
782
|
}
|
|
695
|
-
function race_message(
|
|
696
|
-
if (
|
|
783
|
+
function race_message(read3, target) {
|
|
784
|
+
if (read3.kind === "held") return held_message(read3.hold);
|
|
697
785
|
return `A record already exists at ${target} and this run did not write it \u2014 ${NOT_IDLE}. Either another run claimed the tree at the same moment, or the record belongs to another account. Run \`${FORCE_RELEASE_COMMAND}\`, and remove that file by hand if it is still there.`;
|
|
698
786
|
}
|
|
699
787
|
function unknown_message() {
|
|
@@ -1300,9 +1388,9 @@ function directories(cwd) {
|
|
|
1300
1388
|
const key2 = cache_key(cwd);
|
|
1301
1389
|
const known = answers.get(key2);
|
|
1302
1390
|
if (known !== void 0) return known;
|
|
1303
|
-
const
|
|
1304
|
-
if (
|
|
1305
|
-
return
|
|
1391
|
+
const read3 = read_directories(cwd);
|
|
1392
|
+
if (read3.length > 0) answers.set(key2, read3);
|
|
1393
|
+
return read3;
|
|
1306
1394
|
}
|
|
1307
1395
|
function own(cwd) {
|
|
1308
1396
|
return directories(cwd)[OWN_GIT_DIRECTORY];
|
|
@@ -1616,8 +1704,8 @@ function worktree_git_directory_sync() {
|
|
|
1616
1704
|
}
|
|
1617
1705
|
function carried_cut_sync(now = /* @__PURE__ */ new Date(), directory = worktree_git_directory_sync()) {
|
|
1618
1706
|
if (directory === void 0) return void 0;
|
|
1619
|
-
const
|
|
1620
|
-
return
|
|
1707
|
+
const read3 = read_cut(cut_path(directory), now);
|
|
1708
|
+
return read3.kind === "carried" ? read3.cut : void 0;
|
|
1621
1709
|
}
|
|
1622
1710
|
function lane_cut_sync(lane_directory) {
|
|
1623
1711
|
const directory = git_common_directory.own(lane_directory);
|
|
@@ -1684,8 +1772,8 @@ function end_cut(target) {
|
|
|
1684
1772
|
}
|
|
1685
1773
|
function hold_of(directory) {
|
|
1686
1774
|
if (directory === void 0) return void 0;
|
|
1687
|
-
const
|
|
1688
|
-
return
|
|
1775
|
+
const read3 = run_hold.read_hold(run_hold.hold_path(directory));
|
|
1776
|
+
return read3.kind === "held" || read3.kind === "stale" ? read3.hold : void 0;
|
|
1689
1777
|
}
|
|
1690
1778
|
function fullrun_issue(hold) {
|
|
1691
1779
|
return hold?.is_fullrun === true ? hold.issue : void 0;
|
|
@@ -1989,13 +2077,13 @@ function is_live_beacon(target) {
|
|
|
1989
2077
|
timeout: PROBE_TIMEOUT_MS2
|
|
1990
2078
|
}).status === 0;
|
|
1991
2079
|
}
|
|
1992
|
-
function has_matching_start(pid, recorded_start,
|
|
1993
|
-
const observed =
|
|
2080
|
+
function has_matching_start(pid, recorded_start, read3) {
|
|
2081
|
+
const observed = read3(pid);
|
|
1994
2082
|
return observed === void 0 ? void 0 : observed === recorded_start;
|
|
1995
2083
|
}
|
|
1996
|
-
function matches_recorded_start(pid, recorded_start,
|
|
2084
|
+
function matches_recorded_start(pid, recorded_start, read3) {
|
|
1997
2085
|
const target = beacon_target(recorded_start);
|
|
1998
|
-
return target === void 0 ? has_matching_start(pid, recorded_start,
|
|
2086
|
+
return target === void 0 ? has_matching_start(pid, recorded_start, read3) : is_live_beacon(target);
|
|
1999
2087
|
}
|
|
2000
2088
|
var own_probe = /* @__PURE__ */ new Map();
|
|
2001
2089
|
function own_start() {
|
|
@@ -2006,10 +2094,10 @@ function own_fields() {
|
|
|
2006
2094
|
const start = own_start();
|
|
2007
2095
|
return { pid: process.pid, ...start !== void 0 && { process_start: start } };
|
|
2008
2096
|
}
|
|
2009
|
-
function is_same_process(pid, recorded_start,
|
|
2097
|
+
function is_same_process(pid, recorded_start, read3 = read_start) {
|
|
2010
2098
|
if (pid === void 0 || !is_live_pid(pid)) return false;
|
|
2011
2099
|
if (recorded_start === void 0) return void 0;
|
|
2012
|
-
return matches_recorded_start(pid, recorded_start,
|
|
2100
|
+
return matches_recorded_start(pid, recorded_start, read3);
|
|
2013
2101
|
}
|
|
2014
2102
|
function is_own_process(pid, recorded_start) {
|
|
2015
2103
|
return pid === process.pid && recorded_start === own_start();
|
|
@@ -2188,11 +2276,20 @@ function parse_or_undefined(config) {
|
|
|
2188
2276
|
return void 0;
|
|
2189
2277
|
}
|
|
2190
2278
|
}
|
|
2191
|
-
|
|
2279
|
+
function values_of(argv, options) {
|
|
2280
|
+
return parse_or_undefined({ args: [...argv], options })?.values;
|
|
2281
|
+
}
|
|
2282
|
+
function arguments_of(argv, options) {
|
|
2283
|
+
return parse_or_undefined({ args: [...argv], options, allowPositionals: true });
|
|
2284
|
+
}
|
|
2285
|
+
function string_of(value) {
|
|
2286
|
+
return typeof value === "string" ? value : void 0;
|
|
2287
|
+
}
|
|
2288
|
+
var cli_flags = { refuse_unknown_flags, parse_or_undefined, values_of, arguments_of, string_of };
|
|
2192
2289
|
|
|
2193
2290
|
// scripts/backlog/backlog-budget-cli.ts
|
|
2194
2291
|
var ARGV_OFFSET = 2;
|
|
2195
|
-
var
|
|
2292
|
+
var SUCCESS_EXIT_CODE2 = 0;
|
|
2196
2293
|
var FAILURE_EXIT_CODE2 = 1;
|
|
2197
2294
|
var JSON_KEY = "budget";
|
|
2198
2295
|
var USAGE = `Usage: josh backlog:budget --answer <${backlog_budget.ANSWERS.join("|")}> --started <ISO-8601> [--active <ISO-8601>, required unless the watch is off] [--merged <count>] [--running <count>] [--max <count>] [--idle <minutes>, 0 to turn the watch off] [--json]`;
|
|
@@ -2207,10 +2304,7 @@ var OPTIONS = {
|
|
|
2207
2304
|
started: { type: "string" }
|
|
2208
2305
|
};
|
|
2209
2306
|
function read_arguments(argv) {
|
|
2210
|
-
return cli_flags.
|
|
2211
|
-
}
|
|
2212
|
-
function text_of(value) {
|
|
2213
|
-
return typeof value === "string" ? value : void 0;
|
|
2307
|
+
return cli_flags.values_of(argv, OPTIONS);
|
|
2214
2308
|
}
|
|
2215
2309
|
function to_answer(raw) {
|
|
2216
2310
|
return backlog_budget.ANSWERS.find((answer) => answer === raw);
|
|
@@ -2232,14 +2326,14 @@ function is_readable(raw, parsed) {
|
|
|
2232
2326
|
var COUNT_NAMES = ["idle", "max", "merged", "running"];
|
|
2233
2327
|
function counts_of(values) {
|
|
2234
2328
|
return {
|
|
2235
|
-
idle: to_count(
|
|
2236
|
-
max: to_count(
|
|
2237
|
-
merged: to_count(
|
|
2238
|
-
running: to_count(
|
|
2329
|
+
idle: to_count(cli_flags.string_of(values.idle)),
|
|
2330
|
+
max: to_count(cli_flags.string_of(values.max)),
|
|
2331
|
+
merged: to_count(cli_flags.string_of(values.merged)),
|
|
2332
|
+
running: to_count(cli_flags.string_of(values.running))
|
|
2239
2333
|
};
|
|
2240
2334
|
}
|
|
2241
2335
|
function are_counts_readable(values, counts) {
|
|
2242
|
-
return COUNT_NAMES.every((name) => is_readable(
|
|
2336
|
+
return COUNT_NAMES.every((name) => is_readable(cli_flags.string_of(values[name]), counts[name]));
|
|
2243
2337
|
}
|
|
2244
2338
|
var NO_IDLE_MINUTES = 0;
|
|
2245
2339
|
function to_idle_ms(idle_minutes) {
|
|
@@ -2248,17 +2342,20 @@ function to_idle_ms(idle_minutes) {
|
|
|
2248
2342
|
return idle_minutes * backlog_budget.MS_PER_MINUTE;
|
|
2249
2343
|
}
|
|
2250
2344
|
function is_watch_readable(values) {
|
|
2251
|
-
if (to_idle_ms(to_count(
|
|
2252
|
-
return
|
|
2345
|
+
if (to_idle_ms(to_count(cli_flags.string_of(values.idle))) === void 0) return true;
|
|
2346
|
+
return cli_flags.string_of(values.active) !== void 0;
|
|
2253
2347
|
}
|
|
2254
2348
|
function are_values_readable(values) {
|
|
2255
|
-
return is_readable(
|
|
2349
|
+
return is_readable(
|
|
2350
|
+
cli_flags.string_of(values.active),
|
|
2351
|
+
to_time_ms(cli_flags.string_of(values.active))
|
|
2352
|
+
) && is_watch_readable(values) && are_counts_readable(values, counts_of(values));
|
|
2256
2353
|
}
|
|
2257
2354
|
function input_of(values, base) {
|
|
2258
2355
|
const counts = counts_of(values);
|
|
2259
2356
|
return {
|
|
2260
2357
|
...base,
|
|
2261
|
-
active_at_ms: to_time_ms(
|
|
2358
|
+
active_at_ms: to_time_ms(cli_flags.string_of(values.active)) ?? base.started_at_ms,
|
|
2262
2359
|
merged: counts.merged ?? 0,
|
|
2263
2360
|
running: counts.running ?? 0,
|
|
2264
2361
|
max_issues: counts.max,
|
|
@@ -2266,9 +2363,9 @@ function input_of(values, base) {
|
|
|
2266
2363
|
};
|
|
2267
2364
|
}
|
|
2268
2365
|
function build_input(values, now_ms) {
|
|
2269
|
-
const answer = to_answer(
|
|
2366
|
+
const answer = to_answer(cli_flags.string_of(values.answer));
|
|
2270
2367
|
if (answer === void 0) return void 0;
|
|
2271
|
-
const started_at_ms = to_time_ms(
|
|
2368
|
+
const started_at_ms = to_time_ms(cli_flags.string_of(values.started));
|
|
2272
2369
|
if (started_at_ms === void 0) return void 0;
|
|
2273
2370
|
if (!are_values_readable(values)) return void 0;
|
|
2274
2371
|
return input_of(values, { answer, now_ms, started_at_ms });
|
|
@@ -2277,7 +2374,7 @@ function refuse() {
|
|
|
2277
2374
|
console.error(USAGE);
|
|
2278
2375
|
return FAILURE_EXIT_CODE2;
|
|
2279
2376
|
}
|
|
2280
|
-
function
|
|
2377
|
+
function run2(argv, now_ms = Date.now()) {
|
|
2281
2378
|
const values = read_arguments(argv);
|
|
2282
2379
|
if (values === void 0) return refuse();
|
|
2283
2380
|
const input = build_input(values, now_ms);
|
|
@@ -2287,23 +2384,23 @@ function run(argv, now_ms = Date.now()) {
|
|
|
2287
2384
|
console.info(
|
|
2288
2385
|
JSON.stringify({ [JSON_KEY]: verdict, reason, is_finish: backlog_budget.is_finish(input) })
|
|
2289
2386
|
);
|
|
2290
|
-
return
|
|
2387
|
+
return SUCCESS_EXIT_CODE2;
|
|
2291
2388
|
}
|
|
2292
2389
|
path_decision.print_decision(JSON_KEY, verdict, reason, false);
|
|
2293
|
-
return
|
|
2390
|
+
return SUCCESS_EXIT_CODE2;
|
|
2294
2391
|
}
|
|
2295
2392
|
function main(argv) {
|
|
2296
|
-
process.exitCode =
|
|
2393
|
+
process.exitCode = run2(argv);
|
|
2297
2394
|
}
|
|
2298
2395
|
var backlog_budget_cli = {
|
|
2299
2396
|
JSON_KEY,
|
|
2300
2397
|
OPTIONS,
|
|
2301
|
-
SUCCESS_EXIT_CODE,
|
|
2398
|
+
SUCCESS_EXIT_CODE: SUCCESS_EXIT_CODE2,
|
|
2302
2399
|
USAGE,
|
|
2303
2400
|
build_input,
|
|
2304
2401
|
main,
|
|
2305
2402
|
read_arguments,
|
|
2306
|
-
run,
|
|
2403
|
+
run: run2,
|
|
2307
2404
|
to_answer,
|
|
2308
2405
|
to_count,
|
|
2309
2406
|
to_time_ms
|
|
@@ -2342,10 +2439,10 @@ function flag_tokens(tokens, start) {
|
|
|
2342
2439
|
const rendered = [];
|
|
2343
2440
|
let index = start;
|
|
2344
2441
|
while (index < tokens.length) {
|
|
2345
|
-
const
|
|
2346
|
-
if (
|
|
2347
|
-
rendered.push(
|
|
2348
|
-
index +=
|
|
2442
|
+
const read3 = read_flag(tokens, index);
|
|
2443
|
+
if (read3 === void 0) return void 0;
|
|
2444
|
+
rendered.push(read3.part);
|
|
2445
|
+
index += read3.advance;
|
|
2349
2446
|
}
|
|
2350
2447
|
return rendered;
|
|
2351
2448
|
}
|
|
@@ -2472,8 +2569,8 @@ function classify3(raw, now) {
|
|
|
2472
2569
|
function read_carry(target, now = /* @__PURE__ */ new Date()) {
|
|
2473
2570
|
return classify3(stamp_file.read_stamp_text(target), now);
|
|
2474
2571
|
}
|
|
2475
|
-
function owner_of(pid,
|
|
2476
|
-
return { pid, start:
|
|
2572
|
+
function owner_of(pid, read3 = process_identity.read_start) {
|
|
2573
|
+
return { pid, start: read3(pid) };
|
|
2477
2574
|
}
|
|
2478
2575
|
function fresh_carry(invocation, owner, now) {
|
|
2479
2576
|
return {
|
|
@@ -2533,9 +2630,9 @@ function is_at_cut_cap(carry) {
|
|
|
2533
2630
|
function end_carry(target) {
|
|
2534
2631
|
stamp_file.remove_stamp(target);
|
|
2535
2632
|
}
|
|
2536
|
-
function retrospective_done_of(
|
|
2537
|
-
if (
|
|
2538
|
-
return
|
|
2633
|
+
function retrospective_done_of(read3) {
|
|
2634
|
+
if (read3.kind !== "carried" && read3.kind !== "expired") return false;
|
|
2635
|
+
return read3.carry.retrospective === true;
|
|
2539
2636
|
}
|
|
2540
2637
|
function remaining_of(carry) {
|
|
2541
2638
|
if (carry === void 0) return void 0;
|
|
@@ -2634,11 +2731,11 @@ import { existsSync, lstatSync, mkdirSync, readlinkSync, symlinkSync, unlinkSync
|
|
|
2634
2731
|
import path3 from "node:path";
|
|
2635
2732
|
|
|
2636
2733
|
// scripts/gh/gh-cli-token.ts
|
|
2637
|
-
import { execaSync } from "execa";
|
|
2734
|
+
import { execaSync as execaSync2 } from "execa";
|
|
2638
2735
|
var GH_CLI_TIMEOUT_MS = 5e3;
|
|
2639
2736
|
function get() {
|
|
2640
2737
|
try {
|
|
2641
|
-
const { stdout } =
|
|
2738
|
+
const { stdout } = execaSync2("gh", ["auth", "token"], { timeout: GH_CLI_TIMEOUT_MS });
|
|
2642
2739
|
const token = stdout.trim();
|
|
2643
2740
|
return token.length > 0 ? token : void 0;
|
|
2644
2741
|
} catch {
|
|
@@ -2979,9 +3076,9 @@ function scoped_events(events, scope) {
|
|
|
2979
3076
|
if (floor === void 0) return void 0;
|
|
2980
3077
|
return events.filter((event) => is_within(event, floor));
|
|
2981
3078
|
}
|
|
2982
|
-
function scope_of(
|
|
2983
|
-
if (
|
|
2984
|
-
return { kind: SINCE_SCOPE, started_at:
|
|
3079
|
+
function scope_of(read3) {
|
|
3080
|
+
if (read3.kind === "carried" || read3.kind === "expired") {
|
|
3081
|
+
return { kind: SINCE_SCOPE, started_at: read3.carry.started_at };
|
|
2985
3082
|
}
|
|
2986
3083
|
return UNKNOWN_EVENT_SCOPE;
|
|
2987
3084
|
}
|
|
@@ -3060,7 +3157,8 @@ async function emit_once(kind, text) {
|
|
|
3060
3157
|
const target = await stream_target();
|
|
3061
3158
|
if (target === void 0 || run_event_stream.read_last(target)?.kind === kind) return false;
|
|
3062
3159
|
return run_event_stream.append(target, kind, text, now_iso()).appended;
|
|
3063
|
-
} catch {
|
|
3160
|
+
} catch (error) {
|
|
3161
|
+
error_text.trace_swallowed("run_event_stream_emit.emit_once", error);
|
|
3064
3162
|
return false;
|
|
3065
3163
|
}
|
|
3066
3164
|
}
|
|
@@ -3078,7 +3176,8 @@ async function emit_once_since(kind, text, reset_kind) {
|
|
|
3078
3176
|
return false;
|
|
3079
3177
|
}
|
|
3080
3178
|
return run_event_stream.append(target, kind, text, now_iso()).appended;
|
|
3081
|
-
} catch {
|
|
3179
|
+
} catch (error) {
|
|
3180
|
+
error_text.trace_swallowed("run_event_stream_emit.emit_once_since", error);
|
|
3082
3181
|
return false;
|
|
3083
3182
|
}
|
|
3084
3183
|
}
|
|
@@ -3089,8 +3188,8 @@ async function current_events() {
|
|
|
3089
3188
|
try {
|
|
3090
3189
|
const repository2 = await run_carry.repository_directory();
|
|
3091
3190
|
if (repository2 === void 0) return [];
|
|
3092
|
-
const
|
|
3093
|
-
const scope = run_event_scope.scope_of(
|
|
3191
|
+
const read3 = run_carry.read_carry(run_carry.carry_path(repository2));
|
|
3192
|
+
const scope = run_event_scope.scope_of(read3);
|
|
3094
3193
|
const events = run_event_stream.read_events(run_event_stream.target_of(repository2));
|
|
3095
3194
|
return run_event_scope.scoped_events(events, scope) ?? [];
|
|
3096
3195
|
} catch {
|
|
@@ -3474,7 +3573,9 @@ function is_confirmation_notify(command) {
|
|
|
3474
3573
|
);
|
|
3475
3574
|
}
|
|
3476
3575
|
var NOTIFY_INVOCATION = /josh\s+(?:notify|nf)\b/u;
|
|
3576
|
+
var COMMAND_NOTIFY_MARKER = "stop notified: a confirmation Telegram was sent by this command";
|
|
3477
3577
|
function mentions_confirmation_notify(text) {
|
|
3578
|
+
if (text.includes(COMMAND_NOTIFY_MARKER)) return true;
|
|
3478
3579
|
return NOTIFY_INVOCATION.test(text) && CONFIRMATION_TASK_TYPE.test(text);
|
|
3479
3580
|
}
|
|
3480
3581
|
function is_parking_child(directory = process.cwd()) {
|
|
@@ -3493,6 +3594,7 @@ function records_the_park(command) {
|
|
|
3493
3594
|
}
|
|
3494
3595
|
var LANE_PARK_REASON = '\u26D4 lane child park: this session is a dispatched lane child, and a stop that waits on a person must leave the question on the Issue before it sends this Telegram \u2014 otherwise the parent finds an OPEN Issue with `in-progress` and no question, and a person cannot tell what to answer (joshuafolkken/kit#2034 measured this twice in one run). The child is the only process that knows the question, the options and whether it stashed, so it records the park itself rather than leaving the parent to guess from a log. Before you notify and stop: apply `needs-decision` and post a comment carrying the question, the options, and whether work was stashed \u2014 `gh api repos/{owner}/{repo}/issues/<N>/labels -f \'labels[]=needs-decision\'` then `pnpm josh issue:comment <N> --body-file <path>`. If this stop is a `needs-human-review` or `already-done` one, that label is already on the Issue and there is nothing to add. Then reissue this notify \u2014 it fires once per run and cannot repeat on the call in hand. The procedure is `.claude/skills/workflow-commands/pre-gate-cut.md` \u2192 "A lane child records its park before it stops", and the park itself is `backlogrun-park.md` \u2192 "park and continue".';
|
|
3495
3596
|
var lane_park = {
|
|
3597
|
+
COMMAND_NOTIFY_MARKER,
|
|
3496
3598
|
LANE_PARK_REASON,
|
|
3497
3599
|
is_confirmation_notify,
|
|
3498
3600
|
is_parking_child,
|
|
@@ -3598,9 +3700,9 @@ var PATH_INDEX = 1;
|
|
|
3598
3700
|
function is_same_owner(identity, current_owner2) {
|
|
3599
3701
|
return identity.owner.toLowerCase() === current_owner2.toLowerCase();
|
|
3600
3702
|
}
|
|
3601
|
-
function add_entry(target,
|
|
3602
|
-
if (
|
|
3603
|
-
const identity = repo_origin.parse_origin_url(
|
|
3703
|
+
function add_entry(target, origin_url2, repository_path, current_owner2) {
|
|
3704
|
+
if (origin_url2 === void 0) return;
|
|
3705
|
+
const identity = repo_origin.parse_origin_url(origin_url2);
|
|
3604
3706
|
if (identity === void 0 || !is_same_owner(identity, current_owner2)) return;
|
|
3605
3707
|
target.set(repo_origin.format_identity(identity), repository_path);
|
|
3606
3708
|
}
|
|
@@ -3685,9 +3787,9 @@ function scan_siblings(parent) {
|
|
|
3685
3787
|
}));
|
|
3686
3788
|
}
|
|
3687
3789
|
function resolve_current_owner(repository_path) {
|
|
3688
|
-
const
|
|
3689
|
-
if (
|
|
3690
|
-
return repo_origin.parse_origin_url(
|
|
3790
|
+
const origin_url2 = read_origin_url(repository_path);
|
|
3791
|
+
if (origin_url2 === void 0) return void 0;
|
|
3792
|
+
return repo_origin.parse_origin_url(origin_url2)?.owner;
|
|
3691
3793
|
}
|
|
3692
3794
|
function main_worktree(repository_path) {
|
|
3693
3795
|
const git_directory = resolve_git_directory(repository_path);
|
|
@@ -4181,8 +4283,8 @@ function environment() {
|
|
|
4181
4283
|
function is_headless(source = process.env) {
|
|
4182
4284
|
return source[HEADLESS_ENV_KEY] === HEADLESS_VALUE;
|
|
4183
4285
|
}
|
|
4184
|
-
function is_driving(
|
|
4185
|
-
return
|
|
4286
|
+
function is_driving(read3) {
|
|
4287
|
+
return read3?.kind === "carried" && read3.carry.is_handed_off !== true;
|
|
4186
4288
|
}
|
|
4187
4289
|
async function read_carry_here() {
|
|
4188
4290
|
const repository2 = await run_carry.repository_directory();
|
|
@@ -4194,15 +4296,15 @@ async function must_keep_waiting(source = process.env) {
|
|
|
4194
4296
|
return is_driving(await read_carry_here());
|
|
4195
4297
|
}
|
|
4196
4298
|
async function current_carry() {
|
|
4197
|
-
const
|
|
4198
|
-
return
|
|
4299
|
+
const read3 = await read_carry_here();
|
|
4300
|
+
return read3?.kind === "carried" ? read3.carry : void 0;
|
|
4199
4301
|
}
|
|
4200
4302
|
function is_owned_here(carry, ancestry) {
|
|
4201
4303
|
return carry.owner_pid !== void 0 && ancestry().has(carry.owner_pid);
|
|
4202
4304
|
}
|
|
4203
|
-
function driven_here(
|
|
4204
|
-
if (
|
|
4205
|
-
return is_owned_here(
|
|
4305
|
+
function driven_here(read3, ancestry) {
|
|
4306
|
+
if (read3?.kind !== "carried" || !is_driving(read3)) return void 0;
|
|
4307
|
+
return is_owned_here(read3.carry, ancestry) ? read3.carry : void 0;
|
|
4206
4308
|
}
|
|
4207
4309
|
async function driving_carry(source = process.env, ancestry = lane_reap.own_ancestry) {
|
|
4208
4310
|
if (lane_child_marker.marked_issue(source) !== void 0) return void 0;
|
|
@@ -4234,11 +4336,13 @@ function issue_url(slug, number) {
|
|
|
4234
4336
|
function citation_line(slug, number, summary) {
|
|
4235
4337
|
return `[#${number}](${issue_url(slug, number)})${SUMMARY_SEPARATOR}${summary}`;
|
|
4236
4338
|
}
|
|
4339
|
+
function reference(slug, number, title) {
|
|
4340
|
+
if (slug === void 0) return `#${number}`;
|
|
4341
|
+
return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
|
|
4342
|
+
}
|
|
4237
4343
|
function citer(slug, titles) {
|
|
4238
4344
|
return function cite(number) {
|
|
4239
|
-
|
|
4240
|
-
const title = titles.get(number);
|
|
4241
|
-
return title === void 0 ? `[#${number}](${issue_url(slug, number)})` : citation_line(slug, number, title);
|
|
4345
|
+
return reference(slug, number, titles.get(number));
|
|
4242
4346
|
};
|
|
4243
4347
|
}
|
|
4244
4348
|
function qualified_target(token) {
|
|
@@ -4275,6 +4379,7 @@ var issue_cite = {
|
|
|
4275
4379
|
missing_line,
|
|
4276
4380
|
no_repo_line,
|
|
4277
4381
|
parse_target,
|
|
4382
|
+
reference,
|
|
4278
4383
|
unreadable_line
|
|
4279
4384
|
};
|
|
4280
4385
|
|
|
@@ -4340,19 +4445,13 @@ var git_gh_api_path = {
|
|
|
4340
4445
|
commit_status_api_path
|
|
4341
4446
|
};
|
|
4342
4447
|
|
|
4448
|
+
// scripts/gh/git-gh-exec.ts
|
|
4449
|
+
import { execa as execa5, execaSync as execaSync3 } from "execa";
|
|
4450
|
+
|
|
4343
4451
|
// scripts/lib/timeouts.ts
|
|
4344
4452
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
4345
4453
|
var COMMAND_TIMEOUT_MS = 3e4;
|
|
4346
4454
|
|
|
4347
|
-
// scripts/lib/error-message.ts
|
|
4348
|
-
function message_of(error) {
|
|
4349
|
-
return error instanceof Error ? error.message : String(error);
|
|
4350
|
-
}
|
|
4351
|
-
var error_text = { message_of };
|
|
4352
|
-
|
|
4353
|
-
// scripts/gh/git-gh-exec.ts
|
|
4354
|
-
import { execa as execa5, execaSync as execaSync2 } from "execa";
|
|
4355
|
-
|
|
4356
4455
|
// scripts/gh/git-gh-check.ts
|
|
4357
4456
|
import { execa as execa4 } from "execa";
|
|
4358
4457
|
var GH_NOT_INSTALLED_MSG = "gh CLI is not installed. Install it from https://cli.github.com/";
|
|
@@ -4510,7 +4609,7 @@ async function exec_gh_api_status(path6, timeout_ms) {
|
|
|
4510
4609
|
function exec_gh_api_status_sync(path6, timeout_ms) {
|
|
4511
4610
|
const options = to_spawn_options(timeout_ms);
|
|
4512
4611
|
try {
|
|
4513
|
-
const { stdout } =
|
|
4612
|
+
const { stdout } = execaSync3("gh", ["api", "--include", "--silent", path6], options);
|
|
4514
4613
|
return parse_status_line(stdout);
|
|
4515
4614
|
} catch (error) {
|
|
4516
4615
|
return status_of_failure(error);
|
|
@@ -4554,7 +4653,7 @@ function exec_gh_api_sync(request) {
|
|
|
4554
4653
|
const body_option = request.body === void 0 ? {} : { input: request.body };
|
|
4555
4654
|
const options = { ...body_option, ...to_spawn_options(request.timeout_ms, request.cwd) };
|
|
4556
4655
|
try {
|
|
4557
|
-
const { stdout } =
|
|
4656
|
+
const { stdout } = execaSync3("gh", to_gh_api_args(request), options);
|
|
4558
4657
|
return stdout.trimEnd();
|
|
4559
4658
|
} catch (error) {
|
|
4560
4659
|
throw to_gh_error(error);
|
|
@@ -4579,33 +4678,1706 @@ var git_gh_exec = {
|
|
|
4579
4678
|
parse_status_line
|
|
4580
4679
|
};
|
|
4581
4680
|
|
|
4681
|
+
// scripts/gh/git-gh-helpers.ts
|
|
4682
|
+
function parse_pr_state_string(result) {
|
|
4683
|
+
const trimmed2 = result.trim();
|
|
4684
|
+
return trimmed2.length > 0 ? trimmed2 : void 0;
|
|
4685
|
+
}
|
|
4686
|
+
function is_pr_already_exists_message(error_message) {
|
|
4687
|
+
return error_message.toLowerCase().includes("already exists");
|
|
4688
|
+
}
|
|
4689
|
+
function get_error_message_with_stderr(error) {
|
|
4690
|
+
if (!(error instanceof Error)) return String(error);
|
|
4691
|
+
if (has_stderr_field(error) && error.stderr.length > 0) return `${error.message}
|
|
4692
|
+
${error.stderr}`;
|
|
4693
|
+
return error.message;
|
|
4694
|
+
}
|
|
4695
|
+
function handle_pr_create_error(error) {
|
|
4696
|
+
const error_message = get_error_message_with_stderr(error);
|
|
4697
|
+
if (is_pr_already_exists_message(error_message)) {
|
|
4698
|
+
throw new Error("PR_ALREADY_EXISTS");
|
|
4699
|
+
}
|
|
4700
|
+
throw error;
|
|
4701
|
+
}
|
|
4702
|
+
var git_gh_helpers = {
|
|
4703
|
+
parse_pr_state_string,
|
|
4704
|
+
get_error_message_with_stderr,
|
|
4705
|
+
handle_pr_create_error
|
|
4706
|
+
};
|
|
4707
|
+
|
|
4708
|
+
// scripts/git/schemas.ts
|
|
4709
|
+
import { z as z11 } from "zod";
|
|
4710
|
+
var package_name_schema = z11.object({ name: z11.string().min(1) });
|
|
4711
|
+
var rollup_item_schema = z11.looseObject({
|
|
4712
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- GraphQL field name from GitHub API
|
|
4713
|
+
__typename: z11.string().optional(),
|
|
4714
|
+
state: z11.string().optional(),
|
|
4715
|
+
status: z11.string().optional(),
|
|
4716
|
+
conclusion: z11.string().optional(),
|
|
4717
|
+
name: z11.string().optional(),
|
|
4718
|
+
context: z11.string().optional()
|
|
4719
|
+
});
|
|
4720
|
+
var pr_raw_schema = z11.object({
|
|
4721
|
+
statusCheckRollup: z11.array(rollup_item_schema).optional(),
|
|
4722
|
+
mergeStateStatus: z11.string().optional(),
|
|
4723
|
+
reviewDecision: z11.string().optional()
|
|
4724
|
+
});
|
|
4725
|
+
var pull_comment_schema = z11.object({
|
|
4726
|
+
body: z11.string().optional(),
|
|
4727
|
+
html_url: z11.string().optional(),
|
|
4728
|
+
user: z11.object({ login: z11.string().optional() }).optional()
|
|
4729
|
+
});
|
|
4730
|
+
var ai_review_pull_comment_schema = z11.object({
|
|
4731
|
+
body: z11.string().optional(),
|
|
4732
|
+
url: z11.string().optional(),
|
|
4733
|
+
author: z11.object({ login: z11.string().optional() }).optional()
|
|
4734
|
+
});
|
|
4735
|
+
var pr_info_schema = z11.object({
|
|
4736
|
+
mergeable: z11.union([z11.boolean(), z11.string()]).nullable().optional(),
|
|
4737
|
+
mergeStateStatus: z11.string().nullable().optional(),
|
|
4738
|
+
state: z11.string().optional()
|
|
4739
|
+
});
|
|
4740
|
+
var epic_issue_schema = z11.object({
|
|
4741
|
+
number: z11.number(),
|
|
4742
|
+
body: z11.string().optional()
|
|
4743
|
+
});
|
|
4744
|
+
var blocking_issue_schema = z11.object({
|
|
4745
|
+
number: z11.number(),
|
|
4746
|
+
state: z11.string().optional(),
|
|
4747
|
+
repository_url: z11.string().optional()
|
|
4748
|
+
});
|
|
4749
|
+
var blocked_by_schema = z11.object({
|
|
4750
|
+
nodes: z11.array(blocking_issue_schema).default([]),
|
|
4751
|
+
totalCount: z11.number().optional()
|
|
4752
|
+
}).optional();
|
|
4753
|
+
var epic_child_schema = z11.object({
|
|
4754
|
+
state: z11.string().optional(),
|
|
4755
|
+
blockedBy: blocked_by_schema
|
|
4756
|
+
});
|
|
4757
|
+
var rest_comment_schema = z11.looseObject({
|
|
4758
|
+
body: z11.string().nullish(),
|
|
4759
|
+
html_url: z11.string().optional(),
|
|
4760
|
+
user: z11.looseObject({ login: z11.string().optional() }).nullish()
|
|
4761
|
+
});
|
|
4762
|
+
var issue_label_schema = z11.object({ name: z11.string() });
|
|
4763
|
+
var epic_subject_schema = z11.object({
|
|
4764
|
+
number: z11.number(),
|
|
4765
|
+
state: z11.string().optional(),
|
|
4766
|
+
labels: z11.array(issue_label_schema).optional(),
|
|
4767
|
+
body: z11.string().optional()
|
|
4768
|
+
});
|
|
4769
|
+
var open_issue_schema = z11.object({
|
|
4770
|
+
number: z11.number(),
|
|
4771
|
+
title: z11.string(),
|
|
4772
|
+
labels: z11.array(issue_label_schema).optional(),
|
|
4773
|
+
createdAt: z11.string(),
|
|
4774
|
+
blockedBy: blocked_by_schema
|
|
4775
|
+
});
|
|
4776
|
+
|
|
4777
|
+
// scripts/gh/git-gh-issue-rest.ts
|
|
4778
|
+
import { z as z12 } from "zod";
|
|
4779
|
+
|
|
4780
|
+
// scripts/gh/git-gh-rest-state.ts
|
|
4781
|
+
var MERGED_STATE = "MERGED";
|
|
4782
|
+
function to_gh_state(state) {
|
|
4783
|
+
return state?.toUpperCase();
|
|
4784
|
+
}
|
|
4785
|
+
|
|
4786
|
+
// scripts/gh/git-gh-issue-rest.ts
|
|
4787
|
+
var rest_issue_schema = z12.looseObject({
|
|
4788
|
+
number: z12.number(),
|
|
4789
|
+
issue_dependencies_summary: z12.looseObject({ total_blocked_by: z12.number().optional() }).nullish(),
|
|
4790
|
+
// Present only on a pull request, which this endpoint serves as readily as an issue.
|
|
4791
|
+
pull_request: z12.looseObject({ merged_at: z12.string().nullish() }).nullish()
|
|
4792
|
+
});
|
|
4793
|
+
var REPOSITORY_URL = /\/repos\/([\w.-]+\/[\w.-]+)$/u;
|
|
4794
|
+
var REPOSITORY_URL_GROUP = 1;
|
|
4795
|
+
var BLOCKED_BY_FIELD = "blockedBy";
|
|
4796
|
+
var BLOCKED_BY_COUNT_FIELD = "blocked_by_count";
|
|
4797
|
+
var STATE_FIELD = "state";
|
|
4798
|
+
var BODY_FIELD = "body";
|
|
4799
|
+
var FIELD_SEPARATOR2 = ",";
|
|
4800
|
+
var EMPTY_BODY = "";
|
|
4801
|
+
var REST_FIELD_NAMES = /* @__PURE__ */ new Map([
|
|
4802
|
+
["url", "html_url"],
|
|
4803
|
+
["createdAt", "created_at"]
|
|
4804
|
+
]);
|
|
4805
|
+
var NOT_AN_ISSUE_MESSAGE = "gh api answered something other than an issue object";
|
|
4806
|
+
var NOT_AN_ISSUE_LISTING_MESSAGE = "gh api answered something other than an issue listing";
|
|
4807
|
+
var NOT_A_BLOCKER_LISTING_MESSAGE = "gh api answered something other than a blocked-by listing";
|
|
4808
|
+
function rest_field_name(field) {
|
|
4809
|
+
return REST_FIELD_NAMES.get(field) ?? field;
|
|
4810
|
+
}
|
|
4811
|
+
function split_fields(fields) {
|
|
4812
|
+
return fields.split(FIELD_SEPARATOR2).map((field) => field.trim()).filter((field) => field.length > 0);
|
|
4813
|
+
}
|
|
4814
|
+
function to_issue_state(rest, value) {
|
|
4815
|
+
if (typeof rest.pull_request?.merged_at === "string") return MERGED_STATE;
|
|
4816
|
+
return typeof value === "string" ? to_gh_state(value) : value;
|
|
4817
|
+
}
|
|
4818
|
+
function total_blocked_by(rest) {
|
|
4819
|
+
return rest.issue_dependencies_summary?.total_blocked_by;
|
|
4820
|
+
}
|
|
4821
|
+
function to_gh_field_value(field, rest) {
|
|
4822
|
+
if (field === BLOCKED_BY_COUNT_FIELD) return total_blocked_by(rest);
|
|
4823
|
+
const value = rest[rest_field_name(field)];
|
|
4824
|
+
if (field === STATE_FIELD) return to_issue_state(rest, value);
|
|
4825
|
+
return field === BODY_FIELD ? value ?? EMPTY_BODY : value;
|
|
4826
|
+
}
|
|
4827
|
+
function parse_rest_issue(rest_json) {
|
|
4828
|
+
const parsed = parse_json_object_safe(rest_json, rest_issue_schema);
|
|
4829
|
+
if (parsed === void 0) throw new Error(NOT_AN_ISSUE_MESSAGE);
|
|
4830
|
+
return parsed;
|
|
4831
|
+
}
|
|
4832
|
+
function is_pull_request(rest) {
|
|
4833
|
+
return rest.pull_request !== void 0 && rest.pull_request !== null;
|
|
4834
|
+
}
|
|
4835
|
+
function parse_rest_issues(rest_json) {
|
|
4836
|
+
const parsed = parse_json_array_or_undefined(rest_json, rest_issue_schema);
|
|
4837
|
+
if (parsed === void 0) throw new Error(NOT_AN_ISSUE_LISTING_MESSAGE);
|
|
4838
|
+
return parsed;
|
|
4839
|
+
}
|
|
4840
|
+
function empty_blocked_by() {
|
|
4841
|
+
return { nodes: [], totalCount: 0 };
|
|
4842
|
+
}
|
|
4843
|
+
function repo_of_url(repository_url) {
|
|
4844
|
+
const match = REPOSITORY_URL.exec(repository_url ?? "");
|
|
4845
|
+
return match?.[REPOSITORY_URL_GROUP];
|
|
4846
|
+
}
|
|
4847
|
+
function to_blocked_by(blockers_json, exact_total) {
|
|
4848
|
+
const parsed = parse_json_array_or_undefined(blockers_json, blocking_issue_schema);
|
|
4849
|
+
if (parsed === void 0) throw new Error(NOT_A_BLOCKER_LISTING_MESSAGE);
|
|
4850
|
+
const nodes = parsed.map((blocker) => ({ ...blocker, state: to_gh_state(blocker.state) }));
|
|
4851
|
+
return { nodes, totalCount: exact_total ?? nodes.length };
|
|
4852
|
+
}
|
|
4853
|
+
function to_gh_issue(rest, fields, blocked_by) {
|
|
4854
|
+
return Object.fromEntries(
|
|
4855
|
+
fields.map((field) => [
|
|
4856
|
+
field,
|
|
4857
|
+
field === BLOCKED_BY_FIELD ? blocked_by : to_gh_field_value(field, rest)
|
|
4858
|
+
])
|
|
4859
|
+
);
|
|
4860
|
+
}
|
|
4861
|
+
function to_field_text(value) {
|
|
4862
|
+
if (typeof value === "string") return value;
|
|
4863
|
+
return value === void 0 || value === null ? EMPTY_BODY : JSON.stringify(value);
|
|
4864
|
+
}
|
|
4865
|
+
var CLOSED = "CLOSED";
|
|
4866
|
+
function reference_of_blocker(node, fallback_repo) {
|
|
4867
|
+
const repo = repo_of_url(node.repository_url) ?? fallback_repo;
|
|
4868
|
+
if (node.state === void 0) return { repo, number: node.number };
|
|
4869
|
+
return { repo, number: node.number, state: node.state.toUpperCase() === CLOSED ? CLOSED : "OPEN" };
|
|
4870
|
+
}
|
|
4871
|
+
var git_gh_issue_rest = {
|
|
4872
|
+
empty_blocked_by,
|
|
4873
|
+
repo_of_url,
|
|
4874
|
+
reference_of_blocker,
|
|
4875
|
+
split_fields,
|
|
4876
|
+
is_pull_request,
|
|
4877
|
+
parse_rest_issue,
|
|
4878
|
+
parse_rest_issues,
|
|
4879
|
+
total_blocked_by,
|
|
4880
|
+
to_blocked_by,
|
|
4881
|
+
to_gh_issue,
|
|
4882
|
+
to_field_text
|
|
4883
|
+
};
|
|
4884
|
+
|
|
4885
|
+
// scripts/gh/git-gh-reachability.ts
|
|
4886
|
+
var RATE_LIMITED_STATUS = 429;
|
|
4887
|
+
var SERVER_ERROR_FLOOR = 500;
|
|
4888
|
+
function is_retryable_status(status2) {
|
|
4889
|
+
if (status2 >= SERVER_ERROR_FLOOR) return true;
|
|
4890
|
+
return status2 === RATE_LIMITED_STATUS;
|
|
4891
|
+
}
|
|
4892
|
+
function classify_status(status2) {
|
|
4893
|
+
if (status2 === void 0) return "unreachable";
|
|
4894
|
+
return is_retryable_status(status2) ? "unreachable" : "reachable";
|
|
4895
|
+
}
|
|
4896
|
+
var gh_reachability = { classify_status };
|
|
4897
|
+
|
|
4898
|
+
// scripts/gh/git-gh-issue-read.ts
|
|
4899
|
+
async function read_blocked_by_listing(issue_number, repo, exact_total) {
|
|
4900
|
+
const json = await git_gh_exec.exec_gh_api({
|
|
4901
|
+
path: `${git_gh_api_path.blocked_by_api_path(issue_number, repo)}${git_gh_api_path.FULL_PAGE_QUERY}`
|
|
4902
|
+
});
|
|
4903
|
+
return git_gh_issue_rest.to_blocked_by(json, exact_total);
|
|
4904
|
+
}
|
|
4905
|
+
async function read_blocked_by(issue_number, rest, repo) {
|
|
4906
|
+
if (git_gh_issue_rest.is_pull_request(rest)) return git_gh_issue_rest.empty_blocked_by();
|
|
4907
|
+
const exact_total = git_gh_issue_rest.total_blocked_by(rest);
|
|
4908
|
+
if (exact_total === 0) return git_gh_issue_rest.empty_blocked_by();
|
|
4909
|
+
return await read_blocked_by_listing(issue_number, repo, exact_total);
|
|
4910
|
+
}
|
|
4911
|
+
function mark_followup(error) {
|
|
4912
|
+
if (!(error instanceof Error)) return error;
|
|
4913
|
+
const failure = gh_failure.failure_of(error);
|
|
4914
|
+
if (failure === void 0) return error;
|
|
4915
|
+
return gh_failure.attach(error, { ...failure, is_followup: true });
|
|
4916
|
+
}
|
|
4917
|
+
async function read_relations(issue_number, rest, repo) {
|
|
4918
|
+
try {
|
|
4919
|
+
return await read_blocked_by(issue_number, rest, repo);
|
|
4920
|
+
} catch (error) {
|
|
4921
|
+
throw mark_followup(error);
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
async function read_issue_fields(issue_number, fields, repo) {
|
|
4925
|
+
const rest = git_gh_issue_rest.parse_rest_issue(
|
|
4926
|
+
await git_gh_exec.exec_gh_api({ path: git_gh_api_path.issue_api_path(issue_number, repo) })
|
|
4927
|
+
);
|
|
4928
|
+
const requested = git_gh_issue_rest.split_fields(fields);
|
|
4929
|
+
const blocked_by = requested.includes(BLOCKED_BY_FIELD) ? await read_relations(issue_number, rest, repo) : void 0;
|
|
4930
|
+
return git_gh_issue_rest.to_gh_issue(rest, requested, blocked_by);
|
|
4931
|
+
}
|
|
4932
|
+
var NOT_FOUND_STATUS = 404;
|
|
4933
|
+
function is_number_unresolved(failure) {
|
|
4934
|
+
return failure.status === NOT_FOUND_STATUS && failure.is_followup !== true;
|
|
4935
|
+
}
|
|
4936
|
+
function to_unreadable_reason(status2) {
|
|
4937
|
+
return gh_reachability.classify_status(status2) === "unreachable" ? "unreachable" : "rejected";
|
|
4938
|
+
}
|
|
4939
|
+
function to_read_failure(error) {
|
|
4940
|
+
const failure = gh_failure.failure_of(error);
|
|
4941
|
+
if (failure === void 0) return { kind: "unreadable", reason: "malformed", status: void 0 };
|
|
4942
|
+
if (is_number_unresolved(failure)) return { kind: "missing" };
|
|
4943
|
+
return {
|
|
4944
|
+
kind: "unreadable",
|
|
4945
|
+
reason: to_unreadable_reason(failure.status),
|
|
4946
|
+
status: failure.status
|
|
4947
|
+
};
|
|
4948
|
+
}
|
|
4949
|
+
async function issue_view_json_classified(issue_number, fields, repo) {
|
|
4950
|
+
try {
|
|
4951
|
+
const issue = await read_issue_fields(issue_number, fields, repo);
|
|
4952
|
+
return { kind: "read", json: JSON.stringify(issue) };
|
|
4953
|
+
} catch (error) {
|
|
4954
|
+
return to_read_failure(error);
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
async function issue_view_field_classified(issue_number, field, repo) {
|
|
4958
|
+
try {
|
|
4959
|
+
const issue = await read_issue_fields(issue_number, field, repo);
|
|
4960
|
+
return { kind: "read", text: git_gh_issue_rest.to_field_text(issue[field]) };
|
|
4961
|
+
} catch (error) {
|
|
4962
|
+
return to_read_failure(error);
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
async function issue_view_field(issue_number, field, repo) {
|
|
4966
|
+
const read3 = await issue_view_field_classified(issue_number, field, repo);
|
|
4967
|
+
return read3.kind === "read" ? read3.text : void 0;
|
|
4968
|
+
}
|
|
4969
|
+
async function issue_get_title(issue_number, repo) {
|
|
4970
|
+
const result = await issue_view_field(issue_number, "title", repo);
|
|
4971
|
+
return result === void 0 ? void 0 : git_gh_helpers.parse_pr_state_string(result);
|
|
4972
|
+
}
|
|
4973
|
+
async function issue_get_body(issue_number, repo) {
|
|
4974
|
+
return await issue_view_field(issue_number, "body", repo);
|
|
4975
|
+
}
|
|
4976
|
+
async function issue_get_body_classified(issue_number, repo) {
|
|
4977
|
+
return await issue_view_field_classified(issue_number, "body", repo);
|
|
4978
|
+
}
|
|
4979
|
+
async function issue_view_json(issue_number, fields, repo) {
|
|
4980
|
+
const read3 = await issue_view_json_classified(issue_number, fields, repo);
|
|
4981
|
+
return read3.kind === "read" ? read3.json : void 0;
|
|
4982
|
+
}
|
|
4983
|
+
var STATE_AND_RELATIONS_FIELDS = "number,state,labels,blockedBy";
|
|
4984
|
+
async function issue_get_state_and_relations(issue_number, repo) {
|
|
4985
|
+
return await issue_view_json(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
|
|
4986
|
+
}
|
|
4987
|
+
async function issue_get_state_and_relations_classified(issue_number, repo) {
|
|
4988
|
+
return await issue_view_json_classified(issue_number, STATE_AND_RELATIONS_FIELDS, repo);
|
|
4989
|
+
}
|
|
4990
|
+
function is_unreachable_read(failure) {
|
|
4991
|
+
return failure.kind === "unreadable" && failure.reason === "unreachable";
|
|
4992
|
+
}
|
|
4993
|
+
var PLAN_FIELDS = "number,title,body,state,url,labels,blockedBy";
|
|
4994
|
+
async function issue_get_plan_fields(issue_number) {
|
|
4995
|
+
return await issue_view_json(issue_number, PLAN_FIELDS);
|
|
4996
|
+
}
|
|
4997
|
+
async function issue_get_plan_fields_classified(issue_number) {
|
|
4998
|
+
return await issue_view_json_classified(issue_number, PLAN_FIELDS);
|
|
4999
|
+
}
|
|
5000
|
+
async function issue_get_labels_and_body(issue_number) {
|
|
5001
|
+
return await issue_view_json(issue_number, "number,state,labels,body");
|
|
5002
|
+
}
|
|
5003
|
+
async function issue_list_comments(issue_number) {
|
|
5004
|
+
const path6 = `${git_gh_api_path.issue_comments_api_path(issue_number)}${git_gh_api_path.FULL_PAGE_QUERY}`;
|
|
5005
|
+
try {
|
|
5006
|
+
return await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true });
|
|
5007
|
+
} catch {
|
|
5008
|
+
return void 0;
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
async function has_no_relations_endpoint(issue_number, repo) {
|
|
5012
|
+
const status2 = await git_gh_exec.exec_gh_api_status(
|
|
5013
|
+
git_gh_api_path.blocked_by_api_path(issue_number, repo)
|
|
5014
|
+
);
|
|
5015
|
+
return status2 === NOT_FOUND_STATUS;
|
|
5016
|
+
}
|
|
5017
|
+
async function issue_blocked_by_references(issue_number, own_repo, repo) {
|
|
5018
|
+
try {
|
|
5019
|
+
const listing = await read_blocked_by_listing(issue_number, repo);
|
|
5020
|
+
return listing.nodes.map((blocker) => git_gh_issue_rest.reference_of_blocker(blocker, own_repo));
|
|
5021
|
+
} catch (error) {
|
|
5022
|
+
if (await has_no_relations_endpoint(issue_number, repo)) return [];
|
|
5023
|
+
throw error;
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
async function issue_blocked_by_numbers(issue_number, repo) {
|
|
5027
|
+
const references = await issue_blocked_by_references(issue_number, "", repo);
|
|
5028
|
+
return references.map((blocker) => blocker.number);
|
|
5029
|
+
}
|
|
5030
|
+
var git_gh_issue_read = {
|
|
5031
|
+
issue_blocked_by_numbers,
|
|
5032
|
+
issue_blocked_by_references,
|
|
5033
|
+
issue_get_title,
|
|
5034
|
+
issue_get_body,
|
|
5035
|
+
issue_get_body_classified,
|
|
5036
|
+
issue_view_json,
|
|
5037
|
+
issue_view_json_classified,
|
|
5038
|
+
issue_get_state_and_relations,
|
|
5039
|
+
issue_get_state_and_relations_classified,
|
|
5040
|
+
is_unreachable_read,
|
|
5041
|
+
issue_get_plan_fields,
|
|
5042
|
+
issue_get_plan_fields_classified,
|
|
5043
|
+
issue_get_labels_and_body,
|
|
5044
|
+
issue_list_comments
|
|
5045
|
+
};
|
|
5046
|
+
|
|
5047
|
+
// scripts/gh/git-gh-issue-list.ts
|
|
5048
|
+
var PER_PAGE = 100;
|
|
5049
|
+
var FIRST_PAGE = 1;
|
|
5050
|
+
var DEFAULT_STATE = "open";
|
|
5051
|
+
var DEFAULT_SORT = "created";
|
|
5052
|
+
var LISTING_DIRECTION = "direction=desc";
|
|
5053
|
+
function listing_query(request) {
|
|
5054
|
+
const state = request.state ?? DEFAULT_STATE;
|
|
5055
|
+
const sort = request.sort ?? DEFAULT_SORT;
|
|
5056
|
+
return `state=${state}&sort=${sort}&${LISTING_DIRECTION}`;
|
|
5057
|
+
}
|
|
5058
|
+
function listing_path(request, page) {
|
|
5059
|
+
const label2 = request.label === void 0 ? "" : `&labels=${encodeURIComponent(request.label)}`;
|
|
5060
|
+
const paging = `&per_page=${String(PER_PAGE)}&page=${String(page)}`;
|
|
5061
|
+
return `${git_gh_api_path.issues_api_path(request.repo)}?${listing_query(request)}${label2}${paging}`;
|
|
5062
|
+
}
|
|
5063
|
+
var DIGIT_PATTERN = /\d/u;
|
|
5064
|
+
function has_token_occurrence(body, term) {
|
|
5065
|
+
let index = body.indexOf(term);
|
|
5066
|
+
while (index !== -1) {
|
|
5067
|
+
if (!DIGIT_PATTERN.test(body.charAt(index + term.length))) return true;
|
|
5068
|
+
index = body.indexOf(term, index + 1);
|
|
5069
|
+
}
|
|
5070
|
+
return false;
|
|
5071
|
+
}
|
|
5072
|
+
function matches_body(rest, term) {
|
|
5073
|
+
if (term === void 0) return true;
|
|
5074
|
+
const { body } = rest;
|
|
5075
|
+
return typeof body === "string" && has_token_occurrence(body, term);
|
|
5076
|
+
}
|
|
5077
|
+
function is_selected(rest, request) {
|
|
5078
|
+
return !git_gh_issue_rest.is_pull_request(rest) && matches_body(rest, request.body_term);
|
|
5079
|
+
}
|
|
5080
|
+
async function fetch_page(request, page) {
|
|
5081
|
+
const path6 = listing_path(request, page);
|
|
5082
|
+
const rows = git_gh_issue_rest.parse_rest_issues(await git_gh_exec.exec_gh_api({ path: path6 }));
|
|
5083
|
+
return {
|
|
5084
|
+
selected: rows.filter((row) => is_selected(row, request)),
|
|
5085
|
+
is_full: rows.length >= PER_PAGE
|
|
5086
|
+
};
|
|
5087
|
+
}
|
|
5088
|
+
var MAX_PAGES = 5;
|
|
5089
|
+
var MAX_SCANNED = MAX_PAGES * PER_PAGE;
|
|
5090
|
+
function wants_more(has_more, selected_count, limit) {
|
|
5091
|
+
return has_more && selected_count < limit;
|
|
5092
|
+
}
|
|
5093
|
+
async function fetch_selected(request) {
|
|
5094
|
+
const selected = [];
|
|
5095
|
+
let page = FIRST_PAGE;
|
|
5096
|
+
let has_more = true;
|
|
5097
|
+
while (page <= MAX_PAGES && wants_more(has_more, selected.length, request.limit)) {
|
|
5098
|
+
const result = await fetch_page(request, page);
|
|
5099
|
+
selected.push(...result.selected);
|
|
5100
|
+
has_more = result.is_full;
|
|
5101
|
+
page += 1;
|
|
5102
|
+
}
|
|
5103
|
+
return {
|
|
5104
|
+
rows: selected.slice(0, request.limit),
|
|
5105
|
+
is_capped: wants_more(has_more, selected.length, request.limit)
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
var RELATION_CONCURRENCY = 8;
|
|
5109
|
+
async function read_relations2(rows, repo) {
|
|
5110
|
+
return await bounded_pool.bounded_map(
|
|
5111
|
+
rows,
|
|
5112
|
+
RELATION_CONCURRENCY,
|
|
5113
|
+
async (row) => await read_blocked_by(String(row.number), row, repo)
|
|
5114
|
+
);
|
|
5115
|
+
}
|
|
5116
|
+
async function issue_list(request) {
|
|
5117
|
+
try {
|
|
5118
|
+
const { rows, is_capped } = await fetch_selected(request);
|
|
5119
|
+
const fields = git_gh_issue_rest.split_fields(request.json_fields);
|
|
5120
|
+
const relations = fields.includes(BLOCKED_BY_FIELD) ? await read_relations2(rows, request.repo) : void 0;
|
|
5121
|
+
const mapped = rows.map(
|
|
5122
|
+
(row, index) => git_gh_issue_rest.to_gh_issue(row, fields, relations?.[index])
|
|
5123
|
+
);
|
|
5124
|
+
return { json: JSON.stringify(mapped), is_capped };
|
|
5125
|
+
} catch {
|
|
5126
|
+
return { json: void 0, is_capped: false };
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
var git_gh_issue_list = {
|
|
5130
|
+
issue_list
|
|
5131
|
+
};
|
|
5132
|
+
|
|
5133
|
+
// scripts/gh/git-gh-issue-write.ts
|
|
5134
|
+
var PATCH_METHOD = "PATCH";
|
|
5135
|
+
var DELETE_METHOD = "DELETE";
|
|
5136
|
+
var HTML_URL_FILTER = ".html_url";
|
|
5137
|
+
var ID_FILTER = ".id";
|
|
5138
|
+
var LABELS_SEGMENT = "/labels";
|
|
5139
|
+
var COMMENTS_SEGMENT = "/comments";
|
|
5140
|
+
var CLOSED_STATE = "closed";
|
|
5141
|
+
var COLOR_HASH = "#";
|
|
5142
|
+
async function did_write_succeed(write) {
|
|
5143
|
+
try {
|
|
5144
|
+
await write();
|
|
5145
|
+
return true;
|
|
5146
|
+
} catch (error) {
|
|
5147
|
+
error_text.trace_swallowed("git_gh_issue_write.did_write_succeed", error);
|
|
5148
|
+
return false;
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
async function issue_edit_body(issue_number, body) {
|
|
5152
|
+
return await git_gh_exec.exec_gh_api({
|
|
5153
|
+
path: git_gh_api_path.issue_api_path(issue_number),
|
|
5154
|
+
method: PATCH_METHOD,
|
|
5155
|
+
body: JSON.stringify({ body }),
|
|
5156
|
+
jq_filter: HTML_URL_FILTER
|
|
5157
|
+
});
|
|
5158
|
+
}
|
|
5159
|
+
async function issue_comment(issue_number, body) {
|
|
5160
|
+
return await git_gh_exec.exec_gh_api({
|
|
5161
|
+
path: `${git_gh_api_path.issue_api_path(issue_number)}${COMMENTS_SEGMENT}`,
|
|
5162
|
+
body: JSON.stringify({ body }),
|
|
5163
|
+
jq_filter: HTML_URL_FILTER
|
|
5164
|
+
});
|
|
5165
|
+
}
|
|
5166
|
+
async function issue_try_comment(issue_number, body) {
|
|
5167
|
+
return await did_write_succeed(async () => await issue_comment(issue_number, body));
|
|
5168
|
+
}
|
|
5169
|
+
async function issue_close(issue_number, comment) {
|
|
5170
|
+
return await did_write_succeed(async () => {
|
|
5171
|
+
if (comment !== void 0) await issue_comment(issue_number, comment);
|
|
5172
|
+
await git_gh_exec.exec_gh_api({
|
|
5173
|
+
path: git_gh_api_path.issue_api_path(issue_number),
|
|
5174
|
+
method: PATCH_METHOD,
|
|
5175
|
+
body: JSON.stringify({ state: CLOSED_STATE })
|
|
5176
|
+
});
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
function to_label_color(color) {
|
|
5180
|
+
return color.startsWith(COLOR_HASH) ? color.slice(COLOR_HASH.length) : color;
|
|
5181
|
+
}
|
|
5182
|
+
async function label_ensure(input) {
|
|
5183
|
+
try {
|
|
5184
|
+
await git_gh_exec.exec_gh_api({
|
|
5185
|
+
path: `${git_gh_api_path.repo_api_path()}${LABELS_SEGMENT}`,
|
|
5186
|
+
body: JSON.stringify({
|
|
5187
|
+
name: input.name,
|
|
5188
|
+
color: to_label_color(input.color),
|
|
5189
|
+
description: input.description
|
|
5190
|
+
})
|
|
5191
|
+
});
|
|
5192
|
+
} catch {
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
function issue_create_request(input) {
|
|
5196
|
+
const { labels } = input;
|
|
5197
|
+
return {
|
|
5198
|
+
path: git_gh_api_path.issues_api_path(input.repo),
|
|
5199
|
+
body: JSON.stringify({
|
|
5200
|
+
title: input.title,
|
|
5201
|
+
...labels !== void 0 && { labels },
|
|
5202
|
+
body: input.body
|
|
5203
|
+
}),
|
|
5204
|
+
jq_filter: HTML_URL_FILTER
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
5207
|
+
async function issue_create_with_label(input) {
|
|
5208
|
+
return await git_gh_exec.exec_gh_api(
|
|
5209
|
+
issue_create_request({ title: input.title, body: input.body, labels: [input.label] })
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5212
|
+
async function issue_add_label(issue_number, label2) {
|
|
5213
|
+
return await did_write_succeed(
|
|
5214
|
+
async () => await git_gh_exec.exec_gh_api({
|
|
5215
|
+
path: `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`,
|
|
5216
|
+
body: JSON.stringify({ labels: [label2] })
|
|
5217
|
+
})
|
|
5218
|
+
);
|
|
5219
|
+
}
|
|
5220
|
+
async function issue_remove_label(issue_number, label2) {
|
|
5221
|
+
const labels_path = `${git_gh_api_path.issue_api_path(issue_number)}${LABELS_SEGMENT}`;
|
|
5222
|
+
await git_gh_exec.exec_gh_api({
|
|
5223
|
+
path: `${labels_path}/${encodeURIComponent(label2)}`,
|
|
5224
|
+
method: DELETE_METHOD
|
|
5225
|
+
});
|
|
5226
|
+
}
|
|
5227
|
+
async function read_issue_id(issue_number) {
|
|
5228
|
+
const raw = await git_gh_exec.exec_gh_api({
|
|
5229
|
+
path: git_gh_api_path.issue_api_path(issue_number),
|
|
5230
|
+
jq_filter: ID_FILTER
|
|
5231
|
+
});
|
|
5232
|
+
const issue_id = Number(raw.trim());
|
|
5233
|
+
if (!Number.isSafeInteger(issue_id) || issue_id <= 0) {
|
|
5234
|
+
throw new Error(`gh api answered no database id for issue #${issue_number}`);
|
|
5235
|
+
}
|
|
5236
|
+
return issue_id;
|
|
5237
|
+
}
|
|
5238
|
+
async function issue_add_blocked_by(issue_number, blocker) {
|
|
5239
|
+
return await did_write_succeed(async () => {
|
|
5240
|
+
const issue_id = await read_issue_id(blocker);
|
|
5241
|
+
await git_gh_exec.exec_gh_api({
|
|
5242
|
+
path: git_gh_api_path.blocked_by_api_path(issue_number),
|
|
5243
|
+
body: JSON.stringify({ issue_id })
|
|
5244
|
+
});
|
|
5245
|
+
});
|
|
5246
|
+
}
|
|
5247
|
+
async function issue_remove_blocked_by(issue_number, blocker) {
|
|
5248
|
+
return await did_write_succeed(async () => {
|
|
5249
|
+
const issue_id = await read_issue_id(blocker);
|
|
5250
|
+
const relations_path = git_gh_api_path.blocked_by_api_path(issue_number);
|
|
5251
|
+
await git_gh_exec.exec_gh_api({
|
|
5252
|
+
path: `${relations_path}/${String(issue_id)}`,
|
|
5253
|
+
method: DELETE_METHOD
|
|
5254
|
+
});
|
|
5255
|
+
});
|
|
5256
|
+
}
|
|
5257
|
+
var git_gh_issue_write = {
|
|
5258
|
+
issue_edit_body,
|
|
5259
|
+
issue_comment,
|
|
5260
|
+
issue_try_comment,
|
|
5261
|
+
issue_close,
|
|
5262
|
+
label_ensure,
|
|
5263
|
+
issue_create_request,
|
|
5264
|
+
issue_create_with_label,
|
|
5265
|
+
issue_add_label,
|
|
5266
|
+
issue_remove_label,
|
|
5267
|
+
issue_add_blocked_by,
|
|
5268
|
+
issue_remove_blocked_by
|
|
5269
|
+
};
|
|
5270
|
+
|
|
5271
|
+
// scripts/gh/git-gh-issue.ts
|
|
5272
|
+
var NUMBER_AND_BODY_FIELDS = "number,body";
|
|
5273
|
+
var BACKLOG_SCAN_FIELDS = `number,title,body,${BLOCKED_BY_COUNT_FIELD}`;
|
|
5274
|
+
var NUMBER_TITLE_AND_LABELS_FIELDS = "number,title,labels";
|
|
5275
|
+
var SUMMARY_FIELDS = "number,title,labels,createdAt";
|
|
5276
|
+
var PICKUP_FIELDS = `${SUMMARY_FIELDS},blockedBy`;
|
|
5277
|
+
var { issue_list: issue_list2 } = git_gh_issue_list;
|
|
5278
|
+
async function issue_list_recent(limit) {
|
|
5279
|
+
return await issue_list2({ json_fields: SUMMARY_FIELDS, limit });
|
|
5280
|
+
}
|
|
5281
|
+
async function issue_list_open_bodies(limit) {
|
|
5282
|
+
return await issue_list2({ json_fields: BACKLOG_SCAN_FIELDS, limit });
|
|
5283
|
+
}
|
|
5284
|
+
async function issue_list_recently_closed(limit) {
|
|
5285
|
+
return await issue_list2({
|
|
5286
|
+
json_fields: NUMBER_TITLE_AND_LABELS_FIELDS,
|
|
5287
|
+
limit,
|
|
5288
|
+
state: "closed",
|
|
5289
|
+
sort: "updated"
|
|
5290
|
+
});
|
|
5291
|
+
}
|
|
5292
|
+
async function issue_search_body(term, limit) {
|
|
5293
|
+
return await issue_list2({
|
|
5294
|
+
json_fields: NUMBER_AND_BODY_FIELDS,
|
|
5295
|
+
limit,
|
|
5296
|
+
body_term: term
|
|
5297
|
+
});
|
|
5298
|
+
}
|
|
5299
|
+
async function issue_list_by_label(label2, limit) {
|
|
5300
|
+
return await issue_list2({ json_fields: NUMBER_AND_BODY_FIELDS, limit, label: label2 });
|
|
5301
|
+
}
|
|
5302
|
+
async function issue_list_by_label_summary(label2, limit, options) {
|
|
5303
|
+
return await issue_list2({
|
|
5304
|
+
json_fields: options?.json_fields ?? PICKUP_FIELDS,
|
|
5305
|
+
limit,
|
|
5306
|
+
label: label2
|
|
5307
|
+
});
|
|
5308
|
+
}
|
|
5309
|
+
async function issue_list_by_label_in_repo(label2, limit, repo) {
|
|
5310
|
+
return await issue_list2({ json_fields: SUMMARY_FIELDS, limit, label: label2, repo });
|
|
5311
|
+
}
|
|
5312
|
+
var git_gh_issue = {
|
|
5313
|
+
...git_gh_issue_read,
|
|
5314
|
+
...git_gh_issue_write,
|
|
5315
|
+
issue_list_recent,
|
|
5316
|
+
issue_list_by_label,
|
|
5317
|
+
issue_list_by_label_in_repo,
|
|
5318
|
+
issue_list_by_label_summary,
|
|
5319
|
+
issue_search_body,
|
|
5320
|
+
issue_list_open_bodies,
|
|
5321
|
+
issue_list_recently_closed
|
|
5322
|
+
};
|
|
5323
|
+
|
|
5324
|
+
// scripts/ci/pr-classification.ts
|
|
5325
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
5326
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
5327
|
+
import { z as z13 } from "zod";
|
|
5328
|
+
var CLASSIFICATION_SET = new Set(RELEASE_CLASSIFICATION_NAMES);
|
|
5329
|
+
var ISSUE_BUG_LABEL = "bug";
|
|
5330
|
+
var DECLARATION_PREFIX = "- \u30EA\u30EA\u30FC\u30B9\u5206\u985E: ";
|
|
5331
|
+
var DECLARATION = new RegExp(`^${DECLARATION_PREFIX}(.+)$`, "gmu");
|
|
5332
|
+
var LEGACY_LABELS = /* @__PURE__ */ new Set(["semver-major", "semver-minor"]);
|
|
5333
|
+
var LABEL = z13.object({ name: z13.string() });
|
|
5334
|
+
var USER = z13.object({ login: z13.string() });
|
|
5335
|
+
var PULL_REQUEST = z13.object({ number: z13.number(), user: USER });
|
|
5336
|
+
var EVENT = z13.object({
|
|
5337
|
+
pull_request: PULL_REQUEST,
|
|
5338
|
+
repository: z13.object({ full_name: z13.string() })
|
|
5339
|
+
});
|
|
5340
|
+
function classification_error(labels, author) {
|
|
5341
|
+
const legacy = labels.filter((label2) => LEGACY_LABELS.has(label2.toLowerCase()));
|
|
5342
|
+
if (legacy.length > 0) return `Remove legacy release labels: ${legacy.join(", ")}`;
|
|
5343
|
+
const selected = labels.filter((label2) => CLASSIFICATION_SET.has(label2.toLowerCase()));
|
|
5344
|
+
if (selected.length > 1) {
|
|
5345
|
+
return `Choose exactly one release classification; found: ${selected.join(", ")}`;
|
|
5346
|
+
}
|
|
5347
|
+
if (selected.length === 1 || author.endsWith("[bot]")) return void 0;
|
|
5348
|
+
return `Choose one release classification: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}`;
|
|
5349
|
+
}
|
|
5350
|
+
function declaration(label2) {
|
|
5351
|
+
return `${DECLARATION_PREFIX}${label2}`;
|
|
5352
|
+
}
|
|
5353
|
+
function is_classification(value) {
|
|
5354
|
+
return CLASSIFICATION_SET.has(value);
|
|
5355
|
+
}
|
|
5356
|
+
function release_candidates(candidates) {
|
|
5357
|
+
if (!candidates.includes(BREAKING_CHANGE_LABEL)) return candidates;
|
|
5358
|
+
return candidates.filter((candidate) => candidate !== ENHANCEMENT_LABEL);
|
|
5359
|
+
}
|
|
5360
|
+
function issue_candidates(issue_json) {
|
|
5361
|
+
const issue = z13.object({ labels: z13.array(LABEL), body: z13.string().nullable() }).parse(JSON.parse(issue_json));
|
|
5362
|
+
const names = issue.labels.map((label2) => label2.name.toLowerCase());
|
|
5363
|
+
const declared = [...(issue.body ?? "").matchAll(DECLARATION)].map((match) => match[1] ?? "");
|
|
5364
|
+
const candidates = [...release_candidates([...names, ...declared].filter(is_classification))];
|
|
5365
|
+
const unknown = declared.filter((value) => !is_classification(value));
|
|
5366
|
+
if (unknown.length > 0) throw new Error(`Unknown release classification: ${unknown.join(", ")}`);
|
|
5367
|
+
if (names.includes(ISSUE_BUG_LABEL) && !candidates.includes(BREAKING_CHANGE_LABEL)) {
|
|
5368
|
+
candidates.push(BUGFIX_LABEL);
|
|
5369
|
+
}
|
|
5370
|
+
return candidates;
|
|
5371
|
+
}
|
|
5372
|
+
function select_issue_classification(issue_json) {
|
|
5373
|
+
const candidates = issue_candidates(issue_json);
|
|
5374
|
+
const selected = [...new Set(candidates)];
|
|
5375
|
+
const [label2] = selected;
|
|
5376
|
+
if (label2 === void 0 || selected.length > 1) {
|
|
5377
|
+
throw new Error(
|
|
5378
|
+
`Choose one release classification before opening the PR: ${RELEASE_CLASSIFICATION_NAMES.join(", ")}; found: ${selected.join(", ") || "none"}. Add "${DECLARATION_PREFIX}<label>" to the Issue body.`
|
|
5379
|
+
);
|
|
5380
|
+
}
|
|
5381
|
+
return label2;
|
|
5382
|
+
}
|
|
5383
|
+
function fetch_current_labels(repository2, pull_number) {
|
|
5384
|
+
const path6 = `repos/${repository2}/issues/${String(pull_number)}/labels`;
|
|
5385
|
+
try {
|
|
5386
|
+
const names = git_gh_exec.exec_gh_api_sync({ path: path6, jq_filter: ".[].name" });
|
|
5387
|
+
return names.split("\n").filter((name) => name !== "");
|
|
5388
|
+
} catch (error) {
|
|
5389
|
+
const detail = error_text.message_of(error);
|
|
5390
|
+
throw new Error(`Could not read the pull request labels: ${detail}`, { cause: error });
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
function check_event(event_path, read_labels = fetch_current_labels) {
|
|
5394
|
+
const event = EVENT.parse(JSON.parse(readFileSync4(event_path, "utf8")));
|
|
5395
|
+
const labels = read_labels(event.repository.full_name, event.pull_request.number);
|
|
5396
|
+
return classification_error(labels, event.pull_request.user.login);
|
|
5397
|
+
}
|
|
5398
|
+
if (process.argv[1] === fileURLToPath2(import.meta.url)) {
|
|
5399
|
+
const event_path = process.env["GITHUB_EVENT_PATH"];
|
|
5400
|
+
if (!event_path) throw new Error("GITHUB_EVENT_PATH is required");
|
|
5401
|
+
const error = check_event(event_path);
|
|
5402
|
+
if (error) {
|
|
5403
|
+
console.error(`::error::${error}`);
|
|
5404
|
+
process.exitCode = 1;
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
var pr_classification = {
|
|
5408
|
+
classification_error,
|
|
5409
|
+
check_event,
|
|
5410
|
+
fetch_current_labels,
|
|
5411
|
+
declaration,
|
|
5412
|
+
is_classification,
|
|
5413
|
+
select_issue_classification
|
|
5414
|
+
};
|
|
5415
|
+
|
|
5416
|
+
// scripts/lib/poll.ts
|
|
5417
|
+
async function sleep3(duration_ms) {
|
|
5418
|
+
await new Promise((resolve3) => {
|
|
5419
|
+
setTimeout(resolve3, duration_ms);
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
async function wait_between(wait, interval_ms, attempt, attempts) {
|
|
5423
|
+
if (attempt < attempts - 1) await wait(interval_ms);
|
|
5424
|
+
}
|
|
5425
|
+
async function poll_until(is_done2, options) {
|
|
5426
|
+
const wait = options.sleeper ?? sleep3;
|
|
5427
|
+
for (let attempt = 0; attempt < options.attempts; attempt += 1) {
|
|
5428
|
+
if (await is_done2()) return true;
|
|
5429
|
+
await wait_between(wait, options.interval_ms, attempt, options.attempts);
|
|
5430
|
+
}
|
|
5431
|
+
return false;
|
|
5432
|
+
}
|
|
5433
|
+
var poll = { poll_until, sleep: sleep3 };
|
|
5434
|
+
|
|
5435
|
+
// scripts/gh/git-gh-pr.ts
|
|
5436
|
+
import { z as z17 } from "zod";
|
|
5437
|
+
|
|
5438
|
+
// scripts/gh/git-gh-pr-rest.ts
|
|
5439
|
+
import { z as z14 } from "zod";
|
|
5440
|
+
var rest_repo_schema = z14.looseObject({ full_name: z14.string().optional() }).nullish();
|
|
5441
|
+
var rest_pull_side_schema = z14.looseObject({ repo: rest_repo_schema });
|
|
5442
|
+
var rest_pull_schema = z14.looseObject({
|
|
5443
|
+
number: z14.number(),
|
|
5444
|
+
html_url: z14.string().optional(),
|
|
5445
|
+
body: z14.string().nullish(),
|
|
5446
|
+
state: z14.string().optional(),
|
|
5447
|
+
merged: z14.boolean().nullish(),
|
|
5448
|
+
merged_at: z14.string().nullish(),
|
|
5449
|
+
mergeable: z14.boolean().nullish(),
|
|
5450
|
+
mergeable_state: z14.string().optional(),
|
|
5451
|
+
// `sha` is the head commit, which is what both halves of the merge-gate rollup are keyed by —
|
|
5452
|
+
// REST hangs the check runs and the status contexts off the commit rather than off the pull
|
|
5453
|
+
// request (joshuafolkken/kit#1028).
|
|
5454
|
+
head: z14.looseObject({ ref: z14.string().optional(), sha: z14.string().optional(), repo: rest_repo_schema }).nullish(),
|
|
5455
|
+
base: rest_pull_side_schema.nullish(),
|
|
5456
|
+
// `node_id` is what the GraphQL auto-merge mutation addresses a pull request by, and `auto_merge`
|
|
5457
|
+
// is `null` until auto-merge is enabled — both read by `git-gh-pr-auto-merge.ts`
|
|
5458
|
+
// (joshuafolkken/kit#2497).
|
|
5459
|
+
node_id: z14.string().optional(),
|
|
5460
|
+
auto_merge: z14.looseObject({}).nullish()
|
|
5461
|
+
});
|
|
5462
|
+
var NOT_A_PULL_MESSAGE = "gh api answered something other than a pull request object";
|
|
5463
|
+
var NOT_A_PULL_LISTING_MESSAGE = "gh api answered something other than a pull request listing";
|
|
5464
|
+
var NOT_A_COMMENT_LISTING_MESSAGE = "gh api answered something other than a comment listing";
|
|
5465
|
+
var OPEN_STATE = "open";
|
|
5466
|
+
var EMPTY_BODY2 = "";
|
|
5467
|
+
function parse_rest_pull(rest_json) {
|
|
5468
|
+
const parsed = parse_json_object_safe(rest_json, rest_pull_schema);
|
|
5469
|
+
if (parsed === void 0) throw new Error(NOT_A_PULL_MESSAGE);
|
|
5470
|
+
return parsed;
|
|
5471
|
+
}
|
|
5472
|
+
function parse_rest_pulls(rest_json) {
|
|
5473
|
+
const parsed = parse_json_array_or_undefined(rest_json, rest_pull_schema);
|
|
5474
|
+
if (parsed === void 0) throw new Error(NOT_A_PULL_LISTING_MESSAGE);
|
|
5475
|
+
return parsed;
|
|
5476
|
+
}
|
|
5477
|
+
function select_pull(pulls) {
|
|
5478
|
+
return pulls.find((pull) => pull.state === OPEN_STATE) ?? pulls[0];
|
|
5479
|
+
}
|
|
5480
|
+
function is_merged(pull) {
|
|
5481
|
+
return pull.merged === true || typeof pull.merged_at === "string";
|
|
5482
|
+
}
|
|
5483
|
+
function to_pr_state(pull) {
|
|
5484
|
+
return is_merged(pull) ? MERGED_STATE : to_gh_state(pull.state);
|
|
5485
|
+
}
|
|
5486
|
+
function to_repo_name(side) {
|
|
5487
|
+
return side?.repo?.full_name;
|
|
5488
|
+
}
|
|
5489
|
+
function is_same_repository_head(pull) {
|
|
5490
|
+
const head_repo = to_repo_name(pull.head);
|
|
5491
|
+
return head_repo !== void 0 && head_repo === to_repo_name(pull.base);
|
|
5492
|
+
}
|
|
5493
|
+
function to_pr_info(pull) {
|
|
5494
|
+
return {
|
|
5495
|
+
mergeable: pull.mergeable,
|
|
5496
|
+
mergeStateStatus: to_gh_state(pull.mergeable_state),
|
|
5497
|
+
state: to_pr_state(pull)
|
|
5498
|
+
};
|
|
5499
|
+
}
|
|
5500
|
+
function to_pr_comments(rest_json) {
|
|
5501
|
+
const parsed = parse_json_array_or_undefined(rest_json, rest_comment_schema);
|
|
5502
|
+
if (parsed === void 0) throw new Error(NOT_A_COMMENT_LISTING_MESSAGE);
|
|
5503
|
+
return parsed.map((comment) => ({
|
|
5504
|
+
body: comment.body ?? EMPTY_BODY2,
|
|
5505
|
+
url: comment.html_url,
|
|
5506
|
+
author: { login: comment.user?.login }
|
|
5507
|
+
}));
|
|
5508
|
+
}
|
|
5509
|
+
var git_gh_pr_rest = {
|
|
5510
|
+
parse_rest_pull,
|
|
5511
|
+
parse_rest_pulls,
|
|
5512
|
+
select_pull,
|
|
5513
|
+
is_same_repository_head,
|
|
5514
|
+
is_merged,
|
|
5515
|
+
to_pr_state,
|
|
5516
|
+
to_pr_info,
|
|
5517
|
+
to_pr_comments
|
|
5518
|
+
};
|
|
5519
|
+
|
|
5520
|
+
// scripts/gh/git-gh-pr-read.ts
|
|
5521
|
+
var pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
5522
|
+
var LOOKUP_QUERY = "state=all&sort=created&direction=desc&per_page=100";
|
|
5523
|
+
var COMMENTS_QUERY = FULL_PAGE_QUERY;
|
|
5524
|
+
var OWNER_PLACEHOLDER = "{owner}";
|
|
5525
|
+
var NO_PULL_REQUEST_MESSAGE = "gh api found no pull request for branch";
|
|
5526
|
+
var UNREADABLE_PULL_REQUEST_MESSAGE = "gh api could not read the pull requests for branch";
|
|
5527
|
+
function lookup_path(branch_name) {
|
|
5528
|
+
const head = `${OWNER_PLACEHOLDER}:${encodeURIComponent(branch_name)}`;
|
|
5529
|
+
return `${git_gh_api_path.pulls_api_path()}?head=${head}&${LOOKUP_QUERY}`;
|
|
5530
|
+
}
|
|
5531
|
+
async function fetch_pr_number(branch_name) {
|
|
5532
|
+
const json = await git_gh_exec.exec_gh_api({ path: lookup_path(branch_name) });
|
|
5533
|
+
return git_gh_pr_rest.select_pull(git_gh_pr_rest.parse_rest_pulls(json))?.number;
|
|
5534
|
+
}
|
|
5535
|
+
var pending_pr_number_by_branch = /* @__PURE__ */ new Map();
|
|
5536
|
+
async function fetch_pr_number_read(branch_name) {
|
|
5537
|
+
try {
|
|
5538
|
+
const pr_number = await fetch_pr_number(branch_name);
|
|
5539
|
+
if (pr_number === void 0) return { kind: "missing" };
|
|
5540
|
+
pr_number_by_branch.set(branch_name, pr_number);
|
|
5541
|
+
return { kind: "read", pr_number };
|
|
5542
|
+
} catch (error) {
|
|
5543
|
+
return { kind: "unreadable", cause: error };
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
async function start_pr_number_read(branch_name) {
|
|
5547
|
+
const pending = pending_pr_number_by_branch.get(branch_name);
|
|
5548
|
+
if (pending !== void 0) return await pending;
|
|
5549
|
+
const lookup = fetch_pr_number_read(branch_name);
|
|
5550
|
+
pending_pr_number_by_branch.set(branch_name, lookup);
|
|
5551
|
+
try {
|
|
5552
|
+
return await lookup;
|
|
5553
|
+
} finally {
|
|
5554
|
+
pending_pr_number_by_branch.delete(branch_name);
|
|
5555
|
+
}
|
|
5556
|
+
}
|
|
5557
|
+
function forget_pr_numbers() {
|
|
5558
|
+
pr_number_by_branch.clear();
|
|
5559
|
+
pending_pr_number_by_branch.clear();
|
|
5560
|
+
}
|
|
5561
|
+
async function read_pr_number(branch_name) {
|
|
5562
|
+
const cached = pr_number_by_branch.get(branch_name);
|
|
5563
|
+
if (cached !== void 0) return { kind: "read", pr_number: cached };
|
|
5564
|
+
return await start_pr_number_read(branch_name);
|
|
5565
|
+
}
|
|
5566
|
+
async function resolve_pr_number(branch_name) {
|
|
5567
|
+
const read3 = await read_pr_number(branch_name);
|
|
5568
|
+
return read3.kind === "read" ? read3.pr_number : void 0;
|
|
5569
|
+
}
|
|
5570
|
+
function to_unreadable_error(branch_name, cause) {
|
|
5571
|
+
return new Error(`${UNREADABLE_PULL_REQUEST_MESSAGE}: ${branch_name}`, { cause });
|
|
5572
|
+
}
|
|
5573
|
+
async function require_pr_number(branch_name) {
|
|
5574
|
+
const read3 = await read_pr_number(branch_name);
|
|
5575
|
+
if (read3.kind === "read") return read3.pr_number;
|
|
5576
|
+
if (read3.kind === "missing") throw new Error(`${NO_PULL_REQUEST_MESSAGE}: ${branch_name}`);
|
|
5577
|
+
throw to_unreadable_error(branch_name, read3.cause);
|
|
5578
|
+
}
|
|
5579
|
+
async function read_pull(pr_number) {
|
|
5580
|
+
const json = await git_gh_exec.exec_gh_api({
|
|
5581
|
+
path: git_gh_api_path.pull_api_path(String(pr_number))
|
|
5582
|
+
});
|
|
5583
|
+
return git_gh_pr_rest.parse_rest_pull(json);
|
|
5584
|
+
}
|
|
5585
|
+
async function read_pull_of_branch(branch_name) {
|
|
5586
|
+
const pr_number = await resolve_pr_number(branch_name);
|
|
5587
|
+
if (pr_number === void 0) return void 0;
|
|
5588
|
+
try {
|
|
5589
|
+
return await read_pull(pr_number);
|
|
5590
|
+
} catch {
|
|
5591
|
+
return void 0;
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
async function pr_exists(branch_name) {
|
|
5595
|
+
const read3 = await read_pr_number(branch_name);
|
|
5596
|
+
if (read3.kind === "unreadable") throw to_unreadable_error(branch_name, read3.cause);
|
|
5597
|
+
return read3.kind === "read";
|
|
5598
|
+
}
|
|
5599
|
+
async function pr_get_number(branch_name) {
|
|
5600
|
+
return await resolve_pr_number(branch_name);
|
|
5601
|
+
}
|
|
5602
|
+
async function pr_get_url(branch_name) {
|
|
5603
|
+
const pull = await read_pull_of_branch(branch_name);
|
|
5604
|
+
if (pull?.html_url === void 0) return void 0;
|
|
5605
|
+
return git_gh_helpers.parse_pr_state_string(pull.html_url);
|
|
5606
|
+
}
|
|
5607
|
+
async function pr_get_merge_state(branch_name) {
|
|
5608
|
+
const pull = await read_pull_of_branch(branch_name);
|
|
5609
|
+
if (pull === void 0) return void 0;
|
|
5610
|
+
return {
|
|
5611
|
+
is_merged: git_gh_pr_rest.is_merged(pull),
|
|
5612
|
+
merged_at: pull.merged_at ?? void 0,
|
|
5613
|
+
head_sha: pull.head?.sha
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
async function pr_get_body(branch_name) {
|
|
5617
|
+
const pull = await read_pull_of_branch(branch_name);
|
|
5618
|
+
const body = pull?.body;
|
|
5619
|
+
return typeof body === "string" && body.length > 0 ? body : void 0;
|
|
5620
|
+
}
|
|
5621
|
+
async function pr_view(branch_name) {
|
|
5622
|
+
const pull = await read_pull_of_branch(branch_name);
|
|
5623
|
+
if (pull === void 0) return "";
|
|
5624
|
+
return JSON.stringify(git_gh_pr_rest.to_pr_info(pull));
|
|
5625
|
+
}
|
|
5626
|
+
async function read_comments(branch_name, to_path, to_json) {
|
|
5627
|
+
const pr_number = await resolve_pr_number(branch_name);
|
|
5628
|
+
if (pr_number === void 0) return void 0;
|
|
5629
|
+
const path6 = `${to_path(String(pr_number))}${COMMENTS_QUERY}`;
|
|
5630
|
+
try {
|
|
5631
|
+
return to_json(await git_gh_exec.exec_gh_api({ path: path6, should_paginate: true }));
|
|
5632
|
+
} catch {
|
|
5633
|
+
return void 0;
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
function to_comments_json(raw) {
|
|
5637
|
+
return JSON.stringify(git_gh_pr_rest.to_pr_comments(raw));
|
|
5638
|
+
}
|
|
5639
|
+
function as_served(raw) {
|
|
5640
|
+
return raw;
|
|
5641
|
+
}
|
|
5642
|
+
async function pr_get_comments(branch_name) {
|
|
5643
|
+
return await read_comments(branch_name, git_gh_api_path.issue_comments_api_path, to_comments_json);
|
|
5644
|
+
}
|
|
5645
|
+
async function pr_get_review_comments(branch_name) {
|
|
5646
|
+
return await read_comments(branch_name, git_gh_api_path.pull_comments_api_path, as_served);
|
|
5647
|
+
}
|
|
5648
|
+
var git_gh_pr_read = {
|
|
5649
|
+
pr_exists,
|
|
5650
|
+
pr_get_number,
|
|
5651
|
+
pr_get_url,
|
|
5652
|
+
pr_get_body,
|
|
5653
|
+
pr_get_merge_state,
|
|
5654
|
+
pr_view,
|
|
5655
|
+
pr_get_comments,
|
|
5656
|
+
pr_get_review_comments
|
|
5657
|
+
};
|
|
5658
|
+
|
|
5659
|
+
// scripts/gh/git-gh-pr-review.ts
|
|
5660
|
+
import { z as z15 } from "zod";
|
|
5661
|
+
var REVIEW_APPROVED = "APPROVED";
|
|
5662
|
+
var REVIEW_CHANGES_REQUESTED = "CHANGES_REQUESTED";
|
|
5663
|
+
var REVIEW_NONE = "";
|
|
5664
|
+
var STATE_CHANGING_REVIEWS = /* @__PURE__ */ new Set([REVIEW_APPROVED, REVIEW_CHANGES_REQUESTED]);
|
|
5665
|
+
var rest_review_schema = z15.looseObject({
|
|
5666
|
+
state: z15.string().optional(),
|
|
5667
|
+
user: z15.looseObject({ login: z15.string().optional() }).nullish()
|
|
5668
|
+
});
|
|
5669
|
+
var NOT_A_REVIEW_LISTING = "gh api answered something other than a review listing";
|
|
5670
|
+
function parse_rest_reviews(reviews_json) {
|
|
5671
|
+
const parsed = parse_json_array_or_undefined(reviews_json, rest_review_schema);
|
|
5672
|
+
if (parsed === void 0) throw new Error(NOT_A_REVIEW_LISTING);
|
|
5673
|
+
return parsed;
|
|
5674
|
+
}
|
|
5675
|
+
var UNATTRIBUTED_REVIEWER = "(no author)";
|
|
5676
|
+
function read_reviewer(review) {
|
|
5677
|
+
const login = review.user?.login;
|
|
5678
|
+
return typeof login === "string" && login.length > 0 ? login : UNATTRIBUTED_REVIEWER;
|
|
5679
|
+
}
|
|
5680
|
+
function read_state_changing(review) {
|
|
5681
|
+
const state = review.state?.toUpperCase();
|
|
5682
|
+
return state !== void 0 && STATE_CHANGING_REVIEWS.has(state) ? state : void 0;
|
|
5683
|
+
}
|
|
5684
|
+
function fold_latest_states(reviews) {
|
|
5685
|
+
const latest = /* @__PURE__ */ new Map();
|
|
5686
|
+
for (const review of reviews) {
|
|
5687
|
+
const state = read_state_changing(review);
|
|
5688
|
+
if (state !== void 0) latest.set(read_reviewer(review), state);
|
|
5689
|
+
}
|
|
5690
|
+
return latest;
|
|
5691
|
+
}
|
|
5692
|
+
function to_review_decision(reviews_json) {
|
|
5693
|
+
const latest = new Set(fold_latest_states(parse_rest_reviews(reviews_json)).values());
|
|
5694
|
+
if (latest.has(REVIEW_CHANGES_REQUESTED)) return REVIEW_CHANGES_REQUESTED;
|
|
5695
|
+
return latest.has(REVIEW_APPROVED) ? REVIEW_APPROVED : REVIEW_NONE;
|
|
5696
|
+
}
|
|
5697
|
+
var git_gh_pr_review = { to_review_decision };
|
|
5698
|
+
|
|
5699
|
+
// scripts/gh/git-gh-pr-rollup.ts
|
|
5700
|
+
import { z as z16 } from "zod";
|
|
5701
|
+
var STATUS_CONTEXT_TYPE_NAME = "StatusContext";
|
|
5702
|
+
var TYPE_NAME_KEY = "__typename";
|
|
5703
|
+
var rollup_element_schema = z16.looseObject({});
|
|
5704
|
+
var check_runs_page_schema = z16.looseObject({
|
|
5705
|
+
// Required, not optional: GitHub always names the key, and accepting a page without one would
|
|
5706
|
+
// answer an empty rollup — which `git-pr-followup.ts` reports as "this branch has no checks".
|
|
5707
|
+
check_runs: z16.array(rollup_element_schema)
|
|
5708
|
+
});
|
|
5709
|
+
var id_holder_schema = z16.object({ id: z16.number() });
|
|
5710
|
+
var status_page_schema = z16.looseObject({
|
|
5711
|
+
statuses: z16.array(rollup_element_schema)
|
|
5712
|
+
});
|
|
5713
|
+
var NOT_A_CHECK_RUNS_LISTING = "gh api answered something other than a check run listing";
|
|
5714
|
+
var NOT_A_STATUS_LISTING = "gh api answered something other than a commit status listing";
|
|
5715
|
+
function read_pages(raw_json, schema, message) {
|
|
5716
|
+
const pages = parse_json_array_or_undefined(raw_json, schema);
|
|
5717
|
+
if (pages === void 0) throw new Error(message);
|
|
5718
|
+
return pages;
|
|
5719
|
+
}
|
|
5720
|
+
var NO_SUITE = -1;
|
|
5721
|
+
function read_id(value) {
|
|
5722
|
+
const parsed = id_holder_schema.safeParse(value);
|
|
5723
|
+
return parsed.success ? parsed.data.id : void 0;
|
|
5724
|
+
}
|
|
5725
|
+
function check_run_group_key(run3) {
|
|
5726
|
+
const { name, app } = run3;
|
|
5727
|
+
if (typeof name !== "string") return void 0;
|
|
5728
|
+
return JSON.stringify([name, read_id(app)]);
|
|
5729
|
+
}
|
|
5730
|
+
function read_rank(run3) {
|
|
5731
|
+
const id = read_id(run3);
|
|
5732
|
+
if (id === void 0) return void 0;
|
|
5733
|
+
return { suite: read_id(run3["check_suite"]) ?? NO_SUITE, run: id };
|
|
5734
|
+
}
|
|
5735
|
+
function is_ranked_above(candidate, current) {
|
|
5736
|
+
if (candidate.suite !== current.suite) return candidate.suite > current.suite;
|
|
5737
|
+
return candidate.run > current.run;
|
|
5738
|
+
}
|
|
5739
|
+
function should_replace(previous, rank) {
|
|
5740
|
+
const previous_rank = previous === void 0 ? void 0 : read_rank(previous);
|
|
5741
|
+
return previous_rank === void 0 || is_ranked_above(rank, previous_rank);
|
|
5742
|
+
}
|
|
5743
|
+
function remember_newest_run(run3, newest) {
|
|
5744
|
+
const key2 = check_run_group_key(run3);
|
|
5745
|
+
const rank = read_rank(run3);
|
|
5746
|
+
if (key2 === void 0 || rank === void 0) return;
|
|
5747
|
+
if (should_replace(newest.get(key2), rank)) newest.set(key2, run3);
|
|
5748
|
+
}
|
|
5749
|
+
function is_newest_run(run3, newest) {
|
|
5750
|
+
const key2 = check_run_group_key(run3);
|
|
5751
|
+
if (key2 === void 0 || read_rank(run3) === void 0) return true;
|
|
5752
|
+
return newest.get(key2) === run3;
|
|
5753
|
+
}
|
|
5754
|
+
function to_check_run_items(check_runs_json) {
|
|
5755
|
+
const pages = read_pages(check_runs_json, check_runs_page_schema, NOT_A_CHECK_RUNS_LISTING);
|
|
5756
|
+
const runs = pages.flatMap((page) => page.check_runs);
|
|
5757
|
+
const newest = /* @__PURE__ */ new Map();
|
|
5758
|
+
for (const run3 of runs) remember_newest_run(run3, newest);
|
|
5759
|
+
return runs.filter((run3) => is_newest_run(run3, newest));
|
|
5760
|
+
}
|
|
5761
|
+
function to_status_context_items(status_json) {
|
|
5762
|
+
const pages = read_pages(status_json, status_page_schema, NOT_A_STATUS_LISTING);
|
|
5763
|
+
return pages.flatMap((page) => page.statuses).map((status2) => ({ ...status2, [TYPE_NAME_KEY]: STATUS_CONTEXT_TYPE_NAME }));
|
|
5764
|
+
}
|
|
5765
|
+
function to_status_check_rollup(input) {
|
|
5766
|
+
return [
|
|
5767
|
+
...to_check_run_items(input.check_runs_json),
|
|
5768
|
+
...to_status_context_items(input.status_json)
|
|
5769
|
+
];
|
|
5770
|
+
}
|
|
5771
|
+
var git_gh_pr_rollup = {
|
|
5772
|
+
to_status_check_rollup
|
|
5773
|
+
};
|
|
5774
|
+
|
|
5775
|
+
// scripts/gh/git-gh-pr-snapshot.ts
|
|
5776
|
+
var NO_HEAD_SHA_MESSAGE = "gh api answered a pull request with no head commit";
|
|
5777
|
+
async function read_object_pages(path6) {
|
|
5778
|
+
return await git_gh_exec.exec_gh_api({
|
|
5779
|
+
path: `${path6}${FULL_PAGE_QUERY}`,
|
|
5780
|
+
should_paginate: true,
|
|
5781
|
+
should_slurp: true
|
|
5782
|
+
});
|
|
5783
|
+
}
|
|
5784
|
+
async function read_array_pages(path6) {
|
|
5785
|
+
return await git_gh_exec.exec_gh_api({
|
|
5786
|
+
path: `${path6}${FULL_PAGE_QUERY}`,
|
|
5787
|
+
should_paginate: true
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
function read_head_sha(pull) {
|
|
5791
|
+
const sha = pull.head?.sha;
|
|
5792
|
+
if (sha === void 0) throw new Error(NO_HEAD_SHA_MESSAGE);
|
|
5793
|
+
return sha;
|
|
5794
|
+
}
|
|
5795
|
+
async function read_status_check_rollup(commit_sha) {
|
|
5796
|
+
const [check_runs_json, status_json] = await Promise.all([
|
|
5797
|
+
read_object_pages(git_gh_api_path.commit_check_runs_api_path(commit_sha)),
|
|
5798
|
+
read_object_pages(git_gh_api_path.commit_status_api_path(commit_sha))
|
|
5799
|
+
]);
|
|
5800
|
+
return git_gh_pr_rollup.to_status_check_rollup({ check_runs_json, status_json });
|
|
5801
|
+
}
|
|
5802
|
+
async function pr_get_review_decision(pr_number) {
|
|
5803
|
+
const path6 = git_gh_api_path.pull_reviews_api_path(String(pr_number));
|
|
5804
|
+
return git_gh_pr_review.to_review_decision(await read_array_pages(path6));
|
|
5805
|
+
}
|
|
5806
|
+
async function build_checks_fields(pull) {
|
|
5807
|
+
return {
|
|
5808
|
+
statusCheckRollup: await read_status_check_rollup(read_head_sha(pull)),
|
|
5809
|
+
mergeStateStatus: to_gh_state(pull.mergeable_state)
|
|
5810
|
+
};
|
|
5811
|
+
}
|
|
5812
|
+
async function resolve_pull(branch_name) {
|
|
5813
|
+
const pr_number = await require_pr_number(branch_name);
|
|
5814
|
+
return { pr_number, pull: await read_pull(pr_number) };
|
|
5815
|
+
}
|
|
5816
|
+
async function pr_get_checks_snapshot(branch_name) {
|
|
5817
|
+
const { pr_number, pull } = await resolve_pull(branch_name);
|
|
5818
|
+
return { pr_number, snapshot_json: JSON.stringify(await build_checks_fields(pull)) };
|
|
5819
|
+
}
|
|
5820
|
+
async function pr_get_state_snapshot(branch_name) {
|
|
5821
|
+
const { pr_number, pull } = await resolve_pull(branch_name);
|
|
5822
|
+
const [fields, review_decision] = await Promise.all([
|
|
5823
|
+
build_checks_fields(pull),
|
|
5824
|
+
pr_get_review_decision(pr_number)
|
|
5825
|
+
]);
|
|
5826
|
+
return JSON.stringify({ ...fields, reviewDecision: review_decision });
|
|
5827
|
+
}
|
|
5828
|
+
var git_gh_pr_snapshot = {
|
|
5829
|
+
pr_get_state_snapshot,
|
|
5830
|
+
pr_get_checks_snapshot,
|
|
5831
|
+
pr_get_review_decision
|
|
5832
|
+
};
|
|
5833
|
+
|
|
5834
|
+
// scripts/gh/git-gh-pr.ts
|
|
5835
|
+
var HTML_URL_FILTER2 = ".html_url";
|
|
5836
|
+
var PUT_METHOD = "PUT";
|
|
5837
|
+
var PATCH_METHOD2 = "PATCH";
|
|
5838
|
+
var MERGE_COMMIT_METHOD = "merge";
|
|
5839
|
+
var MERGE_REQUEST_TIMEOUT_MS = 18e4;
|
|
5840
|
+
var MERGE_UNCONFIRMED_MESSAGE = "gh api could not read the pull request back after the merge request failed";
|
|
5841
|
+
var MERGE_READ_BACK_ATTEMPTS = 3;
|
|
5842
|
+
var MERGE_READ_BACK_INTERVAL_MS = 1e3;
|
|
5843
|
+
var PR_NUMBER_PATTERN = /\/pull\/([1-9]\d*)$/u;
|
|
5844
|
+
var LABELS_SEGMENT2 = "/labels";
|
|
5845
|
+
var PR_LABELS = z17.array(z17.object({ name: z17.string() }));
|
|
5846
|
+
async function add_pr_classification(number, label2) {
|
|
5847
|
+
await git_gh_exec.exec_gh_api({
|
|
5848
|
+
path: `${git_gh_api_path.issue_api_path(number)}${LABELS_SEGMENT2}`,
|
|
5849
|
+
body: JSON.stringify({ labels: [label2] })
|
|
5850
|
+
});
|
|
5851
|
+
}
|
|
5852
|
+
async function pr_get_classification(branch_name) {
|
|
5853
|
+
const number = await require_pr_number(branch_name);
|
|
5854
|
+
const response = await git_gh_exec.exec_gh_api({
|
|
5855
|
+
path: `${git_gh_api_path.issue_api_path(String(number))}${LABELS_SEGMENT2}`
|
|
5856
|
+
});
|
|
5857
|
+
const names = PR_LABELS.parse(JSON.parse(response)).map((label2) => label2.name.toLowerCase());
|
|
5858
|
+
const selected = names.filter((name) => pr_classification.is_classification(name));
|
|
5859
|
+
if (selected.length === 0) return void 0;
|
|
5860
|
+
const error = pr_classification.classification_error(names, "human");
|
|
5861
|
+
if (error !== void 0) throw new Error(error);
|
|
5862
|
+
return selected[0];
|
|
5863
|
+
}
|
|
5864
|
+
async function pr_ensure_classification(branch_name, label2) {
|
|
5865
|
+
if (await pr_get_classification(branch_name) !== void 0) return;
|
|
5866
|
+
const number = await require_pr_number(branch_name);
|
|
5867
|
+
await add_pr_classification(String(number), label2);
|
|
5868
|
+
}
|
|
5869
|
+
async function post_pull_request(title, body) {
|
|
5870
|
+
const base = await git_command.get_default_branch();
|
|
5871
|
+
const head = await git_command.branch();
|
|
5872
|
+
forget_pr_numbers();
|
|
5873
|
+
try {
|
|
5874
|
+
return await git_gh_exec.exec_gh_api({
|
|
5875
|
+
path: git_gh_api_path.pulls_api_path(),
|
|
5876
|
+
body: JSON.stringify({ title, body, head, base }),
|
|
5877
|
+
jq_filter: HTML_URL_FILTER2
|
|
5878
|
+
});
|
|
5879
|
+
} catch (error) {
|
|
5880
|
+
return git_gh_helpers.handle_pr_create_error(error);
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
async function pr_create(title, body, label2) {
|
|
5884
|
+
const classification_error2 = pr_classification.classification_error([label2], "human");
|
|
5885
|
+
if (classification_error2 !== void 0) throw new Error(classification_error2);
|
|
5886
|
+
const url = await post_pull_request(title, body);
|
|
5887
|
+
const number = PR_NUMBER_PATTERN.exec(url)?.[1];
|
|
5888
|
+
if (number === void 0) throw new Error(`Could not identify the created pull request: ${url}`);
|
|
5889
|
+
await add_pr_classification(number, label2);
|
|
5890
|
+
return url;
|
|
5891
|
+
}
|
|
5892
|
+
async function pr_comment(branch_name, body) {
|
|
5893
|
+
const pr_number = await require_pr_number(branch_name);
|
|
5894
|
+
return await git_gh_issue_write.issue_comment(String(pr_number), body);
|
|
5895
|
+
}
|
|
5896
|
+
async function pr_update_body(branch_name, body) {
|
|
5897
|
+
const pr_number = await require_pr_number(branch_name);
|
|
5898
|
+
await git_gh_exec.exec_gh_api({
|
|
5899
|
+
path: git_gh_api_path.pull_api_path(String(pr_number)),
|
|
5900
|
+
method: PATCH_METHOD2,
|
|
5901
|
+
body: JSON.stringify({ body })
|
|
5902
|
+
});
|
|
5903
|
+
}
|
|
5904
|
+
async function put_merge(pr_number) {
|
|
5905
|
+
await git_gh_exec.exec_gh_api({
|
|
5906
|
+
path: git_gh_api_path.pull_merge_api_path(String(pr_number)),
|
|
5907
|
+
method: PUT_METHOD,
|
|
5908
|
+
body: JSON.stringify({ merge_method: MERGE_COMMIT_METHOD }),
|
|
5909
|
+
timeout_ms: MERGE_REQUEST_TIMEOUT_MS
|
|
5910
|
+
});
|
|
5911
|
+
}
|
|
5912
|
+
async function read_merge_state(pr_number) {
|
|
5913
|
+
try {
|
|
5914
|
+
return git_gh_pr_rest.is_merged(await read_pull(pr_number));
|
|
5915
|
+
} catch (error) {
|
|
5916
|
+
console.warn(`Could not read the pull request back: ${String(error)}`);
|
|
5917
|
+
return void 0;
|
|
5918
|
+
}
|
|
5919
|
+
}
|
|
5920
|
+
async function sleep_between_read_backs(attempt) {
|
|
5921
|
+
if (attempt < MERGE_READ_BACK_ATTEMPTS - 1) await poll.sleep(MERGE_READ_BACK_INTERVAL_MS);
|
|
5922
|
+
}
|
|
5923
|
+
async function has_merge_landed(pr_number, merge_error) {
|
|
5924
|
+
for (let attempt = 0; attempt < MERGE_READ_BACK_ATTEMPTS; attempt += 1) {
|
|
5925
|
+
const is_merged2 = await read_merge_state(pr_number);
|
|
5926
|
+
if (is_merged2 !== void 0) return is_merged2;
|
|
5927
|
+
await sleep_between_read_backs(attempt);
|
|
5928
|
+
}
|
|
5929
|
+
throw new Error(MERGE_UNCONFIRMED_MESSAGE, { cause: merge_error });
|
|
5930
|
+
}
|
|
5931
|
+
async function pr_merge(branch_name) {
|
|
5932
|
+
const pr_number = await require_pr_number(branch_name);
|
|
5933
|
+
try {
|
|
5934
|
+
await put_merge(pr_number);
|
|
5935
|
+
} catch (error) {
|
|
5936
|
+
if (!await has_merge_landed(pr_number, error)) throw error;
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
var git_gh_pr = {
|
|
5940
|
+
...git_gh_pr_read,
|
|
5941
|
+
...git_gh_pr_snapshot,
|
|
5942
|
+
pr_create,
|
|
5943
|
+
pr_get_classification,
|
|
5944
|
+
pr_ensure_classification,
|
|
5945
|
+
pr_comment,
|
|
5946
|
+
pr_merge,
|
|
5947
|
+
pr_update_body
|
|
5948
|
+
};
|
|
5949
|
+
|
|
5950
|
+
// scripts/gh/git-gh-pr-auto-merge.ts
|
|
5951
|
+
var GRAPHQL_PATH = "graphql";
|
|
5952
|
+
var OPEN_PULLS_QUERY = `${git_gh_api_path.FULL_PAGE_QUERY}&state=open`;
|
|
5953
|
+
var MISSING_NODE_ID_MESSAGE = "gh api answered a pull request without a node_id";
|
|
5954
|
+
var ENABLE_AUTO_MERGE_MUTATION = "mutation($id: ID!) { enablePullRequestAutoMerge(input: { pullRequestId: $id, mergeMethod: MERGE }) { clientMutationId } }";
|
|
5955
|
+
async function pr_enable_auto_merge(branch_name) {
|
|
5956
|
+
const pull = await read_pull(await require_pr_number(branch_name));
|
|
5957
|
+
if (pull.node_id === void 0) throw new Error(MISSING_NODE_ID_MESSAGE);
|
|
5958
|
+
await git_gh_exec.exec_gh_api({
|
|
5959
|
+
path: GRAPHQL_PATH,
|
|
5960
|
+
body: JSON.stringify({ query: ENABLE_AUTO_MERGE_MUTATION, variables: { id: pull.node_id } })
|
|
5961
|
+
});
|
|
5962
|
+
}
|
|
5963
|
+
async function pr_is_merged(branch_name) {
|
|
5964
|
+
try {
|
|
5965
|
+
return git_gh_pr_rest.is_merged(await read_pull(await require_pr_number(branch_name)));
|
|
5966
|
+
} catch (error) {
|
|
5967
|
+
error_text.trace_swallowed("git_gh_pr_auto_merge.pr_is_merged", error);
|
|
5968
|
+
return false;
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
function head_reference_of(pull) {
|
|
5972
|
+
return pull.head?.ref ?? "";
|
|
5973
|
+
}
|
|
5974
|
+
function has_head_prefix(pull, prefix) {
|
|
5975
|
+
return git_gh_pr_rest.is_same_repository_head(pull) && head_reference_of(pull).startsWith(prefix);
|
|
5976
|
+
}
|
|
5977
|
+
function to_open_pull(pull) {
|
|
5978
|
+
return {
|
|
5979
|
+
number: pull.number,
|
|
5980
|
+
head_ref: head_reference_of(pull),
|
|
5981
|
+
has_auto_merge: pull.auto_merge !== null && pull.auto_merge !== void 0
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
async function pr_list_open_with_head_prefix(prefix) {
|
|
5985
|
+
const json = await git_gh_exec.exec_gh_api({
|
|
5986
|
+
path: `${git_gh_api_path.pulls_api_path()}${OPEN_PULLS_QUERY}`,
|
|
5987
|
+
should_paginate: true
|
|
5988
|
+
});
|
|
5989
|
+
return git_gh_pr_rest.parse_rest_pulls(json).filter((pull) => has_head_prefix(pull, prefix)).map((pull) => to_open_pull(pull));
|
|
5990
|
+
}
|
|
5991
|
+
var git_gh_pr_auto_merge = {
|
|
5992
|
+
pr_enable_auto_merge,
|
|
5993
|
+
pr_is_merged,
|
|
5994
|
+
pr_list_open_with_head_prefix
|
|
5995
|
+
};
|
|
5996
|
+
|
|
5997
|
+
// scripts/gh/git-gh-repo.ts
|
|
5998
|
+
async function repo_get_name_with_owner() {
|
|
5999
|
+
try {
|
|
6000
|
+
const result = await git_gh_exec.exec_gh_api({
|
|
6001
|
+
path: git_gh_api_path.repo_api_path(),
|
|
6002
|
+
jq_filter: ".full_name"
|
|
6003
|
+
});
|
|
6004
|
+
return git_gh_helpers.parse_pr_state_string(result);
|
|
6005
|
+
} catch {
|
|
6006
|
+
return void 0;
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
6009
|
+
var git_gh_repo = {
|
|
6010
|
+
repo_get_name_with_owner
|
|
6011
|
+
};
|
|
6012
|
+
|
|
6013
|
+
// scripts/gh/git-pr-checks-parse.ts
|
|
6014
|
+
var CHECK_STATUS_PASS = "pass";
|
|
6015
|
+
var CHECK_STATUS_PENDING = "pending";
|
|
6016
|
+
var CHECK_STATUS_FAIL = "fail";
|
|
6017
|
+
var CHECK_STATUS_MISSING = "missing";
|
|
6018
|
+
var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["success", "skipped", "neutral"]);
|
|
6019
|
+
var KEY_TYPE_NAME = "__typename";
|
|
6020
|
+
var KEY_STATE = "state";
|
|
6021
|
+
var KEY_STATUS = "status";
|
|
6022
|
+
var KEY_CONCLUSION = "conclusion";
|
|
6023
|
+
var KEY_NAME = "name";
|
|
6024
|
+
var KEY_CONTEXT = "context";
|
|
6025
|
+
function read_string(value) {
|
|
6026
|
+
if (typeof value !== "string") return void 0;
|
|
6027
|
+
const trimmed2 = value.trim();
|
|
6028
|
+
if (trimmed2.length === 0) return void 0;
|
|
6029
|
+
return trimmed2;
|
|
6030
|
+
}
|
|
6031
|
+
var PENDING_STATUS_STATES = /* @__PURE__ */ new Set(["pending", "expected"]);
|
|
6032
|
+
function parse_status_context(item) {
|
|
6033
|
+
const state = read_string(item[KEY_STATE])?.toLowerCase();
|
|
6034
|
+
if (state === "success") return CHECK_STATUS_PASS;
|
|
6035
|
+
if (state !== void 0 && PENDING_STATUS_STATES.has(state)) return CHECK_STATUS_PENDING;
|
|
6036
|
+
return CHECK_STATUS_FAIL;
|
|
6037
|
+
}
|
|
6038
|
+
function is_passing_conclusion(item) {
|
|
6039
|
+
const conclusion = read_string(item[KEY_CONCLUSION])?.toLowerCase();
|
|
6040
|
+
return conclusion !== void 0 && PASSING_CONCLUSIONS.has(conclusion);
|
|
6041
|
+
}
|
|
6042
|
+
function parse_check_run(item) {
|
|
6043
|
+
const status2 = read_string(item[KEY_STATUS])?.toLowerCase();
|
|
6044
|
+
if (status2 !== "completed") return CHECK_STATUS_PENDING;
|
|
6045
|
+
return is_passing_conclusion(item) ? CHECK_STATUS_PASS : CHECK_STATUS_FAIL;
|
|
6046
|
+
}
|
|
6047
|
+
function parse_rollup_status(item) {
|
|
6048
|
+
const type_name = read_string(item[KEY_TYPE_NAME]);
|
|
6049
|
+
if (type_name === "StatusContext") return parse_status_context(item);
|
|
6050
|
+
return parse_check_run(item);
|
|
6051
|
+
}
|
|
6052
|
+
function read_rollup_array(parsed) {
|
|
6053
|
+
const result = pr_raw_schema.safeParse(parsed);
|
|
6054
|
+
if (!result.success) return [];
|
|
6055
|
+
return result.data.statusCheckRollup ?? [];
|
|
6056
|
+
}
|
|
6057
|
+
function parse_rollup_item(item) {
|
|
6058
|
+
const name = read_string(item[KEY_NAME]) ?? read_string(item[KEY_CONTEXT]);
|
|
6059
|
+
if (name === void 0) return void 0;
|
|
6060
|
+
return { name, status: parse_rollup_status(item) };
|
|
6061
|
+
}
|
|
6062
|
+
function parse_rollup_checks(raw_json) {
|
|
6063
|
+
const parsed = json_value.parse_or_undefined(raw_json);
|
|
6064
|
+
const rollup = read_rollup_array(parsed);
|
|
6065
|
+
const checks = [];
|
|
6066
|
+
for (const item of rollup) {
|
|
6067
|
+
const parsed_item = parse_rollup_item(item);
|
|
6068
|
+
if (parsed_item !== void 0) checks.push(parsed_item);
|
|
6069
|
+
}
|
|
6070
|
+
return checks;
|
|
6071
|
+
}
|
|
6072
|
+
function parse_pr_state_snapshot(raw_json) {
|
|
6073
|
+
const parsed = json_value.parse_or_undefined(raw_json);
|
|
6074
|
+
const result = pr_raw_schema.safeParse(parsed);
|
|
6075
|
+
const data = result.success ? result.data : void 0;
|
|
6076
|
+
return {
|
|
6077
|
+
rollup: parse_rollup_checks(raw_json),
|
|
6078
|
+
merge_state_status: read_string(data?.mergeStateStatus),
|
|
6079
|
+
review_decision: read_string(data?.reviewDecision)
|
|
6080
|
+
};
|
|
6081
|
+
}
|
|
6082
|
+
|
|
6083
|
+
// scripts/gh/git-pr-checks-eval.ts
|
|
6084
|
+
var DEFAULT_REQUIRED_CHECKS = ["SonarQube"];
|
|
6085
|
+
var CODERABBIT_CHECK_NAME = "CodeRabbit";
|
|
6086
|
+
var REQUIRED_CHECKS_ENV_VAR = "JOSH_REQUIRED_CHECKS";
|
|
6087
|
+
function parse_required_checks() {
|
|
6088
|
+
const raw = process.env[REQUIRED_CHECKS_ENV_VAR];
|
|
6089
|
+
if (!raw) return DEFAULT_REQUIRED_CHECKS;
|
|
6090
|
+
return raw.split(",").map((check) => check.trim()).filter((check) => check.length > 0);
|
|
6091
|
+
}
|
|
6092
|
+
var REQUIRED_CHECKS = parse_required_checks();
|
|
6093
|
+
var MERGE_STATE_CLEAN = "CLEAN";
|
|
6094
|
+
var MERGE_STATE_UNSTABLE = "UNSTABLE";
|
|
6095
|
+
var MERGE_STATE_DIRTY = "DIRTY";
|
|
6096
|
+
var REVIEW_CHANGES_REQUESTED2 = "CHANGES_REQUESTED";
|
|
6097
|
+
var CHECK_SUITE_SEPARATOR = " / ";
|
|
6098
|
+
var FAILURE_MESSAGE_PREFIX = "PR checks failed";
|
|
6099
|
+
var FAILURE_REASON_REVIEW = "review requested changes";
|
|
6100
|
+
var FAILURE_REASON_CHECKS = "failed checks";
|
|
6101
|
+
var FAILURE_REASON_CONFLICT = "merge conflict";
|
|
6102
|
+
function is_required_match(check_name, required_name) {
|
|
6103
|
+
if (check_name === required_name) return true;
|
|
6104
|
+
return check_name.startsWith(`${required_name}${CHECK_SUITE_SEPARATOR}`);
|
|
6105
|
+
}
|
|
6106
|
+
function find_required_check(checks, required_name) {
|
|
6107
|
+
return checks.find((check) => is_required_match(check.name, required_name));
|
|
6108
|
+
}
|
|
6109
|
+
function read_required_statuses(checks) {
|
|
6110
|
+
return REQUIRED_CHECKS.map(
|
|
6111
|
+
(name) => find_required_check(checks, name)?.status ?? CHECK_STATUS_MISSING
|
|
6112
|
+
);
|
|
6113
|
+
}
|
|
6114
|
+
function is_review_blocked(review_decision) {
|
|
6115
|
+
return review_decision === REVIEW_CHANGES_REQUESTED2;
|
|
6116
|
+
}
|
|
6117
|
+
function is_merge_conflict(merge_state_status) {
|
|
6118
|
+
return merge_state_status === MERGE_STATE_DIRTY;
|
|
6119
|
+
}
|
|
6120
|
+
function evaluate_failure_state(input) {
|
|
6121
|
+
if (is_merge_conflict(input.merge_state_status) || is_review_blocked(input.review_decision) || input.failed_checks.length > 0) {
|
|
6122
|
+
return "failure";
|
|
6123
|
+
}
|
|
6124
|
+
return void 0;
|
|
6125
|
+
}
|
|
6126
|
+
function is_merge_state_clean(merge_state_status) {
|
|
6127
|
+
return merge_state_status === MERGE_STATE_CLEAN;
|
|
6128
|
+
}
|
|
6129
|
+
function is_every_required_passing(statuses) {
|
|
6130
|
+
return statuses.every((status2) => status2 === CHECK_STATUS_PASS);
|
|
6131
|
+
}
|
|
6132
|
+
function is_coderabbit_check(check_name) {
|
|
6133
|
+
return is_required_match(check_name, CODERABBIT_CHECK_NAME);
|
|
6134
|
+
}
|
|
6135
|
+
function is_unstable_only_from_coderabbit(snapshot) {
|
|
6136
|
+
if (snapshot.merge_state_status !== MERGE_STATE_UNSTABLE) return false;
|
|
6137
|
+
const non_passing = snapshot.rollup.filter((check) => check.status !== CHECK_STATUS_PASS);
|
|
6138
|
+
if (non_passing.length === 0) return false;
|
|
6139
|
+
return non_passing.every((check) => is_coderabbit_check(check.name));
|
|
6140
|
+
}
|
|
6141
|
+
function is_required_check(check_name) {
|
|
6142
|
+
return REQUIRED_CHECKS.some((required) => is_required_match(check_name, required));
|
|
6143
|
+
}
|
|
6144
|
+
function is_blocking_failure(check) {
|
|
6145
|
+
if (check.status !== CHECK_STATUS_FAIL) return false;
|
|
6146
|
+
return !is_coderabbit_check(check.name) || is_required_check(check.name);
|
|
6147
|
+
}
|
|
6148
|
+
function collect_blocking_failures(snapshot) {
|
|
6149
|
+
return snapshot.rollup.filter((check) => is_blocking_failure(check)).map((check) => check.name);
|
|
6150
|
+
}
|
|
6151
|
+
function is_mergeable_state(snapshot) {
|
|
6152
|
+
if (is_merge_state_clean(snapshot.merge_state_status)) return true;
|
|
6153
|
+
return is_unstable_only_from_coderabbit(snapshot);
|
|
6154
|
+
}
|
|
6155
|
+
function collect_failure_reasons(snapshot) {
|
|
6156
|
+
const reasons = [];
|
|
6157
|
+
if (is_merge_conflict(snapshot.merge_state_status)) reasons.push(FAILURE_REASON_CONFLICT);
|
|
6158
|
+
if (is_review_blocked(snapshot.review_decision)) reasons.push(FAILURE_REASON_REVIEW);
|
|
6159
|
+
const failed = collect_blocking_failures(snapshot);
|
|
6160
|
+
if (failed.length > 0) reasons.push(`${FAILURE_REASON_CHECKS}: ${failed.join(", ")}`);
|
|
6161
|
+
return reasons;
|
|
6162
|
+
}
|
|
6163
|
+
function describe_pr_failure(snapshot) {
|
|
6164
|
+
const reasons = collect_failure_reasons(snapshot);
|
|
6165
|
+
if (reasons.length === 0) return `${FAILURE_MESSAGE_PREFIX}.`;
|
|
6166
|
+
return `${FAILURE_MESSAGE_PREFIX} (${reasons.join("; ")}).`;
|
|
6167
|
+
}
|
|
6168
|
+
function evaluate_pr_state(snapshot) {
|
|
6169
|
+
const statuses = read_required_statuses(snapshot.rollup);
|
|
6170
|
+
const failure = evaluate_failure_state({
|
|
6171
|
+
review_decision: snapshot.review_decision,
|
|
6172
|
+
failed_checks: collect_blocking_failures(snapshot),
|
|
6173
|
+
merge_state_status: snapshot.merge_state_status
|
|
6174
|
+
});
|
|
6175
|
+
if (failure !== void 0) return failure;
|
|
6176
|
+
if (is_mergeable_state(snapshot) && is_every_required_passing(statuses)) {
|
|
6177
|
+
return "success";
|
|
6178
|
+
}
|
|
6179
|
+
return "pending";
|
|
6180
|
+
}
|
|
6181
|
+
function is_review_decision_decisive(snapshot) {
|
|
6182
|
+
return evaluate_pr_state(snapshot) !== "pending";
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
// scripts/gh/git-pr-checks.ts
|
|
6186
|
+
var SECONDS_TO_MS = 1e3;
|
|
6187
|
+
var CHECK_WAIT_INTERVAL_MS = 1e4;
|
|
6188
|
+
var DEFAULT_TIMEOUT_SECONDS = 1920;
|
|
6189
|
+
function compute_max_attempts(timeout_seconds, interval_ms) {
|
|
6190
|
+
return Math.ceil(timeout_seconds / (interval_ms / SECONDS_TO_MS)) + 1;
|
|
6191
|
+
}
|
|
6192
|
+
var DEFAULT_MAX_ATTEMPTS = compute_max_attempts(DEFAULT_TIMEOUT_SECONDS, CHECK_WAIT_INTERVAL_MS);
|
|
6193
|
+
function get_configured_max_attempts() {
|
|
6194
|
+
const environment_seconds = Number(process.env["JOSH_CI_TIMEOUT_SECONDS"]);
|
|
6195
|
+
return Number.isFinite(environment_seconds) && environment_seconds > 0 ? compute_max_attempts(environment_seconds, CHECK_WAIT_INTERVAL_MS) : DEFAULT_MAX_ATTEMPTS;
|
|
6196
|
+
}
|
|
6197
|
+
var CHECK_MAX_ATTEMPTS = get_configured_max_attempts();
|
|
6198
|
+
var DEFAULT_STABLE_READS = 2;
|
|
6199
|
+
var MAX_CONSECUTIVE_READ_FAILURES = 3;
|
|
6200
|
+
var PR_CHECKS_TIMEOUT_MESSAGE = "Timed out while waiting for PR checks to complete.";
|
|
6201
|
+
function is_pr_checks_timeout(error) {
|
|
6202
|
+
return error instanceof Error && error.message === PR_CHECKS_TIMEOUT_MESSAGE;
|
|
6203
|
+
}
|
|
6204
|
+
var SHOULD_ALWAYS_READ_REVIEW_DECISION = () => true;
|
|
6205
|
+
var MERGE_GATE_EVALUATOR = {
|
|
6206
|
+
evaluate: evaluate_pr_state,
|
|
6207
|
+
describe: describe_pr_failure,
|
|
6208
|
+
should_read_review_decision: is_review_decision_decisive
|
|
6209
|
+
};
|
|
6210
|
+
function advance_stable_count(previous, state) {
|
|
6211
|
+
return state === "success" ? previous + 1 : 0;
|
|
6212
|
+
}
|
|
6213
|
+
function classify_poll_result(input) {
|
|
6214
|
+
const state = input.evaluator.evaluate(input.snapshot);
|
|
6215
|
+
if (state === "failure") throw new Error(input.evaluator.describe(input.snapshot));
|
|
6216
|
+
const next_stable_count = advance_stable_count(input.stable_count, state);
|
|
6217
|
+
return {
|
|
6218
|
+
is_done: next_stable_count >= input.required_stable_reads,
|
|
6219
|
+
next_stable_count
|
|
6220
|
+
};
|
|
6221
|
+
}
|
|
6222
|
+
async function read_pr_state(input, evaluator) {
|
|
6223
|
+
try {
|
|
6224
|
+
const snapshot = await input.options.fetcher(
|
|
6225
|
+
input.options.branch_name,
|
|
6226
|
+
evaluator.should_read_review_decision ?? SHOULD_ALWAYS_READ_REVIEW_DECISION
|
|
6227
|
+
);
|
|
6228
|
+
return { kind: "read", snapshot };
|
|
6229
|
+
} catch (error) {
|
|
6230
|
+
return { kind: "unreadable", cause: error };
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
async function sleep_before_next_attempt(input) {
|
|
6234
|
+
if (input.attempt < input.options.max_attempts - 1) {
|
|
6235
|
+
await poll.sleep(input.options.interval_ms);
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
async function retry_after_unreadable(input, cause) {
|
|
6239
|
+
const next_failure_count = input.failure_count + 1;
|
|
6240
|
+
if (next_failure_count >= MAX_CONSECUTIVE_READ_FAILURES) throw cause;
|
|
6241
|
+
console.warn(
|
|
6242
|
+
`Could not read the PR state (${String(next_failure_count)}/${String(MAX_CONSECUTIVE_READ_FAILURES)}); asking again on the next poll: ${String(cause)}`
|
|
6243
|
+
);
|
|
6244
|
+
await sleep_before_next_attempt(input);
|
|
6245
|
+
return { next_stable_count: 0, next_failure_count, read_failure: cause };
|
|
6246
|
+
}
|
|
6247
|
+
function to_exhaustion_error(last_read_failure) {
|
|
6248
|
+
return last_read_failure ?? new Error(PR_CHECKS_TIMEOUT_MESSAGE);
|
|
6249
|
+
}
|
|
6250
|
+
async function attempt_pr_success_poll(input) {
|
|
6251
|
+
const evaluator = input.options.evaluator ?? MERGE_GATE_EVALUATOR;
|
|
6252
|
+
const read3 = await read_pr_state(input, evaluator);
|
|
6253
|
+
if (read3.kind === "unreadable") return await retry_after_unreadable(input, read3.cause);
|
|
6254
|
+
const classification = classify_poll_result({
|
|
6255
|
+
snapshot: read3.snapshot,
|
|
6256
|
+
stable_count: input.stable_count,
|
|
6257
|
+
required_stable_reads: input.options.required_stable_reads,
|
|
6258
|
+
evaluator
|
|
6259
|
+
});
|
|
6260
|
+
if (classification.is_done) {
|
|
6261
|
+
return { snapshot: read3.snapshot, next_stable_count: 0, next_failure_count: 0 };
|
|
6262
|
+
}
|
|
6263
|
+
await sleep_before_next_attempt(input);
|
|
6264
|
+
return { next_stable_count: classification.next_stable_count, next_failure_count: 0 };
|
|
6265
|
+
}
|
|
6266
|
+
async function wait_for_pr_success(options) {
|
|
6267
|
+
let carried = { next_stable_count: 0, next_failure_count: 0 };
|
|
6268
|
+
for (let attempt = 0; attempt < options.max_attempts; attempt += 1) {
|
|
6269
|
+
console.info(`Checking PR status\u2026 (${String(attempt + 1)}/${String(options.max_attempts)})`);
|
|
6270
|
+
carried = await attempt_pr_success_poll({
|
|
6271
|
+
options,
|
|
6272
|
+
stable_count: carried.next_stable_count,
|
|
6273
|
+
failure_count: carried.next_failure_count,
|
|
6274
|
+
attempt
|
|
6275
|
+
});
|
|
6276
|
+
if (carried.snapshot !== void 0) return carried.snapshot;
|
|
6277
|
+
}
|
|
6278
|
+
throw to_exhaustion_error(carried.read_failure);
|
|
6279
|
+
}
|
|
6280
|
+
async function default_fetch_pr_state(branch_name, should_read_review_decision = SHOULD_ALWAYS_READ_REVIEW_DECISION) {
|
|
6281
|
+
const checks = await git_gh_pr_snapshot.pr_get_checks_snapshot(branch_name);
|
|
6282
|
+
const snapshot = parse_pr_state_snapshot(checks.snapshot_json);
|
|
6283
|
+
if (!should_read_review_decision(snapshot)) return snapshot;
|
|
6284
|
+
const decision = await git_gh_pr_snapshot.pr_get_review_decision(checks.pr_number);
|
|
6285
|
+
return { ...snapshot, review_decision: read_string(decision) };
|
|
6286
|
+
}
|
|
6287
|
+
|
|
6288
|
+
// scripts/gh/git-pr-checks-watch.ts
|
|
6289
|
+
var PR_CHECKS_WATCH_TIMEOUT_MS = 12e4;
|
|
6290
|
+
var WATCH_TIMED_OUT_NOTE = "\u23F1\uFE0F pr checks --watch timed out.";
|
|
6291
|
+
var NO_CHECKS_MESSAGE = "gh api reported no checks on the branch";
|
|
6292
|
+
var CHECKS_FAILED_MESSAGE = "gh api reported a failing check on the branch";
|
|
6293
|
+
function is_awaited_check(check) {
|
|
6294
|
+
return !is_coderabbit_check(check.name) || is_required_check(check.name);
|
|
6295
|
+
}
|
|
6296
|
+
function evaluate_checks_settled(snapshot) {
|
|
6297
|
+
const awaited = snapshot.rollup.filter((check) => is_awaited_check(check));
|
|
6298
|
+
const considered = awaited.length === 0 ? snapshot.rollup : awaited;
|
|
6299
|
+
if (considered.length === 0 || considered.some((check) => check.status === CHECK_STATUS_PENDING)) {
|
|
6300
|
+
return "pending";
|
|
6301
|
+
}
|
|
6302
|
+
return snapshot.rollup.some((check) => check.status === CHECK_STATUS_FAIL) ? "failure" : "success";
|
|
6303
|
+
}
|
|
6304
|
+
function describe_checks_failure(snapshot) {
|
|
6305
|
+
const failed = snapshot.rollup.filter((check) => check.status === CHECK_STATUS_FAIL);
|
|
6306
|
+
return `${CHECKS_FAILED_MESSAGE}: ${failed.map((check) => check.name).join(", ")}`;
|
|
6307
|
+
}
|
|
6308
|
+
var SHOULD_NEVER_READ_REVIEW_DECISION = () => false;
|
|
6309
|
+
var CHECKS_SETTLED_EVALUATOR = {
|
|
6310
|
+
evaluate: evaluate_checks_settled,
|
|
6311
|
+
describe: describe_checks_failure,
|
|
6312
|
+
should_read_review_decision: SHOULD_NEVER_READ_REVIEW_DECISION
|
|
6313
|
+
};
|
|
6314
|
+
async function watch_until_settled(branch_name) {
|
|
6315
|
+
await wait_for_pr_success({
|
|
6316
|
+
branch_name,
|
|
6317
|
+
fetcher: default_fetch_pr_state,
|
|
6318
|
+
interval_ms: CHECK_WAIT_INTERVAL_MS,
|
|
6319
|
+
max_attempts: compute_max_attempts(
|
|
6320
|
+
PR_CHECKS_WATCH_TIMEOUT_MS / SECONDS_TO_MS,
|
|
6321
|
+
CHECK_WAIT_INTERVAL_MS
|
|
6322
|
+
),
|
|
6323
|
+
required_stable_reads: DEFAULT_STABLE_READS,
|
|
6324
|
+
evaluator: CHECKS_SETTLED_EVALUATOR
|
|
6325
|
+
});
|
|
6326
|
+
}
|
|
6327
|
+
async function fail_when_no_checks(branch_name) {
|
|
6328
|
+
const snapshot = await default_fetch_pr_state(branch_name, SHOULD_NEVER_READ_REVIEW_DECISION);
|
|
6329
|
+
if (snapshot.rollup.length === 0) throw new Error(NO_CHECKS_MESSAGE);
|
|
6330
|
+
}
|
|
6331
|
+
async function pr_checks_watch(branch_name) {
|
|
6332
|
+
try {
|
|
6333
|
+
await watch_until_settled(branch_name);
|
|
6334
|
+
return { timed_out: false };
|
|
6335
|
+
} catch (error) {
|
|
6336
|
+
if (!is_pr_checks_timeout(error)) throw error;
|
|
6337
|
+
await fail_when_no_checks(branch_name);
|
|
6338
|
+
console.info(WATCH_TIMED_OUT_NOTE);
|
|
6339
|
+
return { timed_out: true };
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6342
|
+
var git_pr_checks_watch = { pr_checks_watch };
|
|
6343
|
+
|
|
6344
|
+
// scripts/gh/git-gh-command.ts
|
|
6345
|
+
var git_gh_command = {
|
|
6346
|
+
...git_gh_pr,
|
|
6347
|
+
...git_gh_pr_auto_merge,
|
|
6348
|
+
pr_checks_watch: git_pr_checks_watch.pr_checks_watch,
|
|
6349
|
+
...git_gh_issue,
|
|
6350
|
+
...git_gh_repo
|
|
6351
|
+
};
|
|
6352
|
+
|
|
4582
6353
|
export {
|
|
6354
|
+
bounded_pool,
|
|
4583
6355
|
shell_segments,
|
|
4584
6356
|
bash_triggers,
|
|
4585
6357
|
issue_cite,
|
|
6358
|
+
PORCELAIN_FLAG,
|
|
6359
|
+
git_spawn_sync,
|
|
4586
6360
|
git_command,
|
|
4587
6361
|
git_common_directory,
|
|
6362
|
+
error_text,
|
|
4588
6363
|
tail_commands,
|
|
4589
6364
|
filing_cap,
|
|
4590
6365
|
CONTEXT_CUT_THRESHOLD,
|
|
4591
6366
|
RECENT_REQUEST_WINDOW,
|
|
4592
6367
|
agent_session_environment,
|
|
4593
6368
|
agent_role_profile,
|
|
6369
|
+
cli_flags,
|
|
4594
6370
|
backlog_budget,
|
|
4595
6371
|
run_hold,
|
|
4596
6372
|
run_cut,
|
|
4597
6373
|
run_tail,
|
|
4598
6374
|
lane_background,
|
|
4599
|
-
ENHANCEMENT_LABEL,
|
|
4600
|
-
BREAKING_CHANGE_LABEL,
|
|
4601
6375
|
IN_PROGRESS_LABEL,
|
|
4602
6376
|
NEEDS_DECISION_LABEL,
|
|
4603
6377
|
RUN_SOLO_LABEL,
|
|
4604
6378
|
RUN_LANE_LABEL,
|
|
4605
6379
|
NEEDS_HUMAN_REVIEW_LABEL,
|
|
4606
6380
|
ALREADY_DONE_LABEL,
|
|
4607
|
-
BUGFIX_LABEL,
|
|
4608
|
-
RELEASE_CLASSIFICATION_NAMES,
|
|
4609
6381
|
has_any_label,
|
|
4610
6382
|
has_label_name,
|
|
4611
6383
|
process_identity,
|
|
@@ -4620,14 +6392,13 @@ export {
|
|
|
4620
6392
|
process_owner_schema,
|
|
4621
6393
|
lane_registry,
|
|
4622
6394
|
run_headless,
|
|
4623
|
-
FULL_PAGE_QUERY,
|
|
4624
6395
|
git_gh_api_path,
|
|
4625
|
-
error_text,
|
|
4626
6396
|
FETCH_TIMEOUT_MS,
|
|
4627
|
-
parse_json_array_or_undefined,
|
|
4628
6397
|
read_json_listing,
|
|
4629
6398
|
parse_json_object_safe,
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
6399
|
+
git_gh_exec,
|
|
6400
|
+
open_issue_schema,
|
|
6401
|
+
git_gh_issue_read,
|
|
6402
|
+
git_gh_pr_read,
|
|
6403
|
+
git_gh_command
|
|
4633
6404
|
};
|