mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
182 lines
4.5 KiB
JavaScript
182 lines
4.5 KiB
JavaScript
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
function getPullRequest(context) {
|
|
const pullRequest = context.payload.pull_request;
|
|
if (!pullRequest?.number || !pullRequest.user?.login) {
|
|
throw new Error('This script must be run from a pull_request_target event.');
|
|
}
|
|
|
|
return {
|
|
author: pullRequest.user.login,
|
|
authorType: pullRequest.user.type,
|
|
labels: pullRequest.labels?.map((label) => label.name).filter(Boolean) ?? [],
|
|
number: pullRequest.number,
|
|
};
|
|
}
|
|
|
|
async function ensureLabel({ github, owner, repo, labelName }) {
|
|
try {
|
|
await github.rest.issues.getLabel({
|
|
owner,
|
|
repo,
|
|
name: labelName,
|
|
});
|
|
} catch (error) {
|
|
if (error.status !== 404) {
|
|
throw error;
|
|
}
|
|
|
|
try {
|
|
await github.rest.issues.createLabel({
|
|
owner,
|
|
repo,
|
|
name: labelName,
|
|
color: 'd93f0b',
|
|
description: 'Community author has exceeded the open pull request limit.',
|
|
});
|
|
} catch (createError) {
|
|
if (createError.status !== 422) {
|
|
throw createError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function hasLabel(labels, labelName) {
|
|
if (!labelName) {
|
|
return false;
|
|
}
|
|
|
|
return labels.some((label) => label.toLowerCase() === labelName.toLowerCase());
|
|
}
|
|
|
|
function isDependabotAuthor({ author, authorType }) {
|
|
return authorType === 'Bot' && author.toLowerCase() === 'dependabot[bot]';
|
|
}
|
|
|
|
function buildLimitMessage({ author, exemptLabelName, maxOpenPrs, openPrCount }) {
|
|
return [
|
|
`Thank you for your contribution, @${author}.`,
|
|
'',
|
|
`To keep the review queue manageable, we currently limit community contributors to ${maxOpenPrs} `
|
|
+ `open pull requests at a time. This PR would put you at ${openPrCount} open pull requests, `
|
|
+ 'so we are closing it automatically.',
|
|
'',
|
|
'Please focus on getting your existing PRs reviewed, merged, or closed before opening another one. '
|
|
+ `If a maintainer asked you to open this PR, they can apply the \`${exemptLabelName}\` label and reopen it.`,
|
|
].join('\n');
|
|
}
|
|
|
|
async function getOpenPrCount({ github, owner, repo, author, pullRequestNumber }) {
|
|
const openPullRequests = await github.paginate(github.rest.pulls.list, {
|
|
owner,
|
|
repo,
|
|
state: 'open',
|
|
per_page: 100,
|
|
});
|
|
|
|
const authorOpenPullRequestNumbers = openPullRequests
|
|
.filter((pullRequest) => pullRequest.user?.login === author)
|
|
.map((pullRequest) => pullRequest.number);
|
|
const currentPrIsOpen = authorOpenPullRequestNumbers.includes(pullRequestNumber);
|
|
const existingOpenPrCount = currentPrIsOpen
|
|
? authorOpenPullRequestNumbers.length - 1
|
|
: authorOpenPullRequestNumbers.length;
|
|
|
|
return existingOpenPrCount + 1;
|
|
}
|
|
|
|
async function enforcePrLimit({ github, context, core, exemptLabelName, maxOpenPrs, labelName }) {
|
|
const { owner, repo } = context.repo;
|
|
const { author, authorType, labels, number } = getPullRequest(context);
|
|
|
|
if (isDependabotAuthor({ author, authorType })) {
|
|
core.info(`Author ${author} is Dependabot; skipping open PR limit enforcement.`);
|
|
return {
|
|
author,
|
|
closed: false,
|
|
dependabotExempt: true,
|
|
openPrCount: null,
|
|
};
|
|
}
|
|
|
|
if (hasLabel(labels, exemptLabelName)) {
|
|
core.info(`PR #${number} has the ${exemptLabelName} label; skipping open PR limit enforcement.`);
|
|
return {
|
|
author,
|
|
closed: false,
|
|
exempt: true,
|
|
openPrCount: null,
|
|
};
|
|
}
|
|
|
|
const openPrCount = await getOpenPrCount({
|
|
github,
|
|
owner,
|
|
repo,
|
|
author,
|
|
pullRequestNumber: number,
|
|
});
|
|
|
|
if (openPrCount <= maxOpenPrs) {
|
|
core.info(
|
|
`${author} has ${openPrCount} open pull request(s), which is within the limit of ${maxOpenPrs}.`,
|
|
);
|
|
return {
|
|
author,
|
|
closed: false,
|
|
openPrCount,
|
|
};
|
|
}
|
|
|
|
await ensureLabel({
|
|
github,
|
|
owner,
|
|
repo,
|
|
labelName,
|
|
});
|
|
|
|
await github.rest.issues.addLabels({
|
|
owner,
|
|
repo,
|
|
issue_number: number,
|
|
labels: [labelName],
|
|
});
|
|
|
|
await github.rest.issues.createComment({
|
|
owner,
|
|
repo,
|
|
issue_number: number,
|
|
body: buildLimitMessage({
|
|
author,
|
|
exemptLabelName,
|
|
maxOpenPrs,
|
|
openPrCount,
|
|
}),
|
|
});
|
|
|
|
await github.rest.pulls.update({
|
|
owner,
|
|
repo,
|
|
pull_number: number,
|
|
state: 'closed',
|
|
});
|
|
|
|
core.info(
|
|
`${author} has ${openPrCount} open pull request(s), which exceeds the limit of ${maxOpenPrs}. `
|
|
+ `Closed PR #${number}.`,
|
|
);
|
|
|
|
return {
|
|
author,
|
|
closed: true,
|
|
openPrCount,
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
buildLimitMessage,
|
|
enforcePrLimit,
|
|
getOpenPrCount,
|
|
};
|