@rmartz/pr-lifecycle 8.0.0 → 9.1.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/dist/bin/pr-lifecycle.js +1 -1
- package/dist/{chunk-ENU2AAWK.js → chunk-4GTGNP35.js} +90 -14
- package/dist/index.d.ts +58 -6
- package/dist/index.js +13 -1
- package/package.json +1 -1
package/dist/bin/pr-lifecycle.js
CHANGED
|
@@ -184,7 +184,8 @@ function parseReconcile(args) {
|
|
|
184
184
|
case "--pr":
|
|
185
185
|
case "--trusted-authors":
|
|
186
186
|
case "--hold-checks":
|
|
187
|
-
case "--ignore-checks":
|
|
187
|
+
case "--ignore-checks":
|
|
188
|
+
case "--prose-paths": {
|
|
188
189
|
const value = args[index + 1];
|
|
189
190
|
if (value === void 0 || value.startsWith("--")) {
|
|
190
191
|
return usageError(`${arg} requires a value`);
|
|
@@ -212,6 +213,7 @@ function parseReconcile(args) {
|
|
|
212
213
|
}
|
|
213
214
|
const holdChecks = listOption(values["--hold-checks"]);
|
|
214
215
|
const ignoredChecks = listOption(values["--ignore-checks"]);
|
|
216
|
+
const prosePatterns = listOption(values["--prose-paths"]);
|
|
215
217
|
return {
|
|
216
218
|
command: "reconcile",
|
|
217
219
|
owner: repoMatch[1],
|
|
@@ -227,7 +229,8 @@ function parseReconcile(args) {
|
|
|
227
229
|
},
|
|
228
230
|
dryRun,
|
|
229
231
|
json,
|
|
230
|
-
tokenAdvisory
|
|
232
|
+
tokenAdvisory,
|
|
233
|
+
...prosePatterns === void 0 ? {} : { prosePatterns }
|
|
231
234
|
};
|
|
232
235
|
}
|
|
233
236
|
function listOption(value) {
|
|
@@ -678,6 +681,7 @@ var LIFECYCLE_LABELS = [
|
|
|
678
681
|
"review requested"
|
|
679
682
|
];
|
|
680
683
|
var AUTO_MERGE_LABEL = "auto-merge enabled";
|
|
684
|
+
var DEPENDABOT_REBASING_LABEL = "dependabot rebasing";
|
|
681
685
|
var UPDATE_REQUIRED_LABEL = "update required";
|
|
682
686
|
function lifecycleLabels(state) {
|
|
683
687
|
switch (state) {
|
|
@@ -733,8 +737,11 @@ function planReconcile(facts, policy) {
|
|
|
733
737
|
autoMerge = facts.immediatelyMergeable ? "merge" : "arm";
|
|
734
738
|
}
|
|
735
739
|
}
|
|
736
|
-
const owned = [...LIFECYCLE_LABELS];
|
|
740
|
+
const owned = [...LIFECYCLE_LABELS, DEPENDABOT_REBASING_LABEL];
|
|
737
741
|
const desired = new Set(lifecycleLabels(state));
|
|
742
|
+
if (facts.updater === "dependabot" && facts.dependabotRebasing) {
|
|
743
|
+
desired.add(DEPENDABOT_REBASING_LABEL);
|
|
744
|
+
}
|
|
738
745
|
if (arming) {
|
|
739
746
|
owned.push(AUTO_MERGE_LABEL);
|
|
740
747
|
if (shouldArm && autoMerge !== "merge") {
|
|
@@ -763,6 +770,10 @@ function withoutReleaseActions(plan) {
|
|
|
763
770
|
// src/github/dependabot-rebase.ts
|
|
764
771
|
var DEPENDABOT_REBASE_COMMAND = "@dependabot rebase";
|
|
765
772
|
var DEPENDABOT_REBASING_NOTICE = "Dependabot is rebasing this PR";
|
|
773
|
+
var DEPENDABOT_RECREATING_NOTICE = "Dependabot is recreating this PR";
|
|
774
|
+
function isDependabotRebasing(prBody) {
|
|
775
|
+
return prBody.includes(DEPENDABOT_REBASING_NOTICE) || prBody.includes(DEPENDABOT_RECREATING_NOTICE);
|
|
776
|
+
}
|
|
766
777
|
function rebaseRequestMarker(headSha) {
|
|
767
778
|
return `<!-- pr-lifecycle:dependabot-rebase head=${headSha} -->`;
|
|
768
779
|
}
|
|
@@ -772,7 +783,7 @@ function buildRebaseRequestBody(headSha) {
|
|
|
772
783
|
${rebaseRequestMarker(headSha)}`;
|
|
773
784
|
}
|
|
774
785
|
function isRebasePending(prBody, comments, headSha) {
|
|
775
|
-
if (prBody
|
|
786
|
+
if (isDependabotRebasing(prBody)) {
|
|
776
787
|
return true;
|
|
777
788
|
}
|
|
778
789
|
const marker = rebaseRequestMarker(headSha);
|
|
@@ -807,6 +818,11 @@ var OWNED_LABEL_DEFINITIONS = {
|
|
|
807
818
|
color: "B60205",
|
|
808
819
|
description: "The PR's required CI (excluding gate checks) is failing on its head."
|
|
809
820
|
},
|
|
821
|
+
"dependabot rebasing": {
|
|
822
|
+
name: DEPENDABOT_REBASING_LABEL,
|
|
823
|
+
color: "0366D6",
|
|
824
|
+
description: "Dependabot is rebasing or recreating this PR; its branch is about to change."
|
|
825
|
+
},
|
|
810
826
|
"fix required": {
|
|
811
827
|
name: "fix required",
|
|
812
828
|
color: "D93F0B",
|
|
@@ -896,6 +912,25 @@ async function updateBranch(release, target) {
|
|
|
896
912
|
}
|
|
897
913
|
}
|
|
898
914
|
|
|
915
|
+
// src/lineage/prose.ts
|
|
916
|
+
var DEFAULT_PROSE_PATTERNS = ["*.md", "!index.md"];
|
|
917
|
+
function compile(pattern) {
|
|
918
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*");
|
|
919
|
+
return new RegExp(`^${escaped}$`);
|
|
920
|
+
}
|
|
921
|
+
function proseMatcher(patterns) {
|
|
922
|
+
const include = patterns.filter((pattern) => !pattern.startsWith("!")).map(compile);
|
|
923
|
+
const exclude = patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => compile(pattern.slice(1)));
|
|
924
|
+
return (path) => {
|
|
925
|
+
const basename2 = path.slice(path.lastIndexOf("/") + 1);
|
|
926
|
+
return include.some((regex) => regex.test(basename2)) && !exclude.some((regex) => regex.test(basename2));
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
function overlappingProse(ours, theirs, isProse) {
|
|
930
|
+
const theirSet = new Set(theirs);
|
|
931
|
+
return ours.filter((path) => theirSet.has(path) && isProse(path)).sort();
|
|
932
|
+
}
|
|
933
|
+
|
|
899
934
|
// src/lineage/git.ts
|
|
900
935
|
import { execFile } from "child_process";
|
|
901
936
|
var TIMEOUT_MS = 12e4;
|
|
@@ -1007,6 +1042,23 @@ async function createMergeVerifier(git, source) {
|
|
|
1007
1042
|
]);
|
|
1008
1043
|
return merged.code === 0 && merged.stdout.split("\n")[0]?.trim() === step.tree;
|
|
1009
1044
|
},
|
|
1045
|
+
async changedPaths(from, to) {
|
|
1046
|
+
if (!OBJECT_ID.test(from) || !OBJECT_ID.test(to)) {
|
|
1047
|
+
return void 0;
|
|
1048
|
+
}
|
|
1049
|
+
const diff = await git.run([
|
|
1050
|
+
"--git-dir",
|
|
1051
|
+
gitDir,
|
|
1052
|
+
"diff-tree",
|
|
1053
|
+
"-r",
|
|
1054
|
+
"-z",
|
|
1055
|
+
"--name-only",
|
|
1056
|
+
"--no-renames",
|
|
1057
|
+
from,
|
|
1058
|
+
to
|
|
1059
|
+
]);
|
|
1060
|
+
return diff.code === 0 ? diff.stdout.split("\0").filter((path) => path !== "") : void 0;
|
|
1061
|
+
},
|
|
1010
1062
|
async dispose() {
|
|
1011
1063
|
await rm(gitDir, { recursive: true, force: true });
|
|
1012
1064
|
}
|
|
@@ -1026,11 +1078,22 @@ async function onBaseBranch(client, commit, baseHeadSha) {
|
|
|
1026
1078
|
const { status } = await client.compareCommits(commit, baseHeadSha);
|
|
1027
1079
|
return status === "identical" || status === "ahead";
|
|
1028
1080
|
}
|
|
1081
|
+
async function proseOverlapReason(verifier, step, isProse) {
|
|
1082
|
+
const ours = await verifier.changedPaths(step.mergeBase, step.first);
|
|
1083
|
+
const theirs = await verifier.changedPaths(step.mergeBase, step.second);
|
|
1084
|
+
if (ours === void 0 || theirs === void 0) {
|
|
1085
|
+
return "could not list the files each side changed";
|
|
1086
|
+
}
|
|
1087
|
+
const overlap = overlappingProse(ours, theirs, isProse);
|
|
1088
|
+
return overlap.length === 0 ? void 0 : `both sides edited the same prose (${overlap.join(", ")})`;
|
|
1089
|
+
}
|
|
1029
1090
|
async function walk(client, git, input, wanted) {
|
|
1030
1091
|
const verifier = await createMergeVerifier(git, input.source);
|
|
1031
1092
|
if (verifier === void 0) {
|
|
1032
1093
|
return { cleanAncestors: [], stoppedBecause: "git unavailable or older than 2.40" };
|
|
1033
1094
|
}
|
|
1095
|
+
const patterns = input.prosePatterns ?? DEFAULT_PROSE_PATTERNS;
|
|
1096
|
+
const isProse = patterns.length === 0 ? void 0 : proseMatcher(patterns);
|
|
1034
1097
|
const cleanAncestors = [];
|
|
1035
1098
|
try {
|
|
1036
1099
|
let current = input.headSha;
|
|
@@ -1044,15 +1107,14 @@ async function walk(client, git, input, wanted) {
|
|
|
1044
1107
|
return { cleanAncestors, stoppedBecause: `${current} merges a commit not on the base` };
|
|
1045
1108
|
}
|
|
1046
1109
|
const { mergeBaseSha } = await client.compareCommits(first, second);
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
first,
|
|
1050
|
-
second,
|
|
1051
|
-
tree: commit.treeSha
|
|
1052
|
-
});
|
|
1053
|
-
if (!clean) {
|
|
1110
|
+
const mergeStep = { mergeBase: mergeBaseSha, first, second, tree: commit.treeSha };
|
|
1111
|
+
if (!await verifier.verify(mergeStep)) {
|
|
1054
1112
|
return { cleanAncestors, stoppedBecause: `${current} is not the clean automatic merge` };
|
|
1055
1113
|
}
|
|
1114
|
+
const overlap = isProse === void 0 ? void 0 : await proseOverlapReason(verifier, mergeStep, isProse);
|
|
1115
|
+
if (overlap !== void 0) {
|
|
1116
|
+
return { cleanAncestors, stoppedBecause: `${current}: ${overlap}` };
|
|
1117
|
+
}
|
|
1056
1118
|
cleanAncestors.push(first);
|
|
1057
1119
|
current = first;
|
|
1058
1120
|
if ([...wanted].every((sha) => cleanAncestors.includes(sha))) {
|
|
@@ -1249,7 +1311,8 @@ async function gatherLineageFor(client, pull, reviews, options) {
|
|
|
1249
1311
|
url: pull.cloneUrl,
|
|
1250
1312
|
...options.lineage.token === void 0 ? {} : { token: options.lineage.token }
|
|
1251
1313
|
},
|
|
1252
|
-
reviews
|
|
1314
|
+
reviews,
|
|
1315
|
+
...options.lineage.prosePatterns === void 0 ? {} : { prosePatterns: options.lineage.prosePatterns }
|
|
1253
1316
|
});
|
|
1254
1317
|
} catch (error) {
|
|
1255
1318
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1260,7 +1323,7 @@ async function gatherRebasePending(client, pr, pull, updater, policy) {
|
|
|
1260
1323
|
if (updater !== "dependabot") {
|
|
1261
1324
|
return false;
|
|
1262
1325
|
}
|
|
1263
|
-
if (pull.body
|
|
1326
|
+
if (isDependabotRebasing(pull.body)) {
|
|
1264
1327
|
return true;
|
|
1265
1328
|
}
|
|
1266
1329
|
const couldUpdate = policy.autoUpdate === true && pull.state === "open" && pull.labels.includes(UPDATE_REQUIRED_LABEL);
|
|
@@ -1300,6 +1363,7 @@ async function gatherFacts(client, pr, policy = {}, options = {}) {
|
|
|
1300
1363
|
cleanAncestors: lineage?.cleanAncestors ?? [],
|
|
1301
1364
|
updater,
|
|
1302
1365
|
rebasePending,
|
|
1366
|
+
dependabotRebasing: updater === "dependabot" && isDependabotRebasing(pull.body),
|
|
1303
1367
|
reviews: reviews.map((review) => toReviewFact(review, permissions)),
|
|
1304
1368
|
pendingBotReviewers: pull.requestedBotReviewers
|
|
1305
1369
|
}
|
|
@@ -1409,6 +1473,8 @@ reconcile options:
|
|
|
1409
1473
|
--skip-copilot-review Don't wait for requested bot reviewers (deprecated)
|
|
1410
1474
|
--hold-checks <a,b> Required checks whose pending is a hold (default: pr-policy)
|
|
1411
1475
|
--ignore-checks <a,b> Required checks CI never counts (default: merge-safety)
|
|
1476
|
+
--prose-paths <a,b> Basename patterns whose overlapping edits in a base merge
|
|
1477
|
+
need a new review (default: *.md,!index.md)
|
|
1412
1478
|
--dry-run Compute the plan without writing anything
|
|
1413
1479
|
--json Print one JSON object (schemaVersion 1) on stdout
|
|
1414
1480
|
--no-token-advisory Don't comment on the PR when arming lacks a release token
|
|
@@ -1452,7 +1518,11 @@ async function runCli(argv, io, deps) {
|
|
|
1452
1518
|
try {
|
|
1453
1519
|
const result2 = await reconcilePullRequest(client, args.pr, args.policy, {
|
|
1454
1520
|
dryRun: args.dryRun,
|
|
1455
|
-
lineage: {
|
|
1521
|
+
lineage: {
|
|
1522
|
+
git: deps.git ?? createGitRunner(),
|
|
1523
|
+
token,
|
|
1524
|
+
...args.prosePatterns === void 0 ? {} : { prosePatterns: args.prosePatterns }
|
|
1525
|
+
},
|
|
1456
1526
|
// Only arming, merging, and updating use the release token (never reads).
|
|
1457
1527
|
// Without one they are skipped, not done with GITHUB_TOKEN; see docs/cli.md.
|
|
1458
1528
|
release: hasReleaseToken ? clientFor(releaseToken) : "unavailable"
|
|
@@ -1519,16 +1589,22 @@ export {
|
|
|
1519
1589
|
computeState,
|
|
1520
1590
|
LIFECYCLE_LABELS,
|
|
1521
1591
|
AUTO_MERGE_LABEL,
|
|
1592
|
+
DEPENDABOT_REBASING_LABEL,
|
|
1522
1593
|
UPDATE_REQUIRED_LABEL,
|
|
1523
1594
|
planReconcile,
|
|
1524
1595
|
withoutReleaseActions,
|
|
1525
1596
|
DEPENDABOT_REBASE_COMMAND,
|
|
1526
1597
|
DEPENDABOT_REBASING_NOTICE,
|
|
1598
|
+
DEPENDABOT_RECREATING_NOTICE,
|
|
1599
|
+
isDependabotRebasing,
|
|
1527
1600
|
rebaseRequestMarker,
|
|
1528
1601
|
buildRebaseRequestBody,
|
|
1529
1602
|
isRebasePending,
|
|
1530
1603
|
OWNED_LABEL_DEFINITIONS,
|
|
1531
1604
|
executePlan,
|
|
1605
|
+
DEFAULT_PROSE_PATTERNS,
|
|
1606
|
+
proseMatcher,
|
|
1607
|
+
overlappingProse,
|
|
1532
1608
|
createGitRunner,
|
|
1533
1609
|
parseGitVersion,
|
|
1534
1610
|
createMergeVerifier,
|
package/dist/index.d.ts
CHANGED
|
@@ -178,6 +178,13 @@ interface PullRequestFacts {
|
|
|
178
178
|
* error. Always `false` for other PRs.
|
|
179
179
|
*/
|
|
180
180
|
rebasePending: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* For a Dependabot PR: the PR body says Dependabot is rebasing or recreating the
|
|
183
|
+
* branch right now. Unlike `rebasePending`, our own request doesn't count — only
|
|
184
|
+
* Dependabot's notice, which it removes when done. Drives the
|
|
185
|
+
* `dependabot rebasing` label. Always `false` for other PRs.
|
|
186
|
+
*/
|
|
187
|
+
dependabotRebasing: boolean;
|
|
181
188
|
reviews: readonly ReviewFact[];
|
|
182
189
|
/**
|
|
183
190
|
* Logins of the bots (e.g. Copilot) whose review is requested and not yet
|
|
@@ -413,7 +420,7 @@ interface CliDeps {
|
|
|
413
420
|
/** Runs git for approval carry-over; tests inject one. Defaults to the real binary. */
|
|
414
421
|
git?: GitRunner;
|
|
415
422
|
}
|
|
416
|
-
declare const USAGE = "Usage: ai-pr-lifecycle <command> [options]\n\nCommands:\n reconcile Recompute a PR's lifecycle state and converge its labels\n help Show this message\n\nreconcile options:\n --repo <owner/repo> Repository (required)\n --pr <number> Pull request number (required)\n --arm-auto-merge Arm/disarm native auto-merge from the state\n --auto-update Update approved PRs merge-safety flags 'update required'\n (Dependabot PRs are asked to rebase, never updated)\n --trusted-authors <a,b> Only these logins (with write access) may cast verdicts\n --skip-copilot-review Don't wait for requested bot reviewers (deprecated)\n --hold-checks <a,b> Required checks whose pending is a hold (default: pr-policy)\n --ignore-checks <a,b> Required checks CI never counts (default: merge-safety)\n --dry-run Compute the plan without writing anything\n --json Print one JSON object (schemaVersion 1) on stdout\n --no-token-advisory Don't comment on the PR when arming lacks a release token\n\nEnvironment:\n GITHUB_TOKEN Token for reads and writes (required)\n PR_LIFECYCLE_TOKEN\n Real-actor token for arming, merging, and updating;\n without it, labels are kept but those are skipped\n GITHUB_API_URL API base URL (GitHub Enterprise Server)";
|
|
423
|
+
declare const USAGE = "Usage: ai-pr-lifecycle <command> [options]\n\nCommands:\n reconcile Recompute a PR's lifecycle state and converge its labels\n help Show this message\n\nreconcile options:\n --repo <owner/repo> Repository (required)\n --pr <number> Pull request number (required)\n --arm-auto-merge Arm/disarm native auto-merge from the state\n --auto-update Update approved PRs merge-safety flags 'update required'\n (Dependabot PRs are asked to rebase, never updated)\n --trusted-authors <a,b> Only these logins (with write access) may cast verdicts\n --skip-copilot-review Don't wait for requested bot reviewers (deprecated)\n --hold-checks <a,b> Required checks whose pending is a hold (default: pr-policy)\n --ignore-checks <a,b> Required checks CI never counts (default: merge-safety)\n --prose-paths <a,b> Basename patterns whose overlapping edits in a base merge\n need a new review (default: *.md,!index.md)\n --dry-run Compute the plan without writing anything\n --json Print one JSON object (schemaVersion 1) on stdout\n --no-token-advisory Don't comment on the PR when arming lacks a release token\n\nEnvironment:\n GITHUB_TOKEN Token for reads and writes (required)\n PR_LIFECYCLE_TOKEN\n Real-actor token for arming, merging, and updating;\n without it, labels are kept but those are skipped\n GITHUB_API_URL API base URL (GitHub Enterprise Server)";
|
|
417
424
|
declare function runCli(argv: readonly string[], io: CliIo, deps: CliDeps): Promise<number>;
|
|
418
425
|
|
|
419
426
|
/**
|
|
@@ -431,6 +438,8 @@ interface ReconcileArgs {
|
|
|
431
438
|
json: boolean;
|
|
432
439
|
/** Comment on the PR when arming is skipped for want of a release token. */
|
|
433
440
|
tokenAdvisory: boolean;
|
|
441
|
+
/** Prose basename patterns for carry-over; absent means the default. */
|
|
442
|
+
prosePatterns?: string[];
|
|
434
443
|
}
|
|
435
444
|
type ParsedArgs = ReconcileArgs | {
|
|
436
445
|
command: 'help';
|
|
@@ -451,12 +460,18 @@ declare function computeState(facts: PullRequestFacts, policy: ReconcilePolicy):
|
|
|
451
460
|
/**
|
|
452
461
|
* The reconcile plan: the minimal label and auto-merge changes that converge a PR
|
|
453
462
|
* to its computed lifecycle state. Labels are output only — the core owns the
|
|
454
|
-
* lifecycle labels (plus `auto-merge enabled` in arming
|
|
455
|
-
* any other label. See docs/reconciler-design.md §Plan.
|
|
463
|
+
* lifecycle labels and `dependabot rebasing` (plus `auto-merge enabled` in arming
|
|
464
|
+
* mode) and never touches any other label. See docs/reconciler-design.md §Plan.
|
|
456
465
|
*/
|
|
457
466
|
declare const LIFECYCLE_LABELS: readonly ["approved", "changes requested", "escalation needed", "fix required", "ci failing", "review requested"];
|
|
458
467
|
type LifecycleLabel = (typeof LIFECYCLE_LABELS)[number];
|
|
459
468
|
declare const AUTO_MERGE_LABEL = "auto-merge enabled";
|
|
469
|
+
/**
|
|
470
|
+
* Shown while Dependabot rebases or recreates its PR, so workflows can hold off
|
|
471
|
+
* until the branch settles. Owned like the lifecycle labels, but independent of
|
|
472
|
+
* the state.
|
|
473
|
+
*/
|
|
474
|
+
declare const DEPENDABOT_REBASING_LABEL = "dependabot rebasing";
|
|
460
475
|
/** merge-safety's label for a PR whose base moved in a way that matters. Read, never written. */
|
|
461
476
|
declare const UPDATE_REQUIRED_LABEL = "update required";
|
|
462
477
|
/**
|
|
@@ -502,6 +517,11 @@ interface RepoSource {
|
|
|
502
517
|
interface MergeVerifier {
|
|
503
518
|
/** True only when the step is a verified clean merge; false on any doubt. */
|
|
504
519
|
verify(step: MergeStep): Promise<boolean>;
|
|
520
|
+
/**
|
|
521
|
+
* Paths that differ between two commits a `verify` call already fetched;
|
|
522
|
+
* undefined on any error, so the caller can fail closed.
|
|
523
|
+
*/
|
|
524
|
+
changedPaths(from: string, to: string): Promise<string[] | undefined>;
|
|
505
525
|
dispose(): Promise<void>;
|
|
506
526
|
}
|
|
507
527
|
/**
|
|
@@ -515,7 +535,9 @@ declare function createMergeVerifier(git: GitRunner, source: RepoSource): Promis
|
|
|
515
535
|
* Finds the head's clean ancestors: commits reached by walking back from the head
|
|
516
536
|
* through a chain of **verified clean base merges**, so reviews bound to them
|
|
517
537
|
* still describe the change being merged (docs/reconciler-design.md §Approval
|
|
518
|
-
* carry-over).
|
|
538
|
+
* carry-over). A clean merge in which both sides edited the same prose file also
|
|
539
|
+
* ends the chain: nothing automatic checks that the merged page still reads
|
|
540
|
+
* correctly. Fails closed: any doubt or error ends the chain, and the cost is
|
|
519
541
|
* an extra review, never a false approval.
|
|
520
542
|
*/
|
|
521
543
|
/** A longer chain of base merges than this is unusual enough to review again. */
|
|
@@ -527,6 +549,11 @@ interface LineageInput {
|
|
|
527
549
|
baseHeadSha: string;
|
|
528
550
|
source: RepoSource;
|
|
529
551
|
reviews: readonly ReviewData[];
|
|
552
|
+
/**
|
|
553
|
+
* Basename patterns for prose files (see src/lineage/prose.ts); default
|
|
554
|
+
* `DEFAULT_PROSE_PATTERNS`. Empty turns the prose-overlap check off.
|
|
555
|
+
*/
|
|
556
|
+
prosePatterns?: readonly string[];
|
|
530
557
|
}
|
|
531
558
|
interface Lineage {
|
|
532
559
|
cleanAncestors: string[];
|
|
@@ -560,6 +587,7 @@ interface GatherOptions {
|
|
|
560
587
|
lineage?: {
|
|
561
588
|
git: GitRunner;
|
|
562
589
|
token?: string;
|
|
590
|
+
prosePatterns?: readonly string[];
|
|
563
591
|
};
|
|
564
592
|
}
|
|
565
593
|
declare function gatherFacts(client: GitHubClient, pr: number, policy?: ReconcilePolicy, options?: GatherOptions): Promise<GatheredPullRequest>;
|
|
@@ -676,6 +704,14 @@ declare function formatSummary(target: ReconcileTarget, result: ReconcileResult)
|
|
|
676
704
|
declare const DEPENDABOT_REBASE_COMMAND = "@dependabot rebase";
|
|
677
705
|
/** Dependabot puts this in the PR body while a rebase runs, and removes it after. */
|
|
678
706
|
declare const DEPENDABOT_REBASING_NOTICE = "Dependabot is rebasing this PR";
|
|
707
|
+
/** The same notice for `@dependabot recreate`, which force-pushes a fresh branch. */
|
|
708
|
+
declare const DEPENDABOT_RECREATING_NOTICE = "Dependabot is recreating this PR";
|
|
709
|
+
/**
|
|
710
|
+
* Whether Dependabot says, in the PR body, that it is rebasing or recreating the
|
|
711
|
+
* branch right now. Only Dependabot's own notice counts — not our request marker,
|
|
712
|
+
* which means "asked", not "running".
|
|
713
|
+
*/
|
|
714
|
+
declare function isDependabotRebasing(prBody: string): boolean;
|
|
679
715
|
/** Hidden marker recording which head a request was made for. */
|
|
680
716
|
declare function rebaseRequestMarker(headSha: string): string;
|
|
681
717
|
/** The command on its own first line, as Dependabot parses it, then the marker. */
|
|
@@ -709,7 +745,7 @@ declare function executePlan(client: GitHubClient, target: PlanTarget, plan: Rec
|
|
|
709
745
|
* a consumer repo is missing; an existing label's color and description are never
|
|
710
746
|
* changed.
|
|
711
747
|
*/
|
|
712
|
-
declare const OWNED_LABEL_DEFINITIONS: Record<LifecycleLabel | typeof AUTO_MERGE_LABEL, LabelDefinition>;
|
|
748
|
+
declare const OWNED_LABEL_DEFINITIONS: Record<LifecycleLabel | typeof AUTO_MERGE_LABEL | typeof DEPENDABOT_REBASING_LABEL, LabelDefinition>;
|
|
713
749
|
|
|
714
750
|
/**
|
|
715
751
|
* The advisory comment posted when a PR is ready to arm or merge but no release
|
|
@@ -727,6 +763,22 @@ declare function buildTokenAdvisoryBody(skipped: 'arm' | 'merge'): string;
|
|
|
727
763
|
*/
|
|
728
764
|
declare function postTokenAdvisory(client: GitHubClient, pr: number, skipped: 'arm' | 'merge'): Promise<boolean>;
|
|
729
765
|
|
|
766
|
+
/**
|
|
767
|
+
* Which files are prose for approval carry-over: a clean base merge in which both
|
|
768
|
+
* sides edited the same prose file doesn't carry an approval over, because no CI
|
|
769
|
+
* check can tell whether two concurrent edits to a page still read correctly
|
|
770
|
+
* together (docs/reconciler-design.md §Approval carry-over).
|
|
771
|
+
*
|
|
772
|
+
* Patterns match a file's **basename** with `*` as the only wildcard; a leading
|
|
773
|
+
* `!` excludes. A file is prose when it matches some pattern and no exclusion.
|
|
774
|
+
*/
|
|
775
|
+
/** Every Markdown file except index pages, which take routine concurrent appends. */
|
|
776
|
+
declare const DEFAULT_PROSE_PATTERNS: readonly string[];
|
|
777
|
+
type ProseMatcher = (path: string) => boolean;
|
|
778
|
+
declare function proseMatcher(patterns: readonly string[]): ProseMatcher;
|
|
779
|
+
/** Prose paths changed on both sides of a merge, sorted. */
|
|
780
|
+
declare function overlappingProse(ours: readonly string[], theirs: readonly string[], isProse: ProseMatcher): string[];
|
|
781
|
+
|
|
730
782
|
/**
|
|
731
783
|
* Verdict parsing and trust. A review is a verdict when it carries a /review
|
|
732
784
|
* `skill-meta` marker with a verdict outcome, or (with no marker) a native
|
|
@@ -747,4 +799,4 @@ declare function isTrustedAuthor(author: ReviewAuthor, policy: ReconcilePolicy):
|
|
|
747
799
|
/** The latest verdict that counts for the PR's current head, if any. */
|
|
748
800
|
declare function currentVerdict(facts: PullRequestFacts, policy: ReconcilePolicy): Verdict | undefined;
|
|
749
801
|
|
|
750
|
-
export { ACTOR_TYPES, AUTO_MERGE_LABEL, type ActorType, type AutoMergeAction, BRANCH_UPDATERS, type BotEligibility, type BotPrCommit, type BotPrFacts, type BotPrType, type BranchUpdater, type ChangedFile, type CliDeps, type CliIo, type CollaboratorPermission, type CommitData, DEFAULT_SETTLE_MS, DEPENDABOT_LOGIN, DEPENDABOT_REBASE_COMMAND, DEPENDABOT_REBASING_NOTICE, DEPENDABOT_UPDATE_TYPES, type DependabotUpdateType, type GatherOptions, type GatheredPullRequest, GitHubApiError, type GitHubClient, type GitResult, type GitRunner, type HttpClientOptions, LIFECYCLE_LABELS, LIFECYCLE_STATES, type LabelDefinition, type LifecycleLabel, type LifecycleState, type Lineage, type LineageInput, MAX_CHAIN_STEPS, type MergeStep, type MergeVerifier, OWNED_LABEL_DEFINITIONS, PR_STATUSES, type ParsedArgs, type ParsedVerdict, type PlanTarget, type PullRequestData, type PullRequestFacts, type PullRequestStatus, RELEASE_PLEASE_BRANCH_PREFIX, RELEASE_TOKEN_DOCS_URL, REPO_PERMISSIONS, REVIEW_STATES, type ReconcileArgs, type ReconcileJson, type ReconcileOptions, type ReconcilePlan, type ReconcilePolicy, type ReconcileResult, type ReconcileTarget, type ReleaseActions, type ReleaseDiffVerdict, type RepoPermission, type RepoSource, type ReviewAuthor, type ReviewData, type ReviewFact, type ReviewState, SCHEMA_VERSION, type SettleOptions, TOKEN_ADVISORY_MARKER, UPDATE_REQUIRED_LABEL, USAGE, type UpdateAction, VERDICTS, type Verdict, buildRebaseRequestBody, buildTokenAdvisoryBody, classifyBotPr, classifyReleaseDiff, computeState, createGitRunner, createHttpClient, createMergeVerifier, currentVerdict, executePlan, formatSummary, gatherFacts, gatherLineage, isApiStatus, isRebasePending, isTrustedAuthor, isVersionOnlyPatch, parseArgs, parseDependabotUpdateType, parseGitVersion, parseVerdict, planReconcile, postTokenAdvisory, rebaseRequestMarker, reconcilePullRequest, runCli, toReconcileJson, withoutReleaseActions };
|
|
802
|
+
export { ACTOR_TYPES, AUTO_MERGE_LABEL, type ActorType, type AutoMergeAction, BRANCH_UPDATERS, type BotEligibility, type BotPrCommit, type BotPrFacts, type BotPrType, type BranchUpdater, type ChangedFile, type CliDeps, type CliIo, type CollaboratorPermission, type CommitData, DEFAULT_PROSE_PATTERNS, DEFAULT_SETTLE_MS, DEPENDABOT_LOGIN, DEPENDABOT_REBASE_COMMAND, DEPENDABOT_REBASING_LABEL, DEPENDABOT_REBASING_NOTICE, DEPENDABOT_RECREATING_NOTICE, DEPENDABOT_UPDATE_TYPES, type DependabotUpdateType, type GatherOptions, type GatheredPullRequest, GitHubApiError, type GitHubClient, type GitResult, type GitRunner, type HttpClientOptions, LIFECYCLE_LABELS, LIFECYCLE_STATES, type LabelDefinition, type LifecycleLabel, type LifecycleState, type Lineage, type LineageInput, MAX_CHAIN_STEPS, type MergeStep, type MergeVerifier, OWNED_LABEL_DEFINITIONS, PR_STATUSES, type ParsedArgs, type ParsedVerdict, type PlanTarget, type ProseMatcher, type PullRequestData, type PullRequestFacts, type PullRequestStatus, RELEASE_PLEASE_BRANCH_PREFIX, RELEASE_TOKEN_DOCS_URL, REPO_PERMISSIONS, REVIEW_STATES, type ReconcileArgs, type ReconcileJson, type ReconcileOptions, type ReconcilePlan, type ReconcilePolicy, type ReconcileResult, type ReconcileTarget, type ReleaseActions, type ReleaseDiffVerdict, type RepoPermission, type RepoSource, type ReviewAuthor, type ReviewData, type ReviewFact, type ReviewState, SCHEMA_VERSION, type SettleOptions, TOKEN_ADVISORY_MARKER, UPDATE_REQUIRED_LABEL, USAGE, type UpdateAction, VERDICTS, type Verdict, buildRebaseRequestBody, buildTokenAdvisoryBody, classifyBotPr, classifyReleaseDiff, computeState, createGitRunner, createHttpClient, createMergeVerifier, currentVerdict, executePlan, formatSummary, gatherFacts, gatherLineage, isApiStatus, isDependabotRebasing, isRebasePending, isTrustedAuthor, isVersionOnlyPatch, overlappingProse, parseArgs, parseDependabotUpdateType, parseGitVersion, parseVerdict, planReconcile, postTokenAdvisory, proseMatcher, rebaseRequestMarker, reconcilePullRequest, runCli, toReconcileJson, withoutReleaseActions };
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,13 @@ import {
|
|
|
2
2
|
ACTOR_TYPES,
|
|
3
3
|
AUTO_MERGE_LABEL,
|
|
4
4
|
BRANCH_UPDATERS,
|
|
5
|
+
DEFAULT_PROSE_PATTERNS,
|
|
5
6
|
DEFAULT_SETTLE_MS,
|
|
6
7
|
DEPENDABOT_LOGIN,
|
|
7
8
|
DEPENDABOT_REBASE_COMMAND,
|
|
9
|
+
DEPENDABOT_REBASING_LABEL,
|
|
8
10
|
DEPENDABOT_REBASING_NOTICE,
|
|
11
|
+
DEPENDABOT_RECREATING_NOTICE,
|
|
9
12
|
DEPENDABOT_UPDATE_TYPES,
|
|
10
13
|
GitHubApiError,
|
|
11
14
|
LIFECYCLE_LABELS,
|
|
@@ -36,29 +39,35 @@ import {
|
|
|
36
39
|
gatherFacts,
|
|
37
40
|
gatherLineage,
|
|
38
41
|
isApiStatus,
|
|
42
|
+
isDependabotRebasing,
|
|
39
43
|
isRebasePending,
|
|
40
44
|
isTrustedAuthor,
|
|
41
45
|
isVersionOnlyPatch,
|
|
46
|
+
overlappingProse,
|
|
42
47
|
parseArgs,
|
|
43
48
|
parseDependabotUpdateType,
|
|
44
49
|
parseGitVersion,
|
|
45
50
|
parseVerdict,
|
|
46
51
|
planReconcile,
|
|
47
52
|
postTokenAdvisory,
|
|
53
|
+
proseMatcher,
|
|
48
54
|
rebaseRequestMarker,
|
|
49
55
|
reconcilePullRequest,
|
|
50
56
|
runCli,
|
|
51
57
|
toReconcileJson,
|
|
52
58
|
withoutReleaseActions
|
|
53
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-4GTGNP35.js";
|
|
54
60
|
export {
|
|
55
61
|
ACTOR_TYPES,
|
|
56
62
|
AUTO_MERGE_LABEL,
|
|
57
63
|
BRANCH_UPDATERS,
|
|
64
|
+
DEFAULT_PROSE_PATTERNS,
|
|
58
65
|
DEFAULT_SETTLE_MS,
|
|
59
66
|
DEPENDABOT_LOGIN,
|
|
60
67
|
DEPENDABOT_REBASE_COMMAND,
|
|
68
|
+
DEPENDABOT_REBASING_LABEL,
|
|
61
69
|
DEPENDABOT_REBASING_NOTICE,
|
|
70
|
+
DEPENDABOT_RECREATING_NOTICE,
|
|
62
71
|
DEPENDABOT_UPDATE_TYPES,
|
|
63
72
|
GitHubApiError,
|
|
64
73
|
LIFECYCLE_LABELS,
|
|
@@ -89,15 +98,18 @@ export {
|
|
|
89
98
|
gatherFacts,
|
|
90
99
|
gatherLineage,
|
|
91
100
|
isApiStatus,
|
|
101
|
+
isDependabotRebasing,
|
|
92
102
|
isRebasePending,
|
|
93
103
|
isTrustedAuthor,
|
|
94
104
|
isVersionOnlyPatch,
|
|
105
|
+
overlappingProse,
|
|
95
106
|
parseArgs,
|
|
96
107
|
parseDependabotUpdateType,
|
|
97
108
|
parseGitVersion,
|
|
98
109
|
parseVerdict,
|
|
99
110
|
planReconcile,
|
|
100
111
|
postTokenAdvisory,
|
|
112
|
+
proseMatcher,
|
|
101
113
|
rebaseRequestMarker,
|
|
102
114
|
reconcilePullRequest,
|
|
103
115
|
runCli,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmartz/pr-lifecycle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"description": "Event-driven PR lifecycle reconciler: recomputes a pull request's lifecycle labels from its current facts on every GitHub event and arms native auto-merge on approval.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|