@feigi/fleet-ctl 3.99.2 → 3.99.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 +1 -1
- package/scripts/pull-attribution.mjs +35 -13
package/package.json
CHANGED
|
@@ -18,12 +18,20 @@
|
|
|
18
18
|
// the Pull whose implementer rows carry that PR in `pr`.
|
|
19
19
|
// - A nested member books with its nearest booked ancestor.
|
|
20
20
|
//
|
|
21
|
-
// RULING.
|
|
22
|
-
//
|
|
23
|
-
// whose implementer rows carry the PR
|
|
24
|
-
// such
|
|
25
|
-
//
|
|
26
|
-
//
|
|
21
|
+
// RULING. One PR's merge counts once, to one Pull. First, carrier binding: a
|
|
22
|
+
// Pull takes the Ruling of a PR it is the carrier of (the last Pull in file
|
|
23
|
+
// order whose implementer rows carry the PR) when a ruling row names that PR,
|
|
24
|
+
// the latest such row in file order, whatever its ticket or date. A Pull that
|
|
25
|
+
// carries several ruled PRs takes the one whose ruling row is latest in file
|
|
26
|
+
// order and leaves the others to the date floor. Every ruling row of the bound
|
|
27
|
+
// row's tickets is checked as `rulingFor` checks the Pull's own ticket's, so a
|
|
28
|
+
// Pull can be refused for a bad row of a ticket that is not its own.
|
|
29
|
+
// Then the date-floor fallback: each other Pull's candidate Ruling is
|
|
30
|
+
// `rulingFor` of its ticket and date, unless a carrier took that PR. Among the
|
|
31
|
+
// Pulls whose candidate names a PR, the last in file order takes it, with the
|
|
32
|
+
// verdict of the latest such ruling row in file order, and is unpriced, since
|
|
33
|
+
// its PR's review and fix spend cannot be booked. The other candidates stay
|
|
34
|
+
// unruled.
|
|
27
35
|
//
|
|
28
36
|
// MERGED: the Pull holds a Ruling and, when PR states are given, its PR is
|
|
29
37
|
// MERGED. With PR states, a ruled PR that is OPEN or not in the list, or an
|
|
@@ -305,20 +313,34 @@ export function attribute({ features, members, tiers, prs = null, windowStart =
|
|
|
305
313
|
}
|
|
306
314
|
}
|
|
307
315
|
|
|
308
|
-
// Rulings: each PR's merge to one Pull.
|
|
316
|
+
// Rulings: each PR's merge to one Pull, carriers first, then the date floor.
|
|
309
317
|
const inWindow = pulls.filter((p) => windowStart === null || p.run_date >= windowStart);
|
|
310
318
|
const rulings = rulingsByTicket(tiers);
|
|
311
319
|
const fileOrder = new Map(tiers.map((t, i) => [t, i]));
|
|
312
|
-
|
|
320
|
+
// Each PR to its latest ruling row in file order, whatever its ticket or date.
|
|
321
|
+
const ruleRows = new Set([...rulings.values()].flat());
|
|
322
|
+
const rulingOfPr = new Map(tiers.filter((t) => ruleRows.has(t)).map((t) => [t.pr, t]));
|
|
323
|
+
const ruled = new Map(); // pull -> { pr, ruling, carried }
|
|
324
|
+
const candidates = new Map(); // pr -> [{ pull, ruling }], the Pulls no carrier binding ruled
|
|
313
325
|
for (const p of inWindow) {
|
|
314
|
-
|
|
315
|
-
|
|
326
|
+
// Called for every Pull, bound or not, so a bad ruling row of its ticket is refused either way.
|
|
327
|
+
const candidate = rulingFor(rulings, p.ticket, p.run_date);
|
|
328
|
+
let bound = null;
|
|
329
|
+
for (const r of impls.get(p)) {
|
|
330
|
+
const row = carrier.get(r.pr) === p ? rulingOfPr.get(r.pr) : undefined;
|
|
331
|
+
if (row && (bound === null || fileOrder.get(row) > fileOrder.get(bound))) bound = row;
|
|
332
|
+
}
|
|
333
|
+
if (bound) {
|
|
334
|
+
// A bound row of another ticket is refused on the same terms as one of the Pull's own.
|
|
335
|
+
for (const t of String(bound.ticket).split("+")) rulingFor(rulings, t, "");
|
|
336
|
+
ruled.set(p, { pr: bound.pr, ruling: bound, carried: true });
|
|
337
|
+
} else if (candidate) (candidates.get(candidate.pr) ?? candidates.set(candidate.pr, []).get(candidate.pr)).push({ pull: p, ruling: candidate });
|
|
316
338
|
}
|
|
317
|
-
const
|
|
339
|
+
const taken = new Set([...ruled.values()].map((r) => r.pr));
|
|
318
340
|
for (const [pr, cands] of candidates) {
|
|
341
|
+
if (taken.has(pr)) continue;
|
|
319
342
|
const ruling = cands.reduce((a, c) => (fileOrder.get(c.ruling) > fileOrder.get(a) ? c.ruling : a), cands[0].ruling);
|
|
320
|
-
|
|
321
|
-
ruled.set(held?.pull ?? cands.at(-1).pull, { pr, ruling, carried: !!held });
|
|
343
|
+
ruled.set(cands.at(-1).pull, { pr, ruling, carried: false });
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
const state = prs && new Map(prs.map((p) => [String(p.number), p.state]));
|