fix(labels): unify issue contributor tiers and managed label metadata

This commit is contained in:
Chummy 2026-02-17 15:32:49 +08:00
parent d7ed5c4187
commit 26323774e4
4 changed files with 44 additions and 17 deletions

View file

@ -28,7 +28,6 @@ jobs:
const issue = context.payload.issue;
const pullRequest = context.payload.pull_request;
const target = issue ?? pullRequest;
const legacyTrustedContributorLabel = "trusted contributor";
const contributorTierRules = [
{ label: "distinguished contributor", minMergedPRs: 50 },
{ label: "principal contributor", minMergedPRs: 20 },
@ -37,10 +36,7 @@ jobs:
];
const contributorTierLabels = contributorTierRules.map((rule) => rule.label);
const contributorTierColor = "2ED9FF"; // Keep in sync with .github/workflows/labeler.yml
const managedContributorLabels = new Set([
legacyTrustedContributorLabel,
...contributorTierLabels,
]);
const managedContributorLabels = new Set(contributorTierLabels);
const action = context.payload.action;
const changedLabel = context.payload.label?.name;
@ -52,18 +48,26 @@ jobs:
const author = target.user;
if (!author || author.type === "Bot") return;
function contributorTierDescription(rule) {
return `Contributor with ${rule.minMergedPRs}+ merged PRs.`;
}
async function ensureContributorTierLabels() {
for (const label of contributorTierLabels) {
for (const rule of contributorTierRules) {
const label = rule.label;
const expectedDescription = contributorTierDescription(rule);
try {
const { data: existing } = await github.rest.issues.getLabel({ owner, repo, name: label });
const currentColor = (existing.color || "").toUpperCase();
if (currentColor !== contributorTierColor) {
const currentDescription = (existing.description || "").trim();
if (currentColor !== contributorTierColor || currentDescription !== expectedDescription) {
await github.rest.issues.updateLabel({
owner,
repo,
name: label,
new_name: label,
color: contributorTierColor,
description: expectedDescription,
});
}
} catch (error) {
@ -73,6 +77,7 @@ jobs:
repo,
name: label,
color: contributorTierColor,
description: expectedDescription,
});
}
}
@ -105,7 +110,7 @@ jobs:
});
const keepLabels = currentLabels
.map((label) => label.name)
.filter((label) => label !== legacyTrustedContributorLabel && !contributorTierLabels.includes(label));
.filter((label) => !contributorTierLabels.includes(label));
if (contributorTierLabel) {
keepLabels.push(contributorTierLabel);

View file

@ -44,8 +44,6 @@ jobs:
manualRiskOverrideLabel,
...computedRiskLabels,
]);
const legacyTrustedContributorLabel = "trusted contributor";
if ((action === "labeled" || action === "unlabeled") && !managedEnforcedLabels.has(changedLabel)) {
core.info(`skip non-size/risk label event: ${changedLabel || "unknown"}`);
return;
@ -442,13 +440,13 @@ jobs:
return "Auto-managed label.";
}
async function ensureLabel(name) {
async function ensureLabel(name, existing = null) {
const expectedColor = colorForLabel(name);
const expectedDescription = descriptionForLabel(name);
try {
const { data: existing } = await github.rest.issues.getLabel({ owner, repo, name });
const currentColor = (existing.color || "").toUpperCase();
const currentDescription = (existing.description || "").trim();
const current = existing || (await github.rest.issues.getLabel({ owner, repo, name })).data;
const currentColor = (current.color || "").toUpperCase();
const currentDescription = (current.description || "").trim();
if (currentColor !== expectedColor || currentDescription !== expectedDescription) {
await github.rest.issues.updateLabel({
owner,
@ -471,6 +469,29 @@ jobs:
}
}
function isManagedLabel(label) {
if (label === manualRiskOverrideLabel) return true;
if (sizeLabels.includes(label) || computedRiskLabels.includes(label)) return true;
if (managedPathLabelSet.has(label)) return true;
if (contributorTierLabels.includes(label)) return true;
if (managedModulePrefixes.some((prefix) => label.startsWith(prefix))) return true;
return false;
}
async function ensureManagedRepoLabelsMetadata() {
const repoLabels = await github.paginate(github.rest.issues.listLabelsForRepo, {
owner,
repo,
per_page: 100,
});
for (const existingLabel of repoLabels) {
const labelName = existingLabel.name || "";
if (!isManagedLabel(labelName)) continue;
await ensureLabel(labelName, existingLabel);
}
}
function selectContributorTier(mergedCount) {
const matchedTier = contributorTierRules.find((rule) => mergedCount >= rule.minMergedPRs);
return matchedTier ? matchedTier.label : null;
@ -629,6 +650,8 @@ jobs:
riskLabel = "risk: medium";
}
await ensureManagedRepoLabelsMetadata();
const labelsToEnsure = new Set([
...sizeLabels,
...computedRiskLabels,
@ -660,7 +683,6 @@ jobs:
const hasManualRiskOverride = currentLabelNames.includes(manualRiskOverrideLabel);
const keepNonManagedLabels = currentLabelNames.filter((label) => {
if (label === manualRiskOverrideLabel) return true;
if (label === legacyTrustedContributorLabel) return false;
if (contributorTierLabels.includes(label)) return false;
if (sizeLabels.includes(label) || computedRiskLabels.includes(label)) return false;
if (managedPathLabelSet.has(label)) return false;