mirror of
https://github.com/github/awesome-copilot.git
synced 2026-05-28 09:31: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:
@@ -0,0 +1,163 @@
|
||||
export const EXTERNAL_PLUGIN_INTAKE_LABELS = Object.freeze({
|
||||
"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 EXTERNAL_PLUGIN_INTAKE_SYNC_LABELS = Object.freeze([
|
||||
"external-plugin",
|
||||
"awaiting-review",
|
||||
"ready-for-review",
|
||||
"rejected",
|
||||
]);
|
||||
|
||||
async function ensureLabel({ github, owner, repo, name, config }) {
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner,
|
||||
repo,
|
||||
name,
|
||||
color: config.color,
|
||||
description: config.description,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 422) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function removeLabel({ github, owner, repo, issueNumber, name }) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
name,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function syncExternalPluginIntakeLabels({ github, owner, repo, issueNumber, desiredLabels }) {
|
||||
await Promise.all(
|
||||
Object.entries(EXTERNAL_PLUGIN_INTAKE_LABELS).map(([name, config]) =>
|
||||
ensureLabel({ github, owner, repo, name, config })
|
||||
)
|
||||
);
|
||||
|
||||
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const currentManagedLabels = currentLabels
|
||||
.map((label) => label.name)
|
||||
.filter((name) => EXTERNAL_PLUGIN_INTAKE_SYNC_LABELS.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,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
labels: labelsToAdd,
|
||||
});
|
||||
}
|
||||
|
||||
for (const name of labelsToRemove) {
|
||||
await removeLabel({ github, owner, repo, issueNumber, name });
|
||||
}
|
||||
}
|
||||
|
||||
export async function upsertExternalPluginIntakeComment({
|
||||
github,
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
marker,
|
||||
body,
|
||||
}) {
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const existingComment = comments.find(
|
||||
(comment) => comment.user?.login === "github-actions[bot]" && comment.body?.includes(marker)
|
||||
);
|
||||
|
||||
if (existingComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner,
|
||||
repo,
|
||||
comment_id: existingComment.id,
|
||||
body,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
export async function applyExternalPluginIntakeEvaluation({
|
||||
github,
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
evaluation,
|
||||
}) {
|
||||
const desiredLabels = evaluation.valid
|
||||
? new Set(["external-plugin", "ready-for-review"])
|
||||
: new Set(["external-plugin", "rejected"]);
|
||||
|
||||
await syncExternalPluginIntakeLabels({
|
||||
github,
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
desiredLabels,
|
||||
});
|
||||
|
||||
await upsertExternalPluginIntakeComment({
|
||||
github,
|
||||
owner,
|
||||
repo,
|
||||
issueNumber,
|
||||
marker: evaluation.commentMarker,
|
||||
body: evaluation.commentBody,
|
||||
});
|
||||
|
||||
return { desiredLabels };
|
||||
}
|
||||
@@ -6,7 +6,13 @@ import { fileURLToPath } from "url";
|
||||
import { ROOT_FOLDER } from "./constants.mjs";
|
||||
import { readExternalPlugins, validateExternalPlugin } from "./external-plugin-validation.mjs";
|
||||
|
||||
const ISSUE_FORM_MARKER = "<!-- external-plugin-submission -->";
|
||||
export const ISSUE_FORM_MARKER = "<!-- external-plugin-submission -->";
|
||||
export const EXTERNAL_PLUGIN_INTAKE_COMMENT_MARKER = "<!-- external-plugin-intake -->";
|
||||
export const RERUN_INTAKE_COMMAND = "/rerun-intake";
|
||||
const RERUN_INTAKE_COMMAND_PATTERN = new RegExp(
|
||||
`^\\s*${RERUN_INTAKE_COMMAND.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
|
||||
"m",
|
||||
);
|
||||
const PLUGINS_DIR = path.join(ROOT_FOLDER, "plugins");
|
||||
|
||||
const REQUIRED_CHECKLIST_ITEMS = [
|
||||
@@ -261,6 +267,10 @@ export function parseExternalPluginIssueBody(body) {
|
||||
};
|
||||
}
|
||||
|
||||
export function parseRerunIntakeCommand(body) {
|
||||
return RERUN_INTAKE_COMMAND_PATTERN.test(String(body ?? ""));
|
||||
}
|
||||
|
||||
export async function evaluateExternalPluginIssue({ issue, token } = {}) {
|
||||
const issueBody = issue?.body ?? "";
|
||||
const parsed = parseExternalPluginIssueBody(issueBody);
|
||||
@@ -294,7 +304,7 @@ export async function evaluateExternalPluginIssue({ issue, token } = {}) {
|
||||
const dedupedErrors = [...new Set(errors)];
|
||||
const dedupedWarnings = [...new Set(warnings)];
|
||||
const valid = dedupedErrors.length === 0;
|
||||
const marker = "<!-- external-plugin-intake -->";
|
||||
const marker = EXTERNAL_PLUGIN_INTAKE_COMMENT_MARKER;
|
||||
const normalizedKeywords = parsed.plugin?.keywords?.length ? parsed.plugin.keywords.join(", ") : "_None provided_";
|
||||
const notes = parsed.additionalNotes ?? "_No additional notes provided._";
|
||||
const payload = parsed.plugin
|
||||
@@ -333,7 +343,7 @@ export async function evaluateExternalPluginIssue({ issue, token } = {}) {
|
||||
"## ❌ External plugin intake failed",
|
||||
"",
|
||||
"This submission did not pass automated intake validation, so the issue has been closed.",
|
||||
"Update the issue form, then reopen the issue to run intake validation again.",
|
||||
`Edit the issue form to address the fixes below, then have the issue author or a maintainer comment \`${RERUN_INTAKE_COMMAND}\` to re-run intake for this closed submission.`,
|
||||
"",
|
||||
"### Required fixes",
|
||||
"",
|
||||
|
||||
Reference in New Issue
Block a user