From 46127f22f5181c27e5cdb55beefedae0877cc196 Mon Sep 17 00:00:00 2001 From: William Easton Date: Sun, 8 Mar 2026 21:12:55 -0500 Subject: [PATCH] Add include/exclude label filters to conflicted PR action. Support comma-separated include-labels and exclude-labels (with opt-out-label kept as deprecated compatibility input) so callers can scope or suppress PR selection more flexibly. Made-with: Cursor --- .../get-prs-with-merge-conflicts/action.yml | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/actions/get-prs-with-merge-conflicts/action.yml b/.github/actions/get-prs-with-merge-conflicts/action.yml index cf486ef6..e7db0d8a 100644 --- a/.github/actions/get-prs-with-merge-conflicts/action.yml +++ b/.github/actions/get-prs-with-merge-conflicts/action.yml @@ -9,8 +9,16 @@ inputs: description: "Seconds to wait before checking merge conflict state" required: false default: "60" + include-labels: + description: "Only include PRs that have at least one of these labels (comma-separated). Empty means no include filter." + required: false + default: "" + exclude-labels: + description: "Exclude PRs that have any of these labels (comma-separated)." + required: false + default: "" opt-out-label: - description: "Skip PRs with this label" + description: "Deprecated: exclude PRs with this label (kept for backward compatibility)" required: false default: "no-address-merge-conflicts" @@ -38,7 +46,19 @@ runs: github-token: ${{ inputs.github-token }} script: | const [owner, repo] = process.env.REPOSITORY.split("/"); - const optOutLabel = core.getInput("opt-out-label"); + const parseLabelSet = (input) => + new Set( + input + .split(",") + .map((label) => label.trim().toLowerCase()) + .filter((label) => label.length > 0), + ); + const includeLabels = parseLabelSet(core.getInput("include-labels")); + const excludeLabels = parseLabelSet(core.getInput("exclude-labels")); + const optOutLabel = core.getInput("opt-out-label").trim().toLowerCase(); + if (optOutLabel) { + excludeLabels.add(optOutLabel); + } const result = await github.graphql( `query($owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { @@ -59,14 +79,18 @@ runs: const prs = []; for (const pr of result.repository.pullRequests.nodes) { - const labels = new Set((pr.labels?.nodes ?? []).map((l) => l.name)); - const optedIn = !labels.has(optOutLabel); + const labels = new Set((pr.labels?.nodes ?? []).map((l) => l.name.toLowerCase())); + const matchesInclude = + includeLabels.size === 0 || [...includeLabels].some((label) => labels.has(label)); + const matchesExclude = [...excludeLabels].some((label) => labels.has(label)); const conflicted = pr.mergeStateStatus === "DIRTY"; - if (!pr.isDraft && optedIn && conflicted) { + if (!pr.isDraft && matchesInclude && !matchesExclude && conflicted) { prs.push({ number: pr.number }); } } - core.info(`Found ${prs.length} conflicted PR(s) without opt-out label '${optOutLabel}'.`); + core.info( + `Found ${prs.length} conflicted PR(s). includeLabels=${JSON.stringify([...includeLabels])}, excludeLabels=${JSON.stringify([...excludeLabels])}`, + ); core.setOutput("prs", JSON.stringify(prs)); core.setOutput("count", String(prs.length));