@on-par/factory-core 0.9.7-next.9 → 0.9.8-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blockers/dependencies.d.ts +19 -0
- package/dist/blockers/dependencies.d.ts.map +1 -0
- package/dist/blockers/dependencies.js +49 -0
- package/dist/blockers/dependencies.js.map +1 -0
- package/dist/blockers/file-blocker.d.ts +44 -0
- package/dist/blockers/file-blocker.d.ts.map +1 -0
- package/dist/blockers/file-blocker.js +203 -0
- package/dist/blockers/file-blocker.js.map +1 -0
- package/dist/blockers/index.d.ts +7 -0
- package/dist/blockers/index.d.ts.map +1 -0
- package/dist/blockers/index.js +5 -0
- package/dist/blockers/index.js.map +1 -0
- package/dist/blockers/poller.d.ts +37 -0
- package/dist/blockers/poller.d.ts.map +1 -0
- package/dist/blockers/poller.js +111 -0
- package/dist/blockers/poller.js.map +1 -0
- package/dist/checkers/index.d.ts +10 -1
- package/dist/checkers/index.d.ts.map +1 -1
- package/dist/checkers/index.js +14 -22
- package/dist/checkers/index.js.map +1 -1
- package/dist/checkers/probe.d.ts.map +1 -1
- package/dist/checkers/probe.js +5 -3
- package/dist/checkers/probe.js.map +1 -1
- package/dist/config/example.d.ts.map +1 -1
- package/dist/config/example.js +18 -0
- package/dist/config/example.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/repo-build-pin.d.ts +4 -0
- package/dist/config/repo-build-pin.d.ts.map +1 -0
- package/dist/config/repo-build-pin.js +13 -0
- package/dist/config/repo-build-pin.js.map +1 -0
- package/dist/config/repo-harnesses.d.ts +43 -0
- package/dist/config/repo-harnesses.d.ts.map +1 -0
- package/dist/config/repo-harnesses.js +20 -0
- package/dist/config/repo-harnesses.js.map +1 -0
- package/dist/config/repo.d.ts +44 -11
- package/dist/config/repo.d.ts.map +1 -1
- package/dist/config/repo.js +35 -31
- package/dist/config/repo.js.map +1 -1
- package/dist/daemon/execution-config.d.ts +34 -0
- package/dist/daemon/execution-config.d.ts.map +1 -1
- package/dist/daemon/run-runtime.d.ts +34 -0
- package/dist/daemon/run-runtime.d.ts.map +1 -1
- package/dist/daemon/ship-executor.js +2 -3
- package/dist/daemon/ship-executor.js.map +1 -1
- package/dist/eval/baseline.d.ts.map +1 -1
- package/dist/eval/baseline.js +4 -4
- package/dist/eval/baseline.js.map +1 -1
- package/dist/eval/judge.d.ts.map +1 -1
- package/dist/eval/judge.js +9 -7
- package/dist/eval/judge.js.map +1 -1
- package/dist/eval/regression-report.js +1 -1
- package/dist/eval/regression-report.js.map +1 -1
- package/dist/eval/score.d.ts.map +1 -1
- package/dist/eval/score.js +2 -1
- package/dist/eval/score.js.map +1 -1
- package/dist/events/kinds.d.ts +1 -1
- package/dist/events/kinds.d.ts.map +1 -1
- package/dist/events/kinds.js +3 -0
- package/dist/events/kinds.js.map +1 -1
- package/dist/harness/catalog.d.ts +2 -0
- package/dist/harness/catalog.d.ts.map +1 -1
- package/dist/harness/catalog.js +5 -1
- package/dist/harness/catalog.js.map +1 -1
- package/dist/harness/contract.js +4 -4
- package/dist/harness/contract.js.map +1 -1
- package/dist/harness/index.d.ts +1 -1
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +1 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/opencode-version.d.ts +7 -0
- package/dist/harness/opencode-version.d.ts.map +1 -0
- package/dist/harness/opencode-version.js +20 -0
- package/dist/harness/opencode-version.js.map +1 -0
- package/dist/harness/opencode.d.ts +10 -2
- package/dist/harness/opencode.d.ts.map +1 -1
- package/dist/harness/opencode.js +26 -5
- package/dist/harness/opencode.js.map +1 -1
- package/dist/hosted/store-contract.js +1 -1
- package/dist/hosted/store-contract.js.map +1 -1
- package/dist/internal.d.ts +4 -4
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -2
- package/dist/internal.js.map +1 -1
- package/dist/kpis/human.d.ts.map +1 -1
- package/dist/kpis/human.js +4 -8
- package/dist/kpis/human.js.map +1 -1
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +7 -1
- package/dist/models/index.js.map +1 -1
- package/dist/phases/build.d.ts +6 -2
- package/dist/phases/build.d.ts.map +1 -1
- package/dist/phases/build.js +43 -12
- package/dist/phases/build.js.map +1 -1
- package/dist/phases/check.d.ts +32 -2
- package/dist/phases/check.d.ts.map +1 -1
- package/dist/phases/check.js +5 -6
- package/dist/phases/check.js.map +1 -1
- package/dist/phases/plan-route.d.ts +16 -0
- package/dist/phases/plan-route.d.ts.map +1 -0
- package/dist/phases/plan-route.js +38 -0
- package/dist/phases/plan-route.js.map +1 -0
- package/dist/phases/plan-routes.d.ts +16 -1
- package/dist/phases/plan-routes.d.ts.map +1 -1
- package/dist/phases/plan-routes.js +40 -1
- package/dist/phases/plan-routes.js.map +1 -1
- package/dist/phases/plan.d.ts +4 -3
- package/dist/phases/plan.d.ts.map +1 -1
- package/dist/phases/plan.js +15 -30
- package/dist/phases/plan.js.map +1 -1
- package/dist/phases/ship-git.d.ts +52 -0
- package/dist/phases/ship-git.d.ts.map +1 -0
- package/dist/phases/ship-git.js +104 -0
- package/dist/phases/ship-git.js.map +1 -0
- package/dist/phases/ship.d.ts +8 -6
- package/dist/phases/ship.d.ts.map +1 -1
- package/dist/phases/ship.js +71 -121
- package/dist/phases/ship.js.map +1 -1
- package/dist/queue/queue-backend.d.ts +6 -1
- package/dist/queue/queue-backend.d.ts.map +1 -1
- package/dist/queue/queue-backend.js +7 -1
- package/dist/queue/queue-backend.js.map +1 -1
- package/dist/queue/unpark.d.ts +28 -0
- package/dist/queue/unpark.d.ts.map +1 -0
- package/dist/queue/unpark.js +69 -0
- package/dist/queue/unpark.js.map +1 -0
- package/dist/readiness/slice-plan.js +1 -1
- package/dist/readiness/slice-plan.js.map +1 -1
- package/dist/review/backtest.js +2 -2
- package/dist/review/backtest.js.map +1 -1
- package/dist/review/containment.js +1 -1
- package/dist/review/containment.js.map +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -13
- package/dist/router/index.js.map +1 -1
- package/dist/router/requires.d.ts +4 -0
- package/dist/router/requires.d.ts.map +1 -0
- package/dist/router/requires.js +22 -0
- package/dist/router/requires.js.map +1 -0
- package/dist/run/ci-rework-loop.d.ts +65 -0
- package/dist/run/ci-rework-loop.d.ts.map +1 -0
- package/dist/run/ci-rework-loop.js +169 -0
- package/dist/run/ci-rework-loop.js.map +1 -0
- package/dist/run/ci-rework.d.ts +25 -0
- package/dist/run/ci-rework.d.ts.map +1 -0
- package/dist/run/ci-rework.js +113 -0
- package/dist/run/ci-rework.js.map +1 -0
- package/dist/run/ci-suppression-guard.d.ts +11 -0
- package/dist/run/ci-suppression-guard.d.ts.map +1 -0
- package/dist/run/ci-suppression-guard.js +143 -0
- package/dist/run/ci-suppression-guard.js.map +1 -0
- package/dist/run/outcome.d.ts +9 -2
- package/dist/run/outcome.d.ts.map +1 -1
- package/dist/run/outcome.js +5 -0
- package/dist/run/outcome.js.map +1 -1
- package/dist/run/park-comment.d.ts +73 -0
- package/dist/run/park-comment.d.ts.map +1 -0
- package/dist/run/park-comment.js +211 -0
- package/dist/run/park-comment.js.map +1 -0
- package/dist/run/phase-snapshot.js +2 -2
- package/dist/run/phase-snapshot.js.map +1 -1
- package/dist/run/resume-context.d.ts +3 -2
- package/dist/run/resume-context.d.ts.map +1 -1
- package/dist/run/resume-context.js.map +1 -1
- package/dist/run/run-issue.d.ts +2 -1
- package/dist/run/run-issue.d.ts.map +1 -1
- package/dist/run/run-issue.js +50 -52
- package/dist/run/run-issue.js.map +1 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sim/pipeline.d.ts +2 -1
- package/dist/sim/pipeline.d.ts.map +1 -1
- package/dist/sim/pipeline.js.map +1 -1
- package/dist/spec/index.d.ts +3 -2
- package/dist/spec/index.d.ts.map +1 -1
- package/dist/spec/index.js +1 -1
- package/dist/spec/index.js.map +1 -1
- package/dist/steward/detect.d.ts +2 -1
- package/dist/steward/detect.d.ts.map +1 -1
- package/dist/steward/detect.js +3 -2
- package/dist/steward/detect.js.map +1 -1
- package/dist/types/index.d.ts +34 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/usage/index.js +1 -1
- package/dist/usage/index.js.map +1 -1
- package/dist/utils/command-runner.js +2 -2
- package/dist/utils/command-runner.js.map +1 -1
- package/dist/utils/worktree-gc.d.ts.map +1 -1
- package/dist/utils/worktree-gc.js +5 -5
- package/dist/utils/worktree-gc.js.map +1 -1
- package/dist/utils/worktree-location.d.ts +2 -2
- package/dist/utils/worktree-location.d.ts.map +1 -1
- package/dist/utils/worktree-location.js +16 -9
- package/dist/utils/worktree-location.js.map +1 -1
- package/dist/utils/worktree-name.d.ts +6 -0
- package/dist/utils/worktree-name.d.ts.map +1 -1
- package/dist/utils/worktree-name.js +16 -1
- package/dist/utils/worktree-name.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// packages/core/src/run/ci-rework.ts — attribution, budget and feedback for SHIP CI rework (#2816)
|
|
2
|
+
import { isPassingConclusion, listAllCheckRuns } from '../utils/ci-watch.js';
|
|
3
|
+
export const MAX_CI_REWORK_ROUNDS = 2;
|
|
4
|
+
export const CI_REWORK_ENV = 'FACTORY_MAX_CI_REWORK_ROUNDS';
|
|
5
|
+
const LIST_FILES_PAGE_SIZE = 100;
|
|
6
|
+
const LIST_FILES_MAX_PAGES = 30;
|
|
7
|
+
export const CI_REWORK_INSTRUCTIONS = 'CI (CodeQL) flagged the alerts above on files this PR changed. Fix the root cause in the code at the listed file:line. ' +
|
|
8
|
+
'Suppression is forbidden: do not add `// codeql[...]`, `lgtm`, `eslint-disable` or `@ts-ignore` comments, do not exclude queries or paths in any config, and do not dismiss alerts. ' +
|
|
9
|
+
'Commit the fix.';
|
|
10
|
+
/** Non-negative integer from FACTORY_MAX_CI_REWORK_ROUNDS (0 disables CI rework); anything else falls back to the default. */
|
|
11
|
+
export function resolveMaxCiReworkRounds(env = process.env) {
|
|
12
|
+
const value = (env[CI_REWORK_ENV] ?? '').trim();
|
|
13
|
+
return /^\d+$/.test(value) ? Number(value) : MAX_CI_REWORK_ROUNDS;
|
|
14
|
+
}
|
|
15
|
+
function alertPath(location) {
|
|
16
|
+
const trimmed = location.trim();
|
|
17
|
+
if (trimmed === '' || trimmed === '(unknown location)')
|
|
18
|
+
return undefined;
|
|
19
|
+
const path = trimmed.replace(/:\d+$/, '').replace(/^\.\//, '');
|
|
20
|
+
return path === '' ? undefined : path;
|
|
21
|
+
}
|
|
22
|
+
/** Alerts whose file is among the PR's changed files; the rest are pre-existing or baseline. */
|
|
23
|
+
export function attributableCodeqlAlerts(evidence, changedFiles) {
|
|
24
|
+
if (!evidence)
|
|
25
|
+
return [];
|
|
26
|
+
const changed = new Set(changedFiles);
|
|
27
|
+
return evidence.alerts.filter((alert) => {
|
|
28
|
+
const path = alertPath(alert.location);
|
|
29
|
+
return path !== undefined && changed.has(path);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** True when a failing check is attributed on its own (not through CodeQL alerts). */
|
|
33
|
+
export function needsBaseChecks(evidence) {
|
|
34
|
+
return !!evidence && evidence.failingChecks.some((c) => !(/codeql/i.test(c.name) && evidence.alerts.length > 0));
|
|
35
|
+
}
|
|
36
|
+
/** Attributes each failing item to the PR: alerts by changed file, other checks by the base commit (#2817). */
|
|
37
|
+
export function attributeCiFailure(evidence, changedFiles, base) {
|
|
38
|
+
if (!evidence)
|
|
39
|
+
return { baseline: false, items: [] };
|
|
40
|
+
const changed = new Set(changedFiles);
|
|
41
|
+
const items = [];
|
|
42
|
+
for (const alert of evidence.alerts) {
|
|
43
|
+
const path = alertPath(alert.location);
|
|
44
|
+
const attributable = path !== undefined && changed.has(path);
|
|
45
|
+
items.push({
|
|
46
|
+
kind: 'codeql-alert',
|
|
47
|
+
ref: `${alert.ruleId} at ${alert.location}`,
|
|
48
|
+
attributable,
|
|
49
|
+
reason: attributable ? 'file changed by this PR' : 'file not changed by this PR',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const baseLabel = base ? `${base.baseRef}@${base.baseSha.slice(0, 12)}` : '';
|
|
53
|
+
for (const check of evidence.failingChecks) {
|
|
54
|
+
if (/codeql/i.test(check.name) && evidence.alerts.length > 0)
|
|
55
|
+
continue;
|
|
56
|
+
if (!base) {
|
|
57
|
+
items.push({ kind: 'check', ref: check.name, attributable: true, reason: 'base checks unavailable' });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const onBase = base.failing.includes(check.name);
|
|
61
|
+
items.push({
|
|
62
|
+
kind: 'check',
|
|
63
|
+
ref: check.name,
|
|
64
|
+
attributable: !onBase,
|
|
65
|
+
reason: onBase ? `also failing on base ${baseLabel}` : `not failing on base ${baseLabel}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const baseline = items.length > 0 && items.every((i) => !i.attributable) && !evidence.errors?.length;
|
|
69
|
+
return { baseline, ...(base ? { baseRef: base.baseRef, baseSha: base.baseSha } : {}), items };
|
|
70
|
+
}
|
|
71
|
+
/** Failing check names on the latest commit of the PR's base branch. Errors propagate to the caller. */
|
|
72
|
+
export async function listBaseFailingChecks(octokit, owner, repo, prNumber) {
|
|
73
|
+
const { data: pr } = await octokit.rest.pulls.get({ owner, repo, pull_number: prNumber });
|
|
74
|
+
const baseRef = pr.base.ref;
|
|
75
|
+
const { data: branch } = await octokit.rest.repos.getBranch({ owner, repo, branch: baseRef });
|
|
76
|
+
const baseSha = branch.commit.sha;
|
|
77
|
+
const runs = await listAllCheckRuns(octokit, owner, repo, baseSha, 100, 10);
|
|
78
|
+
const failing = new Set();
|
|
79
|
+
for (const run of runs) {
|
|
80
|
+
if (run.name && run.status === 'completed' && !isPassingConclusion(run.conclusion))
|
|
81
|
+
failing.add(run.name);
|
|
82
|
+
}
|
|
83
|
+
return { baseRef, baseSha, failing: [...failing] };
|
|
84
|
+
}
|
|
85
|
+
/** The same CheckSummary shape local CHECK rework consumes, with one FAIL `ci_codeql` result. */
|
|
86
|
+
export function ciReworkSummary(alerts) {
|
|
87
|
+
const lines = alerts.map((a) => `- ${a.ruleId} at ${a.location} — ${a.message}`);
|
|
88
|
+
const details = ['CodeQL alerts on files this PR changed:', ...lines].join('\n');
|
|
89
|
+
return {
|
|
90
|
+
failures: 1,
|
|
91
|
+
passes: 0,
|
|
92
|
+
skips: 0,
|
|
93
|
+
total: 1,
|
|
94
|
+
results: [{ checker: 'ci_codeql', result: 'FAIL', details }],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export async function listPrChangedFiles(octokit, owner, repo, prNumber) {
|
|
98
|
+
const files = [];
|
|
99
|
+
for (let page = 1; page <= LIST_FILES_MAX_PAGES; page++) {
|
|
100
|
+
const { data } = await octokit.rest.pulls.listFiles({
|
|
101
|
+
owner,
|
|
102
|
+
repo,
|
|
103
|
+
pull_number: prNumber,
|
|
104
|
+
per_page: LIST_FILES_PAGE_SIZE,
|
|
105
|
+
page,
|
|
106
|
+
});
|
|
107
|
+
files.push(...data.map((f) => f.filename));
|
|
108
|
+
if (data.length < LIST_FILES_PAGE_SIZE)
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
return files;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=ci-rework.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-rework.js","sourceRoot":"","sources":["../../src/run/ci-rework.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CiSuppressionFinding } from '../types/index.js';
|
|
2
|
+
export declare const SUPPRESSION_ATTEMPT = "suppression-attempt";
|
|
3
|
+
/** Scans a unified git diff for suppression comments (added lines) and protected paths (any file header). */
|
|
4
|
+
export declare function scanReworkDiff(diff: string): CiSuppressionFinding[];
|
|
5
|
+
/** Reads the worktree's current HEAD SHA. */
|
|
6
|
+
export declare function readWorktreeHead(worktree: string): Promise<string>;
|
|
7
|
+
/** Reads the committed diff from `fromSha` to HEAD (SHIP re-entry pushes HEAD, not the working tree). */
|
|
8
|
+
export declare function readReworkDiff(worktree: string, fromSha: string): Promise<string>;
|
|
9
|
+
/** One-line summary of guard findings for park messages and logs. */
|
|
10
|
+
export declare function describeSuppression(findings: readonly CiSuppressionFinding[]): string;
|
|
11
|
+
//# sourceMappingURL=ci-suppression-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-suppression-guard.d.ts","sourceRoot":"","sources":["../../src/run/ci-suppression-guard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AA2CzD,6GAA6G;AAC7G,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAsEnE;AAED,6CAA6C;AAC7C,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKxE;AAED,yGAAyG;AACzG,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKvF;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,oBAAoB,EAAE,GAAG,MAAM,CAOrF"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// packages/core/src/run/ci-suppression-guard.ts — mechanical suppression guard for SHIP CI rework diffs (#2818)
|
|
2
|
+
import { execGit } from '../utils/git-exec.js';
|
|
3
|
+
export const SUPPRESSION_ATTEMPT = 'suppression-attempt';
|
|
4
|
+
const MAX_MATCH = 200;
|
|
5
|
+
const MAX_LISTED = 5;
|
|
6
|
+
/** True when an eslint-disable directive in `line` is blanket or names a security rule. */
|
|
7
|
+
function eslintDisablesSecurity(line) {
|
|
8
|
+
const m = /eslint-disable(?:-next-line|-line)?(?![\w-])([^\n]*)/.exec(line);
|
|
9
|
+
if (!m)
|
|
10
|
+
return false;
|
|
11
|
+
let list = m[1];
|
|
12
|
+
const dash = list.indexOf('--');
|
|
13
|
+
if (dash >= 0)
|
|
14
|
+
list = list.slice(0, dash);
|
|
15
|
+
const end = list.indexOf('*/');
|
|
16
|
+
if (end >= 0)
|
|
17
|
+
list = list.slice(0, end);
|
|
18
|
+
const rules = list.split(/[,\s]+/).filter(Boolean);
|
|
19
|
+
return rules.length === 0 || rules.some((r) => /security/i.test(r));
|
|
20
|
+
}
|
|
21
|
+
/** Added-line rules; the single owner of the known suppression comment forms. */
|
|
22
|
+
const SUPPRESSION_COMMENT_RULES = [
|
|
23
|
+
{ rule: 'codeql-comment', test: (l) => /\bcodeql\[/i.test(l) },
|
|
24
|
+
{ rule: 'lgtm-comment', test: (l) => /(?:\/\/|#|\/\*|\*|<!--|--).*\blgtm\b/i.test(l) },
|
|
25
|
+
{ rule: 'nosec-comment', test: (l) => /#\s*nosec\b/i.test(l) || /\/\/\s*#?nosec\b/i.test(l) },
|
|
26
|
+
{ rule: 'eslint-disable-security', test: eslintDisablesSecurity },
|
|
27
|
+
];
|
|
28
|
+
function pathRules(path) {
|
|
29
|
+
const segments = path.split('/');
|
|
30
|
+
const base = segments[segments.length - 1];
|
|
31
|
+
const rules = [];
|
|
32
|
+
if (path.startsWith('.github/workflows/'))
|
|
33
|
+
rules.push('workflow-path');
|
|
34
|
+
if (segments.slice(0, -1).some((s) => /^\.?codeql$/i.test(s)) || /codeql.*\.(ya?ml|json)$/i.test(base)) {
|
|
35
|
+
rules.push('codeql-path');
|
|
36
|
+
}
|
|
37
|
+
if (/\.sarif(\.json)?$/i.test(base))
|
|
38
|
+
rules.push('sarif-path');
|
|
39
|
+
return rules;
|
|
40
|
+
}
|
|
41
|
+
/** Scans a unified git diff for suppression comments (added lines) and protected paths (any file header). */
|
|
42
|
+
export function scanReworkDiff(diff) {
|
|
43
|
+
const findings = [];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
const add = (f) => {
|
|
46
|
+
const key = `${f.kind}|${f.rule}|${f.file}|${f.line ?? ''}|${f.kind === 'path' ? f.match : ''}`;
|
|
47
|
+
if (seen.has(key))
|
|
48
|
+
return;
|
|
49
|
+
seen.add(key);
|
|
50
|
+
findings.push(f);
|
|
51
|
+
};
|
|
52
|
+
const checkPath = (path) => {
|
|
53
|
+
if (!path || path === '/dev/null')
|
|
54
|
+
return;
|
|
55
|
+
for (const rule of pathRules(path))
|
|
56
|
+
add({ kind: 'path', rule, file: path, match: path });
|
|
57
|
+
};
|
|
58
|
+
let file = '(unknown)';
|
|
59
|
+
let line;
|
|
60
|
+
let inHunk = false;
|
|
61
|
+
for (const raw of diff.split('\n')) {
|
|
62
|
+
const header = /^diff --git a\/(.+) b\/(.+)$/.exec(raw);
|
|
63
|
+
if (header) {
|
|
64
|
+
inHunk = false;
|
|
65
|
+
line = undefined;
|
|
66
|
+
file = header[2];
|
|
67
|
+
checkPath(header[1]);
|
|
68
|
+
checkPath(header[2]);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!inHunk) {
|
|
72
|
+
const rename = /^rename (?:from|to) (.+)$/.exec(raw);
|
|
73
|
+
if (rename) {
|
|
74
|
+
checkPath(rename[1]);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const oldPath = /^--- (?:a\/)?(.+)$/.exec(raw);
|
|
78
|
+
if (oldPath) {
|
|
79
|
+
checkPath(oldPath[1]);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const newPath = /^\+\+\+ (?:b\/)?(.+)$/.exec(raw);
|
|
83
|
+
if (newPath) {
|
|
84
|
+
if (newPath[1] !== '/dev/null')
|
|
85
|
+
file = newPath[1];
|
|
86
|
+
checkPath(newPath[1]);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(raw);
|
|
91
|
+
if (hunk) {
|
|
92
|
+
inHunk = true;
|
|
93
|
+
line = Number(hunk[1]);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (raw.startsWith('+')) {
|
|
97
|
+
const text = raw.slice(1);
|
|
98
|
+
for (const { rule, test } of SUPPRESSION_COMMENT_RULES) {
|
|
99
|
+
if (test(text)) {
|
|
100
|
+
add({
|
|
101
|
+
kind: 'comment',
|
|
102
|
+
rule,
|
|
103
|
+
file,
|
|
104
|
+
...(line !== undefined ? { line } : {}),
|
|
105
|
+
match: text.trim().slice(0, MAX_MATCH),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (line !== undefined)
|
|
110
|
+
line++;
|
|
111
|
+
}
|
|
112
|
+
else if (raw.startsWith(' ') && line !== undefined) {
|
|
113
|
+
line++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return findings;
|
|
117
|
+
}
|
|
118
|
+
/** Reads the worktree's current HEAD SHA. */
|
|
119
|
+
export async function readWorktreeHead(worktree) {
|
|
120
|
+
const { stdout } = await execGit(['rev-parse', 'HEAD'], { cwd: worktree });
|
|
121
|
+
const sha = stdout.trim();
|
|
122
|
+
if (!sha)
|
|
123
|
+
throw new Error('git rev-parse HEAD returned nothing');
|
|
124
|
+
return sha;
|
|
125
|
+
}
|
|
126
|
+
/** Reads the committed diff from `fromSha` to HEAD (SHIP re-entry pushes HEAD, not the working tree). */
|
|
127
|
+
export async function readReworkDiff(worktree, fromSha) {
|
|
128
|
+
const { stdout } = await execGit(['diff', '--no-color', '--no-ext-diff', '--unified=0', fromSha, 'HEAD'], {
|
|
129
|
+
cwd: worktree,
|
|
130
|
+
});
|
|
131
|
+
return stdout;
|
|
132
|
+
}
|
|
133
|
+
/** One-line summary of guard findings for park messages and logs. */
|
|
134
|
+
export function describeSuppression(findings) {
|
|
135
|
+
const items = findings.slice(0, MAX_LISTED).map((f) => {
|
|
136
|
+
if (f.kind === 'comment')
|
|
137
|
+
return `${f.rule} at ${f.file}${f.line !== undefined ? `:${f.line}` : ''}`;
|
|
138
|
+
return `${f.rule} ${f.match}`;
|
|
139
|
+
});
|
|
140
|
+
const more = findings.length > MAX_LISTED ? `, +${findings.length - MAX_LISTED} more` : '';
|
|
141
|
+
return `${SUPPRESSION_ATTEMPT}: ${findings.length} finding(s): ${items.join(', ')}${more}`;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=ci-suppression-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ci-suppression-guard.js","sourceRoot":"","sources":["../../src/run/ci-suppression-guard.ts"],"names":[],"mappings":"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"}
|
package/dist/run/outcome.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { EventKind } from '../events/kinds.js';
|
|
2
|
-
export type ParkReason = Extract<EventKind, 'escalate' | 'timeout' | 'fail' | 'conflict' | 'ci-failed' | 'ci-infra' | 'held' | 'config'>;
|
|
3
|
-
|
|
2
|
+
export type ParkReason = Extract<EventKind, 'escalate' | 'timeout' | 'fail' | 'conflict' | 'ci-failed' | 'ci-infra' | 'held' | 'config' | 'blocked'>;
|
|
3
|
+
import type { BuildRoutePin } from '../config/repo.js';
|
|
4
|
+
export type BuildRoute = BuildRoutePin;
|
|
5
|
+
/** CHECK/CI rework reruns on the route BUILD completed on, `command` included (#2873):
|
|
6
|
+
* reworkWorker keeps it when its model can run and reroutes otherwise. */
|
|
7
|
+
export declare function reworkRouteFor(route: BuildRoute, model: string | undefined): {
|
|
8
|
+
reworkRoute?: BuildRoutePin;
|
|
9
|
+
reworkModel?: string;
|
|
10
|
+
};
|
|
4
11
|
/** A run's terminal outcome. `parked` is produced by the CLI's LaneParkError and `released`
|
|
5
12
|
* by an environment-caused CHECK failure (#1928, not a park); `shipped` / `ready` /
|
|
6
13
|
* `escalated` are defined here for later stories (#4, #5) that re-point the CLI
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/run/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,OAAO,CAC9B,SAAS,EACT,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/run/outcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,OAAO,CAC9B,SAAS,EACT,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CACxG,CAAC;AAEF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC;2EAC2E;AAC3E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB;IAAE,WAAW,CAAC,EAAE,aAAa,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAEvD;AAED;;;8CAG8C;AAC9C,MAAM,MAAM,UAAU,GAClB;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/F;IACE,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C,GACD;IACE,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yHAAyH;IACzH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6FAA6F;IAC7F,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,GACD;IACE,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GACD;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvG;;iFAEiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAOtD;AAED;;;+EAG+E;AAC/E,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,CAQ3E"}
|
package/dist/run/outcome.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/** CHECK/CI rework reruns on the route BUILD completed on, `command` included (#2873):
|
|
2
|
+
* reworkWorker keeps it when its model can run and reroutes otherwise. */
|
|
3
|
+
export function reworkRouteFor(route, model) {
|
|
4
|
+
return { reworkRoute: route, reworkModel: model };
|
|
5
|
+
}
|
|
1
6
|
/** Core classifies structurally — via a parked RunOutcome on `err.outcome` or a
|
|
2
7
|
* `parkReason` marker property — rather than `instanceof`, because core cannot import
|
|
3
8
|
* the CLI's error classes (LaneParkError, LandConflictError, CiFailedError). */
|
package/dist/run/outcome.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/run/outcome.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outcome.js","sourceRoot":"","sources":["../../src/run/outcome.ts"],"names":[],"mappings":"AAWA;2EAC2E;AAC3E,MAAM,UAAU,cAAc,CAC5B,KAAiB,EACjB,KAAyB;IAEzB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AACpD,CAAC;AA4CD;;iFAEiF;AACjF,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,OAAO,GAAI,GAAmD,EAAE,OAAO,CAAC;IAC9E,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC;IACjE,MAAM,MAAM,GAAI,GAAsD,EAAE,UAAU,CAAC;IACnF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,IAAK,GAA+C,EAAE,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC7F,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;+EAG+E;AAC/E,MAAM,UAAU,UAAU,CAAC,GAAY;IACrC,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAuC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,wDAAwD,GAAG,EAAE,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Octokit } from '@octokit/rest';
|
|
2
|
+
import type { ParkReason } from './outcome.js';
|
|
3
|
+
export declare const ISSUE_STATE_MARKER_VERSION = "v1";
|
|
4
|
+
export interface ParkCommentInput {
|
|
5
|
+
issue: number;
|
|
6
|
+
reason: ParkReason;
|
|
7
|
+
message: string;
|
|
8
|
+
runId: string;
|
|
9
|
+
branch: string;
|
|
10
|
+
lane?: string;
|
|
11
|
+
parkSha?: string;
|
|
12
|
+
blockers?: readonly number[];
|
|
13
|
+
autoUnparks?: number;
|
|
14
|
+
status?: string;
|
|
15
|
+
}
|
|
16
|
+
/** `reason` is a plain string because the parser reads untrusted text. */
|
|
17
|
+
export interface ParkState {
|
|
18
|
+
reason: string;
|
|
19
|
+
lane?: string;
|
|
20
|
+
runId?: string;
|
|
21
|
+
branch?: string;
|
|
22
|
+
parkSha?: string;
|
|
23
|
+
blockers: number[];
|
|
24
|
+
/** Times the blocked-park poller re-queued this issue; 0 when the line is absent or malformed. */
|
|
25
|
+
autoUnparks: number;
|
|
26
|
+
status?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ParkStateComment {
|
|
29
|
+
id: number;
|
|
30
|
+
body: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function issueStateMarker(issue: number): string;
|
|
33
|
+
export declare function parkUnblockHint(reason: ParkReason, issue: number): string;
|
|
34
|
+
export declare function renderParkComment(input: ParkCommentInput): string;
|
|
35
|
+
export interface ParkCommentPatch {
|
|
36
|
+
reason?: ParkReason;
|
|
37
|
+
status?: string;
|
|
38
|
+
autoUnparks?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Rewrites the state block (and the unblock hint on a reason change) in place; null when there is no state block. */
|
|
41
|
+
export declare function patchParkComment(body: string, issue: number, patch: ParkCommentPatch): string | null;
|
|
42
|
+
/** Lowest-id marked comment decides; null when none, or when its state block is unreadable. */
|
|
43
|
+
export declare function readParkState(comments: readonly ParkStateComment[], issue?: number): ParkState | null;
|
|
44
|
+
/** The lowest-id marker comment written by the authenticated user, or undefined. */
|
|
45
|
+
export declare function findOwnParkComment(deps: {
|
|
46
|
+
octokit: Octokit;
|
|
47
|
+
repo: string;
|
|
48
|
+
issue: number;
|
|
49
|
+
}): Promise<ParkStateComment | undefined>;
|
|
50
|
+
export declare function upsertParkComment(deps: {
|
|
51
|
+
octokit: Octokit;
|
|
52
|
+
repo: string;
|
|
53
|
+
input: ParkCommentInput;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
commentId: number;
|
|
56
|
+
created: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
/** Never throws: a GitHub or git failure comes back as `{ ok: false, error }`. */
|
|
59
|
+
export declare function publishParkComment(deps: {
|
|
60
|
+
octokit: Octokit;
|
|
61
|
+
repo: string;
|
|
62
|
+
worktree?: string;
|
|
63
|
+
input: Omit<ParkCommentInput, 'parkSha'>;
|
|
64
|
+
readHead?: (worktree: string) => Promise<string>;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
ok: true;
|
|
67
|
+
commentId: number;
|
|
68
|
+
created: boolean;
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
error: string;
|
|
72
|
+
}>;
|
|
73
|
+
//# sourceMappingURL=park-comment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"park-comment.d.ts","sourceRoot":"","sources":["../../src/run/park-comment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// src/run/park-comment.ts — upsert one issue comment with the park state behind a hidden factory-issue-state marker (#2314, ADR-0130)
|
|
2
|
+
//
|
|
3
|
+
// Every runIssue park edits one comment per issue in place (#728). Anyone can post the marker, so only a
|
|
4
|
+
// marker comment written by the authenticated identity is trusted (same rule as readiness/slice-plan-github.ts).
|
|
5
|
+
import { fenceExcerpt, stripHiddenContent } from '../filing/sanitize.js';
|
|
6
|
+
import { readWorktreeHead } from './ci-suppression-guard.js';
|
|
7
|
+
export const ISSUE_STATE_MARKER_VERSION = 'v1';
|
|
8
|
+
const PAGE_SIZE = 100;
|
|
9
|
+
const STATE_HEADING = '**Park state**';
|
|
10
|
+
const MARKER_RE = new RegExp(`<!-- factory-issue-state ${ISSUE_STATE_MARKER_VERSION} issue:(\\d+) -->`);
|
|
11
|
+
export function issueStateMarker(issue) {
|
|
12
|
+
return `<!-- factory-issue-state ${ISSUE_STATE_MARKER_VERSION} issue:${issue} -->`;
|
|
13
|
+
}
|
|
14
|
+
const PARK_HINTS = {
|
|
15
|
+
fail: 'fix the failing checks on the branch or clarify the issue, then run `factory unpark <n>`.',
|
|
16
|
+
blocked: 'waiting on a filed blocker. The factory resumes it once the blocker closes; run `factory unpark <n>` to retry sooner.',
|
|
17
|
+
escalate: 'a human decision is needed. Answer it on the issue, then run `factory unpark <n>`.',
|
|
18
|
+
timeout: 'a phase timed out. Check the lane log and retry with `factory unpark <n>`.',
|
|
19
|
+
conflict: 'rebase the branch onto main, then run `factory unpark <n>`.',
|
|
20
|
+
'ci-failed': 'fix CI on the PR, then run `factory unpark <n>`.',
|
|
21
|
+
'ci-infra': 'CI infrastructure failed. Re-run CI, then run `factory unpark <n>`.',
|
|
22
|
+
held: 'the same failure parked a prior run. A human must change the approach before `factory unpark <n>`.',
|
|
23
|
+
config: 'fix the named config file and line, then run `factory unpark <n>`.',
|
|
24
|
+
};
|
|
25
|
+
export function parkUnblockHint(reason, issue) {
|
|
26
|
+
return PARK_HINTS[reason].replaceAll('<n>', String(issue));
|
|
27
|
+
}
|
|
28
|
+
function clean(v) {
|
|
29
|
+
return stripHiddenContent(v)
|
|
30
|
+
.replace(/[\r\n]+/g, ' ')
|
|
31
|
+
.trim();
|
|
32
|
+
}
|
|
33
|
+
export function renderParkComment(input) {
|
|
34
|
+
const blockers = input.blockers ?? [];
|
|
35
|
+
const stateLines = [
|
|
36
|
+
`reason: ${clean(input.reason)}`,
|
|
37
|
+
`lane: ${(input.lane && clean(input.lane)) || '-'}`,
|
|
38
|
+
`run: ${clean(input.runId)}`,
|
|
39
|
+
`branch: ${clean(input.branch)}`,
|
|
40
|
+
`park-sha: ${(input.parkSha && clean(input.parkSha)) || '-'}`,
|
|
41
|
+
`blockers: ${blockers.length > 0 ? blockers.map((n) => `#${n}`).join(', ') : '-'}`,
|
|
42
|
+
];
|
|
43
|
+
if (input.autoUnparks !== undefined && input.autoUnparks > 0)
|
|
44
|
+
stateLines.push(`auto-unparks: ${input.autoUnparks}`);
|
|
45
|
+
if (input.status)
|
|
46
|
+
stateLines.push(`status: ${clean(input.status)}`);
|
|
47
|
+
const stateBlock = stateLines.join('\n');
|
|
48
|
+
return [
|
|
49
|
+
'### Factory park state',
|
|
50
|
+
'',
|
|
51
|
+
STATE_HEADING,
|
|
52
|
+
'',
|
|
53
|
+
fenceExcerpt(stateBlock),
|
|
54
|
+
'',
|
|
55
|
+
'**Why it parked**',
|
|
56
|
+
'',
|
|
57
|
+
fenceExcerpt(stripHiddenContent(input.message)),
|
|
58
|
+
'',
|
|
59
|
+
`**What unblocks this:** ${parkUnblockHint(input.reason, input.issue)}`,
|
|
60
|
+
'',
|
|
61
|
+
issueStateMarker(input.issue),
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
function findStateFence(body) {
|
|
65
|
+
const lines = body.split('\n');
|
|
66
|
+
const heading = lines.indexOf(STATE_HEADING);
|
|
67
|
+
if (heading === -1)
|
|
68
|
+
return null;
|
|
69
|
+
const isFence = (l) => /^`{5,}$/.test(l);
|
|
70
|
+
const open = lines.findIndex((l, i) => i > heading && isFence(l));
|
|
71
|
+
if (open === -1)
|
|
72
|
+
return null;
|
|
73
|
+
const close = lines.indexOf(lines[open], open + 1);
|
|
74
|
+
if (close === -1)
|
|
75
|
+
return null;
|
|
76
|
+
return { lines, open, close };
|
|
77
|
+
}
|
|
78
|
+
function parseStateBlock(body) {
|
|
79
|
+
const fence = findStateFence(body);
|
|
80
|
+
if (fence === null)
|
|
81
|
+
return null;
|
|
82
|
+
const { lines, open, close } = fence;
|
|
83
|
+
const values = new Map();
|
|
84
|
+
for (const line of lines.slice(open + 1, close)) {
|
|
85
|
+
const sep = line.indexOf(':');
|
|
86
|
+
if (sep === -1)
|
|
87
|
+
continue;
|
|
88
|
+
values.set(line.slice(0, sep).trim(), line.slice(sep + 1).trim());
|
|
89
|
+
}
|
|
90
|
+
const reason = values.get('reason');
|
|
91
|
+
if (!reason)
|
|
92
|
+
return null;
|
|
93
|
+
const optional = (key) => {
|
|
94
|
+
const v = values.get(key);
|
|
95
|
+
return v === undefined || v === '' || v === '-' ? undefined : v;
|
|
96
|
+
};
|
|
97
|
+
const blockers = [...(values.get('blockers') ?? '').matchAll(/#(\d+)/g)].map((m) => Number(m[1]));
|
|
98
|
+
const rawAutoUnparks = values.get('auto-unparks') ?? '';
|
|
99
|
+
const autoUnparks = /^\d+$/.test(rawAutoUnparks) && Number.isSafeInteger(Number(rawAutoUnparks)) ? Number(rawAutoUnparks) : 0;
|
|
100
|
+
return {
|
|
101
|
+
reason,
|
|
102
|
+
lane: optional('lane'),
|
|
103
|
+
runId: optional('run'),
|
|
104
|
+
branch: optional('branch'),
|
|
105
|
+
parkSha: optional('park-sha'),
|
|
106
|
+
blockers,
|
|
107
|
+
autoUnparks,
|
|
108
|
+
status: optional('status'),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Rewrites the state block (and the unblock hint on a reason change) in place; null when there is no state block. */
|
|
112
|
+
export function patchParkComment(body, issue, patch) {
|
|
113
|
+
const fence = findStateFence(body);
|
|
114
|
+
if (fence === null)
|
|
115
|
+
return null;
|
|
116
|
+
const { lines, open, close } = fence;
|
|
117
|
+
const inner = lines.slice(open + 1, close);
|
|
118
|
+
const setLine = (key, value) => {
|
|
119
|
+
const at = inner.findIndex((l) => l.slice(0, Math.max(l.indexOf(':'), 0)).trim() === key);
|
|
120
|
+
if (at === -1)
|
|
121
|
+
inner.push(`${key}: ${value}`);
|
|
122
|
+
else
|
|
123
|
+
inner[at] = `${key}: ${value}`;
|
|
124
|
+
};
|
|
125
|
+
if (patch.reason !== undefined)
|
|
126
|
+
setLine('reason', clean(patch.reason));
|
|
127
|
+
if (patch.status !== undefined)
|
|
128
|
+
setLine('status', clean(patch.status));
|
|
129
|
+
if (patch.autoUnparks !== undefined)
|
|
130
|
+
setLine('auto-unparks', String(patch.autoUnparks));
|
|
131
|
+
const out = [...lines.slice(0, open + 1), ...inner, ...lines.slice(close)];
|
|
132
|
+
if (patch.reason !== undefined) {
|
|
133
|
+
const hint = out.findIndex((l) => l.startsWith('**What unblocks this:**'));
|
|
134
|
+
if (hint !== -1)
|
|
135
|
+
out[hint] = `**What unblocks this:** ${parkUnblockHint(patch.reason, issue)}`;
|
|
136
|
+
}
|
|
137
|
+
return out.join('\n');
|
|
138
|
+
}
|
|
139
|
+
/** Lowest-id marked comment decides; null when none, or when its state block is unreadable. */
|
|
140
|
+
export function readParkState(comments, issue) {
|
|
141
|
+
const marked = comments
|
|
142
|
+
.filter((c) => {
|
|
143
|
+
const m = MARKER_RE.exec(c.body);
|
|
144
|
+
return m !== null && (issue === undefined || Number(m[1]) === issue);
|
|
145
|
+
})
|
|
146
|
+
.sort((a, b) => a.id - b.id);
|
|
147
|
+
if (marked.length === 0)
|
|
148
|
+
return null;
|
|
149
|
+
return parseStateBlock(marked[0].body);
|
|
150
|
+
}
|
|
151
|
+
/** The lowest-id marker comment written by the authenticated user, or undefined. */
|
|
152
|
+
export async function findOwnParkComment(deps) {
|
|
153
|
+
const { octokit, repo, issue } = deps;
|
|
154
|
+
const [owner, name] = repo.split('/');
|
|
155
|
+
const marker = issueStateMarker(issue);
|
|
156
|
+
const { data: me } = await octokit.rest.users.getAuthenticated();
|
|
157
|
+
let existing;
|
|
158
|
+
for (let page = 1;; page++) {
|
|
159
|
+
const { data } = await octokit.rest.issues.listComments({
|
|
160
|
+
owner,
|
|
161
|
+
repo: name,
|
|
162
|
+
issue_number: issue,
|
|
163
|
+
per_page: PAGE_SIZE,
|
|
164
|
+
page,
|
|
165
|
+
});
|
|
166
|
+
for (const comment of data) {
|
|
167
|
+
const body = comment.body ?? '';
|
|
168
|
+
if (comment.user?.id !== me.id || !body.includes(marker))
|
|
169
|
+
continue;
|
|
170
|
+
if (existing === undefined || comment.id < existing.id)
|
|
171
|
+
existing = { id: comment.id, body };
|
|
172
|
+
}
|
|
173
|
+
if (data.length < PAGE_SIZE)
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
return existing;
|
|
177
|
+
}
|
|
178
|
+
export async function upsertParkComment(deps) {
|
|
179
|
+
const { octokit, repo, input } = deps;
|
|
180
|
+
const [owner, name] = repo.split('/');
|
|
181
|
+
const existing = await findOwnParkComment({ octokit, repo, issue: input.issue });
|
|
182
|
+
const autoUnparks = input.autoUnparks ?? (existing === undefined ? 0 : (readParkState([existing], input.issue)?.autoUnparks ?? 0));
|
|
183
|
+
const body = renderParkComment({ ...input, autoUnparks });
|
|
184
|
+
if (existing !== undefined) {
|
|
185
|
+
await octokit.rest.issues.updateComment({ owner, repo: name, comment_id: existing.id, body });
|
|
186
|
+
return { commentId: existing.id, created: false };
|
|
187
|
+
}
|
|
188
|
+
const { data } = await octokit.rest.issues.createComment({ owner, repo: name, issue_number: input.issue, body });
|
|
189
|
+
return { commentId: data.id, created: true };
|
|
190
|
+
}
|
|
191
|
+
/** Never throws: a GitHub or git failure comes back as `{ ok: false, error }`. */
|
|
192
|
+
export async function publishParkComment(deps) {
|
|
193
|
+
const { octokit, repo, worktree, input, readHead = readWorktreeHead } = deps;
|
|
194
|
+
let parkSha;
|
|
195
|
+
if (worktree) {
|
|
196
|
+
try {
|
|
197
|
+
parkSha = (await readHead(worktree)) || undefined;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
parkSha = undefined;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const result = await upsertParkComment({ octokit, repo, input: { ...input, parkSha } });
|
|
205
|
+
return { ok: true, ...result };
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=park-comment.js.map
|