From 5cac9e2479751bf2145bee0e3e7a559dcd701201 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 28 Mar 2026 17:57:35 -0300 Subject: [PATCH 1/3] fix(ci): use whitespace-insensitive PR size diff --- .github/workflows/pr-size.yml | 75 ++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pr-size.yml b/.github/workflows/pr-size.yml index ced845a28f..669efee0ff 100644 --- a/.github/workflows/pr-size.yml +++ b/.github/workflows/pr-size.yml @@ -124,13 +124,24 @@ jobs: group: pr-size-${{ github.event.pull_request.number }} cancel-in-progress: true steps: + - name: Checkout base repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Sync PR size label uses: actions/github-script@v8 env: PR_SIZE_LABELS_JSON: ${{ needs.prepare-config.outputs.labels_json }} with: script: | + const { execFileSync } = require("node:child_process"); + const issueNumber = context.payload.pull_request.number; + const baseSha = context.payload.pull_request.base.sha; + const headSha = context.payload.pull_request.head.sha; + const headCloneUrl = context.payload.pull_request.head.repo.clone_url; + const headRef = context.payload.pull_request.head.ref; + const headTrackingRef = `refs/remotes/pr-size/${issueNumber}`; const managedLabels = JSON.parse(process.env.PR_SIZE_LABELS_JSON ?? "[]"); const managedLabelNames = new Set(managedLabels.map((label) => label.name)); // Keep this aligned with the repo's test entrypoints and test-only support files. @@ -144,6 +155,28 @@ jobs: const isTestFile = (filename) => testFilePatterns.some((pattern) => pattern.test(filename)); + const parseNumstat = (text) => + text + .split("\n") + .filter(Boolean) + .map((line) => { + const [insertionsRaw = "0", deletionsRaw = "0", ...pathParts] = line.split("\t"); + const filename = pathParts.join("\t").trim(); + + if (!filename) { + return null; + } + + return { + filename, + additions: + insertionsRaw === "-" ? 0 : Number.parseInt(insertionsRaw, 10) || 0, + deletions: + deletionsRaw === "-" ? 0 : Number.parseInt(deletionsRaw, 10) || 0, + }; + }) + .filter((file) => file !== null); + const resolveSizeLabel = (totalChangedLines) => { if (totalChangedLines < 10) { return "size:XS"; @@ -168,23 +201,47 @@ jobs: return "size:XXL"; }; - const files = await github.paginate( - github.rest.pulls.listFiles, + execFileSync("git", ["fetch", "--no-tags", "origin", baseSha], { + stdio: "inherit", + }); + + execFileSync( + "git", + ["fetch", "--no-tags", headCloneUrl, `+refs/heads/${headRef}:${headTrackingRef}`], { - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: issueNumber, - per_page: 100, + stdio: "inherit", }, - (response) => response.data, ); - if (files.length >= 3000) { + const resolvedHeadSha = execFileSync("git", ["rev-parse", headTrackingRef], { + encoding: "utf8", + }).trim(); + + if (resolvedHeadSha !== headSha) { core.warning( - "The GitHub pull request files API may truncate results at 3,000 files; PR size may be undercounted.", + `Fetched head SHA ${resolvedHeadSha} does not match pull request head SHA ${headSha}; using fetched ref for sizing.`, ); } + execFileSync("git", ["cat-file", "-e", `${baseSha}^{commit}`], { + stdio: "inherit", + }); + + const files = parseNumstat( + execFileSync( + "git", + [ + "diff", + "--numstat", + "--ignore-all-space", + "--ignore-blank-lines", + "--no-renames", + `${baseSha}...${resolvedHeadSha}`, + ], + { encoding: "utf8" }, + ), + ); + let testChangedLines = 0; let nonTestChangedLines = 0; From 961a63e7abe76acefa10d46dd7bcff7b2fa59e45 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 28 Mar 2026 18:45:47 -0300 Subject: [PATCH 2/3] fix(ci): simplify PR size diff filtering --- .github/workflows/pr-size.yml | 86 ++++++++++++++--------------------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/.github/workflows/pr-size.yml b/.github/workflows/pr-size.yml index 669efee0ff..32929b4d48 100644 --- a/.github/workflows/pr-size.yml +++ b/.github/workflows/pr-size.yml @@ -145,37 +145,30 @@ jobs: const managedLabels = JSON.parse(process.env.PR_SIZE_LABELS_JSON ?? "[]"); const managedLabelNames = new Set(managedLabels.map((label) => label.name)); // Keep this aligned with the repo's test entrypoints and test-only support files. - const testFilePatterns = [ - /(^|\/)__tests__(\/|$)/, - /(^|\/)tests?(\/|$)/, - /^apps\/server\/integration\//, - /\.(test|spec|browser|integration)\.[^.\/]+$/, + const testExcludePathspecs = [ + ":(glob,exclude)**/__tests__/**", + ":(glob,exclude)**/test/**", + ":(glob,exclude)**/tests/**", + ":(glob,exclude)apps/server/integration/**", + ":(glob,exclude)**/*.test.*", + ":(glob,exclude)**/*.spec.*", + ":(glob,exclude)**/*.browser.*", + ":(glob,exclude)**/*.integration.*", ]; - const isTestFile = (filename) => - testFilePatterns.some((pattern) => pattern.test(filename)); - - const parseNumstat = (text) => + const sumNumstat = (text) => text .split("\n") .filter(Boolean) - .map((line) => { - const [insertionsRaw = "0", deletionsRaw = "0", ...pathParts] = line.split("\t"); - const filename = pathParts.join("\t").trim(); - - if (!filename) { - return null; - } + .reduce((total, line) => { + const [insertionsRaw = "0", deletionsRaw = "0"] = line.split("\t"); + const additions = + insertionsRaw === "-" ? 0 : Number.parseInt(insertionsRaw, 10) || 0; + const deletions = + deletionsRaw === "-" ? 0 : Number.parseInt(deletionsRaw, 10) || 0; - return { - filename, - additions: - insertionsRaw === "-" ? 0 : Number.parseInt(insertionsRaw, 10) || 0, - deletions: - deletionsRaw === "-" ? 0 : Number.parseInt(deletionsRaw, 10) || 0, - }; - }) - .filter((file) => file !== null); + return total + additions + deletions; + }, 0); const resolveSizeLabel = (totalChangedLines) => { if (totalChangedLines < 10) { @@ -227,38 +220,27 @@ jobs: stdio: "inherit", }); - const files = parseNumstat( + const diffArgs = [ + "diff", + "--numstat", + "--ignore-all-space", + "--ignore-blank-lines", + `${baseSha}...${resolvedHeadSha}`, + ]; + + const totalChangedLines = sumNumstat( execFileSync( "git", - [ - "diff", - "--numstat", - "--ignore-all-space", - "--ignore-blank-lines", - "--no-renames", - `${baseSha}...${resolvedHeadSha}`, - ], + diffArgs, { encoding: "utf8" }, ), ); - - let testChangedLines = 0; - let nonTestChangedLines = 0; - - for (const file of files) { - const changedLinesForFile = (file.additions ?? 0) + (file.deletions ?? 0); - - if (changedLinesForFile === 0) { - continue; - } - - if (isTestFile(file.filename)) { - testChangedLines += changedLinesForFile; - continue; - } - - nonTestChangedLines += changedLinesForFile; - } + const nonTestChangedLines = sumNumstat( + execFileSync("git", [...diffArgs, "--", ".", ...testExcludePathspecs], { + encoding: "utf8", + }), + ); + const testChangedLines = Math.max(0, totalChangedLines - nonTestChangedLines); const changedLines = nonTestChangedLines === 0 ? testChangedLines : nonTestChangedLines; const nextLabelName = resolveSizeLabel(changedLines); From 0dbdc0ed7ff7301d69253aaf4cbd4fb7d30a8833 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 28 Mar 2026 19:05:49 -0300 Subject: [PATCH 3/3] fix(ci): fetch PR head ref from origin --- .github/workflows/pr-size.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/pr-size.yml b/.github/workflows/pr-size.yml index 32929b4d48..15c04d663d 100644 --- a/.github/workflows/pr-size.yml +++ b/.github/workflows/pr-size.yml @@ -139,8 +139,6 @@ jobs: const issueNumber = context.payload.pull_request.number; const baseSha = context.payload.pull_request.base.sha; const headSha = context.payload.pull_request.head.sha; - const headCloneUrl = context.payload.pull_request.head.repo.clone_url; - const headRef = context.payload.pull_request.head.ref; const headTrackingRef = `refs/remotes/pr-size/${issueNumber}`; const managedLabels = JSON.parse(process.env.PR_SIZE_LABELS_JSON ?? "[]"); const managedLabelNames = new Set(managedLabels.map((label) => label.name)); @@ -200,7 +198,7 @@ jobs: execFileSync( "git", - ["fetch", "--no-tags", headCloneUrl, `+refs/heads/${headRef}:${headTrackingRef}`], + ["fetch", "--no-tags", "origin", `+refs/pull/${issueNumber}/head:${headTrackingRef}`], { stdio: "inherit", },