pr-shepherd 0.26.1 → 0.26.2
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-plugin/plugin.json +1 -1
- package/bin/checks/classify.mjs +4 -3
- package/bin/cli/iterate-formatter.mjs +8 -0
- package/bin/cli/iterate-lean.mjs +3 -0
- package/bin/commands/check-status.mjs +7 -2
- package/bin/commands/check.mjs +1 -0
- package/bin/commands/iterate/index.mjs +2 -0
- package/package.json +1 -1
- package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
package/bin/checks/classify.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export function classifyChecks(checks) {
|
|
|
20
20
|
const config = loadConfig();
|
|
21
21
|
const relevantEvents = new Set(config.checks.ciTriggerEvents);
|
|
22
22
|
const isIgnored = buildIgnoreMatcher(config.ignoreChecks ?? []);
|
|
23
|
-
return checks.
|
|
23
|
+
return checks.map((c) => isIgnored(c.name) ? { ...c, category: "ignored" } : classify(c, relevantEvents));
|
|
24
24
|
}
|
|
25
25
|
function buildIgnoreMatcher(patterns) {
|
|
26
26
|
if (patterns.length === 0)
|
|
@@ -51,7 +51,7 @@ function classify(check, relevantEvents) {
|
|
|
51
51
|
}
|
|
52
52
|
/** Compute a high-level CI verdict from a list of classified checks. */
|
|
53
53
|
export function getCiVerdict(classified) {
|
|
54
|
-
const relevant = classified.filter((c) => c.category !== "filtered" && c.category !== "skipped");
|
|
54
|
+
const relevant = classified.filter((c) => c.category !== "filtered" && c.category !== "skipped" && c.category !== "ignored");
|
|
55
55
|
const anyInProgress = relevant.some((c) => c.category === "in_progress");
|
|
56
56
|
const anyFailing = relevant.some((c) => c.category === "failing");
|
|
57
57
|
// When there are no relevant checks (e.g. docs-only PR where all checks are filtered/skipped),
|
|
@@ -59,5 +59,6 @@ export function getCiVerdict(classified) {
|
|
|
59
59
|
const allPassed = !anyInProgress && !anyFailing;
|
|
60
60
|
const hasChecks = relevant.length > 0;
|
|
61
61
|
const filteredNames = classified.filter((c) => c.category === "filtered").map((c) => c.name);
|
|
62
|
-
|
|
62
|
+
const ignoredNames = Array.from(new Set(classified.filter((c) => c.category === "ignored").map((c) => c.name)));
|
|
63
|
+
return { allPassed, hasChecks, anyInProgress, anyFailing, filteredNames, ignoredNames };
|
|
63
64
|
}
|
|
@@ -112,6 +112,10 @@ export function formatIterateResult(result, opts) {
|
|
|
112
112
|
const headerLines = [heading, "", baseLine, summaryLine];
|
|
113
113
|
if (requiredLine)
|
|
114
114
|
headerLines.push(requiredLine);
|
|
115
|
+
if (result.ignoredNames && result.ignoredNames.length > 0) {
|
|
116
|
+
const names = result.ignoredNames.map((n) => "`" + n + "`").join(", ");
|
|
117
|
+
headerLines.push(`**ignored** ${names}`);
|
|
118
|
+
}
|
|
115
119
|
const activityLine = formatActivityLine(result);
|
|
116
120
|
if (activityLine)
|
|
117
121
|
headerLines.push(activityLine);
|
|
@@ -133,6 +137,10 @@ export function formatIterateResult(result, opts) {
|
|
|
133
137
|
const cancelHeaderLines = [`${heading} — ${result.reason}`, "", baseLine, summaryLine];
|
|
134
138
|
if (requiredLine)
|
|
135
139
|
cancelHeaderLines.push(requiredLine);
|
|
140
|
+
if (result.ignoredNames && result.ignoredNames.length > 0) {
|
|
141
|
+
const ignoredStr = result.ignoredNames.map((n) => "`" + n + "`").join(", ");
|
|
142
|
+
cancelHeaderLines.push(`**ignored** ${ignoredStr}`);
|
|
143
|
+
}
|
|
136
144
|
if (activityLine)
|
|
137
145
|
cancelHeaderLines.push(activityLine);
|
|
138
146
|
return joinSections([
|
package/bin/cli/iterate-lean.mjs
CHANGED
|
@@ -55,6 +55,9 @@ export function projectIterateLean(result, opts) {
|
|
|
55
55
|
...((result.inProgressChecks?.length ?? 0) > 0 && {
|
|
56
56
|
inProgressChecks: result.inProgressChecks,
|
|
57
57
|
}),
|
|
58
|
+
...((result.ignoredNames?.length ?? 0) > 0 && {
|
|
59
|
+
ignoredNames: result.ignoredNames,
|
|
60
|
+
}),
|
|
58
61
|
};
|
|
59
62
|
switch (result.action) {
|
|
60
63
|
case "wait":
|
|
@@ -13,13 +13,18 @@ export function computeStatus(verdict, unresolvedThreads, unresolvedComments, me
|
|
|
13
13
|
// is BLOCKED (review pending, insufficient approvals, branch-protection rule, etc.).
|
|
14
14
|
// Requires hasChecks so that a PR with zero relevant checks (CI never started, or all
|
|
15
15
|
// filtered/skipped) doesn't prematurely trigger READY before any check has reported.
|
|
16
|
+
// Exception: UNSTABLE with ignored checks — UNSTABLE means only non-required checks are
|
|
17
|
+
// pending/failing, and if those are all ignored the handoff is safe even with no other checks.
|
|
18
|
+
// BLOCKED is excluded from the ignoredNames extension: BLOCKED can mean required checks haven't
|
|
19
|
+
// started, and handing off prematurely there risks a broken merge attempt.
|
|
16
20
|
// blockingBotReviewInProgress is still excluded — a bot review is shepherd's problem, not a hand-off.
|
|
21
|
+
const hasRelevantPassingChecks = verdict.hasChecks || (mergeStatus.status === "UNSTABLE" && verdict.ignoredNames.length > 0);
|
|
17
22
|
if (verdict.allPassed &&
|
|
18
|
-
|
|
23
|
+
hasRelevantPassingChecks &&
|
|
19
24
|
unresolvedThreads === 0 &&
|
|
20
25
|
unresolvedComments === 0 &&
|
|
21
26
|
changesRequestedReviews === 0 &&
|
|
22
|
-
mergeStatus.status === "BLOCKED" &&
|
|
27
|
+
(mergeStatus.status === "BLOCKED" || mergeStatus.status === "UNSTABLE") &&
|
|
23
28
|
!mergeStatus.blockingBotReviewInProgress) {
|
|
24
29
|
return "READY";
|
|
25
30
|
}
|
package/bin/commands/check.mjs
CHANGED
|
@@ -124,6 +124,7 @@ export async function runCheck(opts) {
|
|
|
124
124
|
filtered,
|
|
125
125
|
filteredNames: verdict.filteredNames,
|
|
126
126
|
blockedByFilteredCheck,
|
|
127
|
+
...(verdict.ignoredNames.length > 0 && { ignoredNames: verdict.ignoredNames }),
|
|
127
128
|
},
|
|
128
129
|
threads: {
|
|
129
130
|
actionable: threadVisibility.activeThreads,
|
|
@@ -50,6 +50,7 @@ export async function runIterate(opts) {
|
|
|
50
50
|
branchProtection: report.branchProtection,
|
|
51
51
|
checks: buildRelevantChecks(report),
|
|
52
52
|
inProgressChecks: buildActiveChecks(report),
|
|
53
|
+
...(report.checks.ignoredNames?.length ? { ignoredNames: report.checks.ignoredNames } : {}),
|
|
53
54
|
activity: report.activity,
|
|
54
55
|
action: "cancel",
|
|
55
56
|
reason: report.mergeStatus.state === "MERGED" ? "merged" : "closed",
|
|
@@ -93,6 +94,7 @@ export async function runIterate(opts) {
|
|
|
93
94
|
branchProtection: report.branchProtection,
|
|
94
95
|
checks: buildRelevantChecks(report),
|
|
95
96
|
inProgressChecks: buildActiveChecks(report),
|
|
97
|
+
...(report.checks.ignoredNames?.length ? { ignoredNames: report.checks.ignoredNames } : {}),
|
|
96
98
|
activity: report.activity,
|
|
97
99
|
};
|
|
98
100
|
if (readyState.shouldCancel) {
|
package/package.json
CHANGED