@feigi/fleet-ctl 3.91.1 → 3.91.3
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/package.json
CHANGED
package/scripts/ledger.mjs
CHANGED
|
@@ -1984,7 +1984,8 @@ function checkReaders(where) {
|
|
|
1984
1984
|
// A `gh pr view --json <fields>` answer: an object whose every requested
|
|
1985
1985
|
// field has the type that field has. Anything else was not what was asked
|
|
1986
1986
|
// for, so it could not look.
|
|
1987
|
-
const
|
|
1987
|
+
const isString = (v) => typeof v === "string";
|
|
1988
|
+
const prFieldOk = { state: isString, labels: Array.isArray, closingIssuesReferences: Array.isArray, url: isString };
|
|
1988
1989
|
const prView = (pr, fields) => {
|
|
1989
1990
|
const what = `gh pr view ${pr} --json ${fields}`;
|
|
1990
1991
|
const { r, v } = lookJson(what, ["pr", "view", String(pr), "--json", fields]);
|
|
@@ -1997,6 +1998,34 @@ function checkReaders(where) {
|
|
|
1997
1998
|
}
|
|
1998
1999
|
return v;
|
|
1999
2000
|
};
|
|
2001
|
+
// PR `pr`'s prView() answer to `fields`, which name closingIssuesReferences
|
|
2002
|
+
// and url, as `view`, with the issue numbers, unique, of its closing
|
|
2003
|
+
// references that are in the PR's own repository: its url less
|
|
2004
|
+
// `/pull/<pr>`. A reference is in it when its url is that repository's
|
|
2005
|
+
// `/issues/<number>`, or, carrying no url, when its repository's
|
|
2006
|
+
// `<owner>/<name>` ends that repository's url. A reference with no integer
|
|
2007
|
+
// number, or with neither a url nor a repository to place it by, could not
|
|
2008
|
+
// look: it is never taken for the PR's own repository's.
|
|
2009
|
+
const prViewClosing = (pr, fields) => {
|
|
2010
|
+
const view = prView(pr, fields);
|
|
2011
|
+
const what = `gh pr view ${pr} --json ${fields}`;
|
|
2012
|
+
const suffix = `/pull/${pr}`;
|
|
2013
|
+
if (!view.url.endsWith(suffix)) die(`${where}: could not look — ${what} answered url ${view.url}, not PR #${pr}'s; nothing written`);
|
|
2014
|
+
const own = view.url.slice(0, -suffix.length);
|
|
2015
|
+
const str = (v) => typeof v === "string" && v !== "";
|
|
2016
|
+
const numbers = new Set();
|
|
2017
|
+
for (const ref of view.closingIssuesReferences) {
|
|
2018
|
+
const n = ref?.number;
|
|
2019
|
+
if (!Number.isInteger(n)) die(`${where}: could not look — ${what} answered a reference with no issue number; nothing written`);
|
|
2020
|
+
const repo = ref.repository;
|
|
2021
|
+
let isOwn;
|
|
2022
|
+
if (str(ref.url)) isOwn = ref.url === `${own}/issues/${n}`;
|
|
2023
|
+
else if (str(repo?.name) && str(repo?.owner?.login)) isOwn = own.endsWith(`/${repo.owner.login}/${repo.name}`);
|
|
2024
|
+
else die(`${where}: could not look — ${what} answered a reference to #${n} with neither a url nor a repository; nothing written`);
|
|
2025
|
+
if (isOwn) numbers.add(n);
|
|
2026
|
+
}
|
|
2027
|
+
return { view, numbers: [...numbers] };
|
|
2028
|
+
};
|
|
2000
2029
|
// The title of issue `n`, as `gh issue view <n> --json title` answers it.
|
|
2001
2030
|
const issueTitle = (n) => {
|
|
2002
2031
|
const what = `gh issue view ${n} --json title`;
|
|
@@ -2006,28 +2035,27 @@ function checkReaders(where) {
|
|
|
2006
2035
|
}
|
|
2007
2036
|
return v.title;
|
|
2008
2037
|
};
|
|
2009
|
-
return { repoDir, look, verifyShaOf, prView, issueTitle };
|
|
2038
|
+
return { repoDir, look, verifyShaOf, prView, prViewClosing, issueTitle };
|
|
2010
2039
|
}
|
|
2011
2040
|
|
|
2012
2041
|
// The finisher gate's closing-reference checks for the PR `member` finishes,
|
|
2013
|
-
// read off the PR's closingIssuesReferences
|
|
2014
|
-
//
|
|
2015
|
-
//
|
|
2016
|
-
//
|
|
2017
|
-
//
|
|
2018
|
-
//
|
|
2019
|
-
//
|
|
2020
|
-
//
|
|
2021
|
-
//
|
|
2042
|
+
// read off the PR's closingIssuesReferences in its own repository — a
|
|
2043
|
+
// reference into another repository closes no issue here, whatever its
|
|
2044
|
+
// number — and the titles of the issues they name; its second argument is
|
|
2045
|
+
// rulingOf()'s answer, which let the PR through. Check 1, when a settled
|
|
2046
|
+
// implementer's token names the PR: each ticket it names is closed by the
|
|
2047
|
+
// body when the PR's ruling is closed_own_ticket=yes, and is not when the
|
|
2048
|
+
// ruling is no. Check 2, for every PR: no reference may be the
|
|
2049
|
+
// suggestion-band record of this PR's review — an issue whose title, trimmed,
|
|
2050
|
+
// is recordTitle(), as dispositions-check.mjs finds the record. Returns one
|
|
2051
|
+
// line per failure. A read the checks need dies, naming the read, and writes
|
|
2052
|
+
// nothing.
|
|
2022
2053
|
async function closingRefusals(member, { tickets, ruling, tsv }) {
|
|
2023
2054
|
const pr = member.number;
|
|
2024
2055
|
const where = `dispatch ${member.name}`;
|
|
2025
2056
|
const { recordTitle } = await import("./dispositions-check.mjs");
|
|
2026
|
-
const {
|
|
2027
|
-
const refs =
|
|
2028
|
-
if (!refs.every(Number.isInteger)) {
|
|
2029
|
-
die(`${where}: could not look — gh pr view ${pr} --json closingIssuesReferences answered a reference with no issue number; nothing written`);
|
|
2030
|
-
}
|
|
2057
|
+
const { prViewClosing, issueTitle } = checkReaders(where);
|
|
2058
|
+
const refs = prViewClosing(pr, "closingIssuesReferences,url").numbers;
|
|
2031
2059
|
const failures = [];
|
|
2032
2060
|
if (tickets.length > 0) {
|
|
2033
2061
|
const verdict = ruling.closed_own_ticket;
|
|
@@ -2491,7 +2519,8 @@ function recordFiled(id, subject) {
|
|
|
2491
2519
|
// checked before anything is written:
|
|
2492
2520
|
//
|
|
2493
2521
|
// impl-N=PR#M verify-sha.sh <branch> <head> exits 0; PR M
|
|
2494
|
-
// reads OPEN and its closingIssuesReferences
|
|
2522
|
+
// reads OPEN and its closingIssuesReferences in its
|
|
2523
|
+
// own repository name N;
|
|
2495
2524
|
// the Check-step record (below) holds for <head>
|
|
2496
2525
|
// impl-N=bailed bailCause is not null; the receipt names it
|
|
2497
2526
|
// fix-pr-M=applied:<head> origin's refs/heads/<branch> is <head>, fetched
|
|
@@ -2603,7 +2632,7 @@ async function runSettleFromReport() {
|
|
|
2603
2632
|
if (report.member !== member) invalid(`$.member: ${JSON.stringify(report.member)} is not ${member}`);
|
|
2604
2633
|
|
|
2605
2634
|
// The gh and git reads this check makes, shared with the finisher gate.
|
|
2606
|
-
const { repoDir, look, verifyShaOf, prView } = checkReaders(where);
|
|
2635
|
+
const { repoDir, look, verifyShaOf, prView, prViewClosing } = checkReaders(where);
|
|
2607
2636
|
// origin's head of `branch`, or null when origin has no such branch.
|
|
2608
2637
|
const remoteHead = (branch) => {
|
|
2609
2638
|
const ref = `refs/heads/${branch}`;
|
|
@@ -2653,9 +2682,9 @@ async function runSettleFromReport() {
|
|
|
2653
2682
|
const sha = verifyShaOf(report.branch, report.head);
|
|
2654
2683
|
check(`verify-sha.sh ${report.branch} ${report.head} exits 0`, sha.status === 0);
|
|
2655
2684
|
recipeGate(report.head);
|
|
2656
|
-
const pr =
|
|
2685
|
+
const { view: pr, numbers } = prViewClosing(report.pr, "state,closingIssuesReferences,url");
|
|
2657
2686
|
check(`PR #${report.pr} reads OPEN`, pr.state === "OPEN");
|
|
2658
|
-
check(`PR #${report.pr} closes #${live.number}`,
|
|
2687
|
+
check(`PR #${report.pr} closes #${live.number}`, numbers.includes(live.number));
|
|
2659
2688
|
} else {
|
|
2660
2689
|
outcome = "bailed";
|
|
2661
2690
|
check(`bailCause: ${report.bailCause}`, report.bailCause !== null);
|
|
@@ -117,7 +117,7 @@ and will act on it. Relay everything — reversals too — then dispatch.
|
|
|
117
117
|
append` command to run. Any row for the PR passes, so a replacement finisher
|
|
118
118
|
is never held once the first passed; a PR no such token names is not gated.
|
|
119
119
|
No flag skips it: a PR `append` refuses is fixed on the ledger, as at close-out.
|
|
120
|
-
- **The PR's closing references match its ruling and exclude its own suggestion-band record** → `ledger.mjs dispatch` reads the PR's `closingIssuesReferences` from `gh pr view <M>` before it dispatches any finisher. When the ruled row says `closed_own_ticket=yes`, each ticket a settled `impl-<N>=PR#<M>` token names must be closed by the body (`Closes #<N>`); when it says `no`, none may be, and no reference may be the PR's own suggestion-band record. A blank verdict is refused until it is set. A refusal names the PR and the ticket. The controller repairs the body itself with `gh pr edit <M> --body-file <file>`, then dispatches the finisher again. A `gh` read that fails refuses the dispatch and writes nothing.
|
|
120
|
+
- **The PR's closing references match its ruling and exclude its own suggestion-band record** → `ledger.mjs dispatch` reads the PR's `closingIssuesReferences` from `gh pr view <M>` before it dispatches any finisher, counting only those in the PR's own repository: a reference into another repository closes none of this repository's tickets, whatever its number. When the ruled row says `closed_own_ticket=yes`, each ticket a settled `impl-<N>=PR#<M>` token names must be closed by the body (`Closes #<N>`); when it says `no`, none may be, and no reference may be the PR's own suggestion-band record. A blank verdict is refused until it is set. A refusal names the PR and the ticket. The controller repairs the body itself with `gh pr edit <M> --body-file <file>`, then dispatches the finisher again. A `gh` read that fails refuses the dispatch and writes nothing.
|
|
121
121
|
|
|
122
122
|
Gate on the `check` job, **not** on `ci-state --quiet` exit 0: a behind PR never
|
|
123
123
|
reaches full green, so an exit-0 gate strands it unlabelled. The finisher reads
|