From c28cbac51276cf003d111063aa3e3c9889007fb3 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Wed, 11 Feb 2026 21:12:27 -0600 Subject: [PATCH] CI: add PR size autolabel workflow (#14410) --- .github/workflows/labeler.yml | 89 +++++++++++++++++++++++++++++++++++ scripts/sync-labels.ts | 6 ++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 1170975c7a..cdb200a946 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -25,6 +25,95 @@ jobs: configuration-path: .github/labeler.yml repo-token: ${{ steps.app-token.outputs.token }} sync-labels: true + - name: Apply PR size label + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + const pullRequest = context.payload.pull_request; + if (!pullRequest) { + return; + } + + const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"]; + const labelColor = "fbca04"; + + for (const label of sizeLabels) { + try { + await github.rest.issues.getLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label, + }); + } catch (error) { + if (error?.status !== 404) { + throw error; + } + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label, + color: labelColor, + }); + } + } + + const files = await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pullRequest.number, + per_page: 100, + }); + + const excludedLockfiles = new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]); + const totalChangedLines = files.reduce((total, file) => { + const path = file.filename ?? ""; + if (path === "docs.acp.md" || path.startsWith("docs/") || excludedLockfiles.has(path)) { + return total; + } + return total + (file.additions ?? 0) + (file.deletions ?? 0); + }, 0); + + let targetSizeLabel = "size: XL"; + if (totalChangedLines < 50) { + targetSizeLabel = "size: XS"; + } else if (totalChangedLines < 200) { + targetSizeLabel = "size: S"; + } else if (totalChangedLines < 500) { + targetSizeLabel = "size: M"; + } else if (totalChangedLines < 1000) { + targetSizeLabel = "size: L"; + } + + const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequest.number, + per_page: 100, + }); + + for (const label of currentLabels) { + const name = label.name ?? ""; + if (!sizeLabels.includes(name)) { + continue; + } + if (name === targetSizeLabel) { + continue; + } + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequest.number, + name, + }); + } + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pullRequest.number, + labels: [targetSizeLabel], + }); - name: Apply maintainer label for org members uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 with: diff --git a/scripts/sync-labels.ts b/scripts/sync-labels.ts index c31983cca1..2d02886394 100644 --- a/scripts/sync-labels.ts +++ b/scripts/sync-labels.ts @@ -14,10 +14,14 @@ const COLOR_BY_PREFIX = new Map([ ["docs", "0075ca"], ["cli", "f9d0c4"], ["gateway", "d4c5f9"], + ["size", "fbca04"], ]); const configPath = resolve(".github/labeler.yml"); -const labelNames = extractLabelNames(readFileSync(configPath, "utf8")); +const EXTRA_LABELS = ["size: XS", "size: S", "size: M", "size: L", "size: XL"] as const; +const labelNames = [ + ...new Set([...extractLabelNames(readFileSync(configPath, "utf8")), ...EXTRA_LABELS]), +]; if (!labelNames.length) { throw new Error("labeler.yml must declare at least one label.");