Merge pull request #18 from modelcontextprotocol/den/lgtm-project-gate-label
slash-commands: gate project update on a required PR label
This commit is contained in:
commit
913cf32c13
3 changed files with 18 additions and 5 deletions
|
|
@ -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-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-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-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-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. |
|
| `stageblog-paths` | | `blog/**` | Comma-separated CODEOWNERS-style glob patterns. `/stageblog` is refused if no changed file matches. |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,12 @@ inputs:
|
||||||
description: Name of the option to set in the status field on accept
|
description: Name of the option to set in the status field on accept
|
||||||
required: false
|
required: false
|
||||||
default: Accepted
|
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:
|
stageblog-workflow:
|
||||||
description: >-
|
description: >-
|
||||||
Workflow file name (e.g. stage-blog.yml) to dispatch when /stageblog is
|
Workflow file name (e.g. stage-blog.yml) to dispatch when /stageblog is
|
||||||
|
|
@ -133,6 +139,7 @@ runs:
|
||||||
PROJECT_NUMBER: ${{ inputs.project-number }}
|
PROJECT_NUMBER: ${{ inputs.project-number }}
|
||||||
PROJECT_STATUS_FIELD: ${{ inputs.project-status-field }}
|
PROJECT_STATUS_FIELD: ${{ inputs.project-status-field }}
|
||||||
PROJECT_ACCEPTED_OPTION: ${{ inputs.project-accepted-option }}
|
PROJECT_ACCEPTED_OPTION: ${{ inputs.project-accepted-option }}
|
||||||
|
PROJECT_GATE_LABEL: ${{ inputs.project-gate-label }}
|
||||||
STAGEBLOG_WORKFLOW: ${{ inputs.stageblog-workflow }}
|
STAGEBLOG_WORKFLOW: ${{ inputs.stageblog-workflow }}
|
||||||
STAGEBLOG_PATHS: ${{ inputs.stageblog-paths }}
|
STAGEBLOG_PATHS: ${{ inputs.stageblog-paths }}
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ module.exports = async ({ github, context, core }) => {
|
||||||
const projectNumber = parseInt(process.env.PROJECT_NUMBER, 10) || 0;
|
const projectNumber = parseInt(process.env.PROJECT_NUMBER, 10) || 0;
|
||||||
const projectStatusField = process.env.PROJECT_STATUS_FIELD;
|
const projectStatusField = process.env.PROJECT_STATUS_FIELD;
|
||||||
const projectAcceptedOption = process.env.PROJECT_ACCEPTED_OPTION;
|
const projectAcceptedOption = process.env.PROJECT_ACCEPTED_OPTION;
|
||||||
|
const projectGateLabel = process.env.PROJECT_GATE_LABEL;
|
||||||
const reviewMarker = '<!-- slash-commands-lgtm -->';
|
const reviewMarker = '<!-- slash-commands-lgtm -->';
|
||||||
|
|
||||||
function setResult(result, actor) {
|
function setResult(result, actor) {
|
||||||
|
|
@ -265,11 +266,15 @@ module.exports = async ({ github, context, core }) => {
|
||||||
core.info(`Auto-merge disable skipped: ${e.message}`);
|
core.info(`Auto-merge disable skipped: ${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function onAccept(prNumber, prNodeId) {
|
async function onAccept(pr) {
|
||||||
for (const label of removeLabelsOnAccept) {
|
for (const label of removeLabelsOnAccept) {
|
||||||
await removeLabel(prNumber, label);
|
await removeLabel(pr.number, label);
|
||||||
}
|
}
|
||||||
if (!projectNumber) return;
|
if (!projectNumber) return;
|
||||||
|
if (projectGateLabel && !pr.labels.some(l => l.name === projectGateLabel)) {
|
||||||
|
core.info(`Skipping project update — PR lacks '${projectGateLabel}' label`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const lookup = await github.graphql(
|
const lookup = await github.graphql(
|
||||||
`query($org: String!, $num: Int!, $field: String!) {
|
`query($org: String!, $num: Int!, $field: String!) {
|
||||||
|
|
@ -304,7 +309,7 @@ module.exports = async ({ github, context, core }) => {
|
||||||
item { id }
|
item { id }
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
{ proj: project.id, pr: prNodeId },
|
{ proj: project.id, pr: pr.node_id },
|
||||||
);
|
);
|
||||||
const itemId = add.addProjectV2ItemById.item.id;
|
const itemId = add.addProjectV2ItemById.item.id;
|
||||||
await github.graphql(
|
await github.graphql(
|
||||||
|
|
@ -376,7 +381,7 @@ module.exports = async ({ github, context, core }) => {
|
||||||
return setResult('force-unauthorized', actor);
|
return setResult('force-unauthorized', actor);
|
||||||
}
|
}
|
||||||
await addLabel(prNumber, approvedLabel);
|
await addLabel(prNumber, approvedLabel);
|
||||||
await onAccept(prNumber, pr.node_id);
|
await onAccept(pr);
|
||||||
if (submitReview) await submitApproval(prNumber, actor, true);
|
if (submitReview) await submitApproval(prNumber, actor, true);
|
||||||
await react(commentId, '+1');
|
await react(commentId, '+1');
|
||||||
await enableAutoMerge(pr.node_id, prNumber);
|
await enableAutoMerge(pr.node_id, prNumber);
|
||||||
|
|
@ -447,7 +452,7 @@ module.exports = async ({ github, context, core }) => {
|
||||||
// Execute
|
// Execute
|
||||||
if (cmd === 'lgtm' && !cancel) {
|
if (cmd === 'lgtm' && !cancel) {
|
||||||
await addLabel(prNumber, approvedLabel);
|
await addLabel(prNumber, approvedLabel);
|
||||||
await onAccept(prNumber, pr.node_id);
|
await onAccept(pr);
|
||||||
if (submitReview) await submitApproval(prNumber, actor);
|
if (submitReview) await submitApproval(prNumber, actor);
|
||||||
await react(commentId, '+1');
|
await react(commentId, '+1');
|
||||||
await enableAutoMerge(pr.node_id, prNumber);
|
await enableAutoMerge(pr.node_id, prNumber);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue