merge-steward 0.35.3 → 0.35.4
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/github/pr-client.d.ts +7 -0
- package/dist/github/pr-client.js +29 -0
- package/dist/interfaces.d.ts +9 -0
- package/dist/reconciler-merge.js +23 -3
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -33,6 +33,13 @@ export declare class GitHubPRClient implements GitHubPRApi {
|
|
|
33
33
|
headSha: string;
|
|
34
34
|
baseBranch: string;
|
|
35
35
|
}>>;
|
|
36
|
+
listOpenPRsByBase(baseBranch: string): Promise<Array<{
|
|
37
|
+
number: number;
|
|
38
|
+
branch: string;
|
|
39
|
+
headSha: string;
|
|
40
|
+
baseBranch: string;
|
|
41
|
+
}>>;
|
|
42
|
+
setBaseBranch(prNumber: number, baseBranch: string): Promise<void>;
|
|
36
43
|
deleteBranch(prNumber: number): Promise<void>;
|
|
37
44
|
findPRByBranch(branch: string): Promise<number | null>;
|
|
38
45
|
listLabels(prNumber: number): Promise<string[]>;
|
package/dist/github/pr-client.js
CHANGED
|
@@ -117,6 +117,35 @@ export class GitHubPRClient {
|
|
|
117
117
|
throw new Error("GitHub returned malformed open PR data", { cause: error });
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
async listOpenPRsByBase(baseBranch) {
|
|
121
|
+
const result = await exec("gh", [
|
|
122
|
+
"api", "--method", "GET",
|
|
123
|
+
`repos/${this.repoFullName}/pulls`,
|
|
124
|
+
"-f", "state=open",
|
|
125
|
+
"-f", `base=${baseBranch}`,
|
|
126
|
+
"-f", "per_page=100",
|
|
127
|
+
"--paginate", "--slurp",
|
|
128
|
+
], { githubRepoFullName: this.repoFullName });
|
|
129
|
+
try {
|
|
130
|
+
const pages = JSON.parse(result.stdout);
|
|
131
|
+
return pages.flat().map((pr) => ({
|
|
132
|
+
number: pr.number,
|
|
133
|
+
branch: pr.head.ref,
|
|
134
|
+
headSha: pr.head.sha,
|
|
135
|
+
baseBranch: pr.base.ref,
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new Error("GitHub returned malformed stack child PR data", { cause: error });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async setBaseBranch(prNumber, baseBranch) {
|
|
143
|
+
await exec("gh", [
|
|
144
|
+
"api", "--method", "PATCH",
|
|
145
|
+
`repos/${this.repoFullName}/pulls/${prNumber}`,
|
|
146
|
+
"-f", `base=${baseBranch}`,
|
|
147
|
+
], { githubRepoFullName: this.repoFullName });
|
|
148
|
+
}
|
|
120
149
|
async deleteBranch(prNumber) {
|
|
121
150
|
const status = await this.getStatus(prNumber);
|
|
122
151
|
await exec("gh", [
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -57,8 +57,17 @@ export interface GitHubPRApi {
|
|
|
57
57
|
headSha: string;
|
|
58
58
|
baseBranch: string;
|
|
59
59
|
}>>;
|
|
60
|
+
/** List every open PR whose current base is the given branch. */
|
|
61
|
+
listOpenPRsByBase(baseBranch: string): Promise<Array<{
|
|
62
|
+
number: number;
|
|
63
|
+
branch: string;
|
|
64
|
+
headSha: string;
|
|
65
|
+
baseBranch: string;
|
|
66
|
+
}>>;
|
|
60
67
|
/** Find the open PR number for a branch, or null if none exists. */
|
|
61
68
|
findPRByBranch(branch: string): Promise<number | null>;
|
|
69
|
+
/** Retarget a PR before deleting a branch used as its current base. */
|
|
70
|
+
setBaseBranch(prNumber: number, baseBranch: string): Promise<void>;
|
|
62
71
|
/** Delete the PR's head branch from the remote (best-effort cleanup). */
|
|
63
72
|
deleteBranch(prNumber: number): Promise<void>;
|
|
64
73
|
/** List open PRs with a specific label (for startup scan). */
|
package/dist/reconciler-merge.js
CHANGED
|
@@ -418,15 +418,35 @@ export async function deletePrBranchAfterGitHubMarksMerged(ctx, entry, options =
|
|
|
418
418
|
const attempts = options.attempts ?? DEFAULT_PR_MERGED_POLL_ATTEMPTS;
|
|
419
419
|
const delayMs = options.delayMs ?? DEFAULT_PR_MERGED_POLL_DELAY_MS;
|
|
420
420
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
421
|
-
let
|
|
421
|
+
let mergedStatus = null;
|
|
422
422
|
try {
|
|
423
|
-
|
|
423
|
+
const status = await ctx.github.getStatus(entry.prNumber);
|
|
424
|
+
if (status.merged)
|
|
425
|
+
mergedStatus = status;
|
|
424
426
|
}
|
|
425
427
|
catch {
|
|
426
428
|
// Keep polling briefly. If GitHub is unavailable, branch cleanup is
|
|
427
429
|
// cosmetic; preserving correct PR merge classification matters more.
|
|
428
430
|
}
|
|
429
|
-
if (
|
|
431
|
+
if (mergedStatus) {
|
|
432
|
+
let children;
|
|
433
|
+
try {
|
|
434
|
+
children = await ctx.github.listOpenPRsByBase(mergedStatus.branch);
|
|
435
|
+
for (const child of children) {
|
|
436
|
+
await ctx.github.setBaseBranch(child.number, ctx.baseBranch);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
emit(ctx, entry, "pr_branch_cleanup_deferred", {
|
|
441
|
+
detail: "preserving the merged PR branch because stacked children could not be safely retargeted",
|
|
442
|
+
});
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (children.length > 0) {
|
|
446
|
+
emit(ctx, entry, "stack_children_retargeted", {
|
|
447
|
+
detail: `retargeted child PR ${children.map((child) => `#${child.number}`).join(", ")} to ${ctx.baseBranch} before deleting ${mergedStatus.branch}`,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
430
450
|
try {
|
|
431
451
|
await ctx.github.deleteBranch(entry.prNumber);
|
|
432
452
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -243,7 +243,7 @@ export interface QueueConfig {
|
|
|
243
243
|
flakyRetries: number;
|
|
244
244
|
pollIntervalMs: number;
|
|
245
245
|
}
|
|
246
|
-
export type ReconcileAction = "promoted" | "fetch_started" | "main_broken" | "branch_mismatch" | "candidate_selected" | "integration_build_started" | "integration_build_succeeded" | "integration_build_conflict" | "ci_triggered" | "ci_pending" | "ci_passed" | "ci_failed" | "ci_flaky_retry" | "merge_revalidating" | "head_candidate_landed" | "merge_succeeded" | "merge_rejected" | "merge_external" | "policy_changed" | "evicted" | "invalidated" | "retry_gated" | "budget_exhausted" | "merge_waiting_approval" | "open_pr_ancestry_blocked" | "stack_dependency_waiting" | "merge_waiting_recognition" | "merge_waiting_main" | "main_pending_bypassed" | "pr_branch_cleanup_deferred" | "pr_branch_cleanup_failed" | "sanitized_closed" | "sanitized_duplicate" | "post_merge_verification_started" | "post_merge_verification_completed" | "queue_label_synced" | "branch_unreachable";
|
|
246
|
+
export type ReconcileAction = "promoted" | "fetch_started" | "main_broken" | "branch_mismatch" | "candidate_selected" | "integration_build_started" | "integration_build_succeeded" | "integration_build_conflict" | "ci_triggered" | "ci_pending" | "ci_passed" | "ci_failed" | "ci_flaky_retry" | "merge_revalidating" | "head_candidate_landed" | "merge_succeeded" | "merge_rejected" | "merge_external" | "policy_changed" | "evicted" | "invalidated" | "retry_gated" | "budget_exhausted" | "merge_waiting_approval" | "open_pr_ancestry_blocked" | "stack_dependency_waiting" | "merge_waiting_recognition" | "merge_waiting_main" | "main_pending_bypassed" | "pr_branch_cleanup_deferred" | "pr_branch_cleanup_failed" | "stack_children_retargeted" | "sanitized_closed" | "sanitized_duplicate" | "post_merge_verification_started" | "post_merge_verification_completed" | "queue_label_synced" | "branch_unreachable";
|
|
247
247
|
export interface ReconcileEvent {
|
|
248
248
|
at: string;
|
|
249
249
|
entryId: string;
|