mirror of
https://github.com/github/awesome-copilot.git
synced 2026-05-27 17:11:44 +00:00
Adding a new /rerun-intake command for when updates are required (#1786)
* Adding a new /rerun-intake command for when updates are required Reruns the intake process if feedback is given that will require the submitter to update something about the submittion. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Use rerun command constant in parser regex Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>
This commit is contained in:
@@ -118,7 +118,7 @@ jobs:
|
||||
'',
|
||||
'### Required fixes',
|
||||
'',
|
||||
...(errors.length > 0 ? errors.map((error) => `- ${error}`) : ['- Re-run intake validation by updating the issue details.'])
|
||||
...(errors.length > 0 ? errors.map((error) => `- ${error}`) : ['- Edit the issue details and let intake rerun automatically, or comment `/rerun-intake` to trigger it again on demand.'])
|
||||
].join('\n');
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
@@ -493,7 +493,7 @@ jobs:
|
||||
'',
|
||||
reason,
|
||||
'',
|
||||
'If you address the feedback, open a new external plugin submission issue with the updated details.'
|
||||
'If you address the feedback, edit this issue with the updated details and have the issue author or a maintainer comment `/rerun-intake` to re-run automated intake.'
|
||||
].join('\n');
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
|
||||
@@ -4,6 +4,10 @@ on:
|
||||
issues:
|
||||
types: [opened, edited, reopened]
|
||||
|
||||
concurrency:
|
||||
group: external-plugin-intake-${{ github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
@@ -36,81 +40,10 @@ jobs:
|
||||
RESULT_JSON: ${{ steps.evaluation.outputs.result }}
|
||||
with:
|
||||
script: |
|
||||
const managedLabels = {
|
||||
'external-plugin': {
|
||||
color: 'FEF2C0',
|
||||
description: 'Public external plugin submission'
|
||||
},
|
||||
'awaiting-review': {
|
||||
color: 'FBCA04',
|
||||
description: 'Submission is waiting for automated intake validation'
|
||||
},
|
||||
'ready-for-review': {
|
||||
color: '0E8A16',
|
||||
description: 'Submission passed intake validation and is ready for maintainer review'
|
||||
},
|
||||
'approved': {
|
||||
color: '1D76DB',
|
||||
description: 'Submission was approved by a maintainer'
|
||||
},
|
||||
'rejected': {
|
||||
color: 'B60205',
|
||||
description: 'Submission was rejected or failed intake validation'
|
||||
}
|
||||
};
|
||||
const path = require('path');
|
||||
const { pathToFileURL } = require('url');
|
||||
|
||||
async function ensureLabel(name, config) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function syncManagedLabels(issueNumber, desiredLabels) {
|
||||
await Promise.all(Object.entries(managedLabels).map(([name, config]) => ensureLabel(name, config)));
|
||||
|
||||
const managedForSync = ['external-plugin', 'awaiting-review', 'ready-for-review', 'rejected'];
|
||||
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
const currentManagedLabels = currentLabels
|
||||
.map((label) => label.name)
|
||||
.filter((name) => managedForSync.includes(name));
|
||||
|
||||
const labelsToAdd = [...desiredLabels].filter((name) => !currentManagedLabels.includes(name));
|
||||
const labelsToRemove = currentManagedLabels.filter((name) => !desiredLabels.has(name));
|
||||
|
||||
if (labelsToAdd.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
labels: labelsToAdd
|
||||
});
|
||||
}
|
||||
|
||||
for (const name of labelsToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
name
|
||||
});
|
||||
}
|
||||
}
|
||||
const intakeState = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake-state.mjs')).href);
|
||||
|
||||
const result = JSON.parse(process.env.RESULT_JSON);
|
||||
const issueNumber = context.issue.number;
|
||||
@@ -128,40 +61,14 @@ jobs:
|
||||
return;
|
||||
}
|
||||
|
||||
const desiredLabels = result.valid
|
||||
? new Set(['external-plugin', 'ready-for-review'])
|
||||
: new Set(['external-plugin', 'rejected']);
|
||||
|
||||
await syncManagedLabels(issueNumber, desiredLabels);
|
||||
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
await intakeState.applyExternalPluginIntakeEvaluation({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100
|
||||
issueNumber,
|
||||
evaluation: result
|
||||
});
|
||||
|
||||
const existingComment = comments.find((comment) =>
|
||||
comment.user?.login === 'github-actions[bot]' &&
|
||||
comment.body?.includes(result.commentMarker)
|
||||
);
|
||||
|
||||
if (existingComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existingComment.id,
|
||||
body: result.commentBody
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: result.commentBody
|
||||
});
|
||||
}
|
||||
|
||||
if (!result.valid && issueState === 'open') {
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
name: External Plugin Rerun Intake Commands
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
concurrency:
|
||||
group: external-plugin-intake-${{ github.event.issue.number }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
handle-command:
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
!github.event.issue.pull_request &&
|
||||
startsWith(github.event.comment.body, '/rerun-intake')
|
||||
steps:
|
||||
- name: Checkout staged branch
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: staged
|
||||
|
||||
- name: Re-run external plugin intake
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
script: |
|
||||
const path = require('path');
|
||||
const { pathToFileURL } = require('url');
|
||||
|
||||
const intake = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake.mjs')).href);
|
||||
const intakeState = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake-state.mjs')).href);
|
||||
|
||||
const commentAuthor = context.payload.comment.user?.login;
|
||||
if (!commentAuthor || context.payload.comment.user?.type === 'Bot' || commentAuthor === 'github-actions[bot]') {
|
||||
core.info('Ignoring /rerun-intake from a bot or unknown actor.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!intake.parseRerunIntakeCommand(context.payload.comment.body)) {
|
||||
core.info('No supported /rerun-intake command was found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: currentIssue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number
|
||||
});
|
||||
|
||||
const labelNames = new Set((currentIssue.labels || []).map((label) => label.name));
|
||||
const isExternalPluginIssue =
|
||||
labelNames.has('external-plugin') ||
|
||||
String(currentIssue.body || '').includes(intake.ISSUE_FORM_MARKER);
|
||||
if (!isExternalPluginIssue) {
|
||||
core.info('Ignoring /rerun-intake because the issue is not an external plugin submission.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (labelNames.has('approved') || labelNames.has('re-review-due') || labelNames.has('re-review-follow-up')) {
|
||||
core.info('Ignoring /rerun-intake because the issue is already approved or in the six-month re-review flow.');
|
||||
return;
|
||||
}
|
||||
|
||||
const issueAuthor = currentIssue.user?.login;
|
||||
const isIssueAuthor = Boolean(issueAuthor && commentAuthor === issueAuthor);
|
||||
|
||||
let hasWriteAccess = false;
|
||||
if (!isIssueAuthor) {
|
||||
const permission = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
username: commentAuthor
|
||||
});
|
||||
hasWriteAccess = ['admin', 'write', 'maintain'].includes(permission.data.permission);
|
||||
}
|
||||
|
||||
if (!isIssueAuthor && !hasWriteAccess) {
|
||||
core.info(`Ignoring /rerun-intake because ${commentAuthor} is neither the issue author nor a maintainer.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const canRerunFromCurrentState = currentIssue.state === 'open' || labelNames.has('rejected');
|
||||
if (!canRerunFromCurrentState) {
|
||||
core.info('Ignoring /rerun-intake because the issue is closed outside the intake/rejection flow.');
|
||||
return;
|
||||
}
|
||||
|
||||
const evaluation = await intake.evaluateExternalPluginIssue({
|
||||
issue: currentIssue,
|
||||
token: process.env.GITHUB_TOKEN
|
||||
});
|
||||
|
||||
await intakeState.applyExternalPluginIntakeEvaluation({
|
||||
github,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issueNumber: context.issue.number,
|
||||
evaluation
|
||||
});
|
||||
|
||||
if (evaluation.valid && currentIssue.state === 'closed' && labelNames.has('rejected')) {
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
state: 'open'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!evaluation.valid && currentIssue.state === 'open') {
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user