


























@@ -1,3 +1,4 @@
1+// Finds duplicate PRs after merge and closes overlapping candidates.
12import { execFileSync } from "node:child_process";
23import { pathToFileURL } from "node:url";
34@@ -10,6 +11,9 @@ Closes explicit duplicate PRs after a landed PR, after verifying the landed PR i
1011each duplicate has either a shared referenced issue or overlapping changed hunks. Defaults to dry-run.`;
1112}
121314+/**
15+ * Parses comma-separated PR numbers from CLI/env input.
16+ */
1317export function parsePrNumberList(value) {
1418return [
1519 ...new Set(
@@ -27,6 +31,9 @@ export function parsePrNumberList(value) {
2731];
2832}
293334+/**
35+ * Parses duplicate PR close workflow arguments.
36+ */
3037export function parseArgs(argv, env = process.env) {
3138const args = {
3239apply: false,
@@ -109,6 +116,9 @@ function intersectSets(left, right) {
109116return [...left].filter((value) => right.has(value));
110117}
111118119+/**
120+ * Parses changed hunk ranges from unified diff text.
121+ */
112122export function parseUnifiedDiffRanges(diffText) {
113123const ranges = new Map();
114124let currentPath = null;
@@ -136,6 +146,9 @@ export function parseUnifiedDiffRanges(diffText) {
136146return ranges;
137147}
138148149+/**
150+ * Reports whether two PR diffs touch overlapping hunks.
151+ */
139152export function hasOverlappingHunks(leftRanges, rightRanges) {
140153for (const [path, left] of leftRanges) {
141154const right = rightRanges.get(path) ?? [];
@@ -182,6 +195,9 @@ Evidence: ${formatEvidence(evidence)}.
182195Closing #${candidate.number} as a duplicate.`;
183196}
184197198+/**
199+ * Builds the close/skip plan for duplicate PR candidates.
200+ */
185201export function buildDuplicateClosePlan({ candidates, diffs, landed, repo }) {
186202if (landed.state !== "MERGED" || !landed.mergedAt) {
187203throw new Error(`#${landed.number} is not merged`);
@@ -246,6 +262,9 @@ function loadDiff(repo, number, runGh) {
246262return runGh(["pr", "diff", String(number), "--repo", repo, "--color=never"]);
247263}
248264265+/**
266+ * Applies labels/comments/closes for planned duplicate PR actions.
267+ */
249268export function applyClosePlan({ labels = DEFAULT_LABELS, plan, repo, runGh }) {
250269for (const item of plan) {
251270if (item.action !== "close") {
@@ -261,6 +280,9 @@ export function applyClosePlan({ labels = DEFAULT_LABELS, plan, repo, runGh }) {
261280}
262281}
263282283+/**
284+ * Runs the duplicate PR close workflow.
285+ */
264286export function runDuplicateCloseWorkflow(args, runGh = defaultRunGh) {
265287const landed = loadPr(args.repo, args.landedPr, runGh);
266288const candidates = args.duplicates.map((number) => loadPr(args.repo, number, runGh));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。