diff --git a/slash-commands/README.md b/slash-commands/README.md index 6dadcc4..31aebf6 100644 --- a/slash-commands/README.md +++ b/slash-commands/README.md @@ -221,6 +221,7 @@ jobs: | `project-number` | | _(empty)_ | Org-level Project (V2) number to update on accept. Empty = disabled. Adds the PR to the project if not already present. Requires the App to have **Organization Projects: write**. | | `project-status-field` | | `Status` | Name of the single-select field to set on accept. | | `project-accepted-option` | | `Accepted` | Name of the option to set in the status field on accept. | +| `project-gate-label` | | _(empty)_ | If set, only update the project status when the PR has this label (e.g. `SEP`). Empty = update for every accepted PR. Label removal via `remove-labels-on-accept` is not gated — it always runs. | | `stageblog-workflow` | | _(empty)_ | Workflow file name (e.g. `stage-blog.yml`) to dispatch when `/stageblog` is invoked. Empty = command disabled. The workflow must accept `pr_number` and `head_sha` string inputs. | | `stageblog-paths` | | `blog/**` | Comma-separated CODEOWNERS-style glob patterns. `/stageblog` is refused if no changed file matches. | diff --git a/slash-commands/action.yml b/slash-commands/action.yml index 60794fd..d560e56 100644 --- a/slash-commands/action.yml +++ b/slash-commands/action.yml @@ -87,6 +87,12 @@ inputs: description: Name of the option to set in the status field on accept required: false default: Accepted + project-gate-label: + description: >- + If set, only update the project status when the PR has this label. + Leave empty to update the project for every accepted PR. + required: false + default: '' stageblog-workflow: description: >- Workflow file name (e.g. stage-blog.yml) to dispatch when /stageblog is @@ -133,6 +139,7 @@ runs: PROJECT_NUMBER: ${{ inputs.project-number }} PROJECT_STATUS_FIELD: ${{ inputs.project-status-field }} PROJECT_ACCEPTED_OPTION: ${{ inputs.project-accepted-option }} + PROJECT_GATE_LABEL: ${{ inputs.project-gate-label }} STAGEBLOG_WORKFLOW: ${{ inputs.stageblog-workflow }} STAGEBLOG_PATHS: ${{ inputs.stageblog-paths }} with: diff --git a/slash-commands/main.js b/slash-commands/main.js index 3c314e2..f2da594 100644 --- a/slash-commands/main.js +++ b/slash-commands/main.js @@ -20,6 +20,7 @@ module.exports = async ({ github, context, core }) => { const projectNumber = parseInt(process.env.PROJECT_NUMBER, 10) || 0; const projectStatusField = process.env.PROJECT_STATUS_FIELD; const projectAcceptedOption = process.env.PROJECT_ACCEPTED_OPTION; + const projectGateLabel = process.env.PROJECT_GATE_LABEL; const reviewMarker = ''; function setResult(result, actor) { @@ -265,11 +266,15 @@ module.exports = async ({ github, context, core }) => { core.info(`Auto-merge disable skipped: ${e.message}`); } } - async function onAccept(prNumber, prNodeId) { + async function onAccept(pr) { for (const label of removeLabelsOnAccept) { - await removeLabel(prNumber, label); + await removeLabel(pr.number, label); } if (!projectNumber) return; + if (projectGateLabel && !pr.labels.some(l => l.name === projectGateLabel)) { + core.info(`Skipping project update — PR lacks '${projectGateLabel}' label`); + return; + } try { const lookup = await github.graphql( `query($org: String!, $num: Int!, $field: String!) { @@ -304,7 +309,7 @@ module.exports = async ({ github, context, core }) => { item { id } } }`, - { proj: project.id, pr: prNodeId }, + { proj: project.id, pr: pr.node_id }, ); const itemId = add.addProjectV2ItemById.item.id; await github.graphql( @@ -376,7 +381,7 @@ module.exports = async ({ github, context, core }) => { return setResult('force-unauthorized', actor); } await addLabel(prNumber, approvedLabel); - await onAccept(prNumber, pr.node_id); + await onAccept(pr); if (submitReview) await submitApproval(prNumber, actor, true); await react(commentId, '+1'); await enableAutoMerge(pr.node_id, prNumber); @@ -447,7 +452,7 @@ module.exports = async ({ github, context, core }) => { // Execute if (cmd === 'lgtm' && !cancel) { await addLabel(prNumber, approvedLabel); - await onAccept(prNumber, pr.node_id); + await onAccept(pr); if (submitReview) await submitApproval(prNumber, actor); await react(commentId, '+1'); await enableAutoMerge(pr.node_id, prNumber);