Remove PR size notifications (#52373)

These are noisy, and in general I'd prefer people to focus on the
quality of the resulting system; not the size of the diff. (Which may
require deliberately making changes larger)

## Context

<!-- What does this PR do, and why? How is it expected to impact users?
     Not just what changed, but what motivated it and why this approach.

Link to Linear issue (e.g., ENG-123) or GitHub issue (e.g., Closes #456)
     if one exists — helps with traceability. -->

## How to Review

<!-- Help reviewers focus their attention:
- For small PRs: note what to focus on (e.g., "error handling in
foo.rs")
- For large PRs (>400 LOC): provide a guided tour — numbered list of
files/commits to read in order. (The `large-pr` label is applied
automatically.)
     - See the review process guidelines for comment conventions -->

## Self-Review Checklist

<!-- Check before requesting review: -->
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin authored and GitHub committed 2026-03-27 03:39:48 +00:00
1 parent d77aba3ee7
commit 6bc34ff44f
3 files changed
+3 -322

No files matched your search

-109
View File
@@ -1,109 +0,0 @@
# PR Size Check — Compute
#
# Calculates PR size and saves the result as an artifact. A companion
# workflow (pr-size-label.yml) picks up the artifact via workflow_run
# and applies labels + comments with write permissions.
#
# This two-workflow split is required because fork PRs receive a
# read-only GITHUB_TOKEN. The compute step needs no write access;
# the label/comment step runs via workflow_run on the base repo with
# full write permissions.
#
# Security note: This workflow only reads PR file data via the JS API
# and writes a JSON artifact. No untrusted input is interpolated into
# shell commands.
name: PR Size Check
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: read
jobs:
compute-size:
if: github.repository_owner == 'zed-industries'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Calculate PR size
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require('fs');
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
per_page: 300,
});
// Sum additions + deletions, excluding generated/lock files
const IGNORED_PATTERNS = [
/\.lock$/,
/^Cargo\.lock$/,
/pnpm-lock\.yaml$/,
/\.generated\./,
/\/fixtures\//,
/\/snapshots\//,
];
let totalChanges = 0;
for (const file of files) {
const ignored = IGNORED_PATTERNS.some(p => p.test(file.filename));
if (!ignored) {
totalChanges += file.additions + file.deletions;
}
}
// Assign size bracket
const SIZE_BRACKETS = [
['Size S', 0, 100, '0e8a16'],
['Size M', 100, 400, 'fbca04'],
['Size L', 400, 800, 'e99695'],
['Size XL', 800, Infinity, 'b60205'],
];
let sizeLabel = 'Size S';
let labelColor = '0e8a16';
for (const [label, min, max, color] of SIZE_BRACKETS) {
if (totalChanges >= min && totalChanges < max) {
sizeLabel = label;
labelColor = color;
break;
}
}
// Check if the author wrote content in the "How to Review" section.
const rawBody = context.payload.pull_request.body || '';
const howToReview = rawBody.match(/## How to Review\s*\n([\s\S]*?)(?=\n## |$)/i);
const hasReviewGuidance = howToReview
? howToReview[1].replace(/<!--[\s\S]*?-->/g, '').trim().length > 0
: false;
const result = {
pr_number: context.issue.number,
total_changes: totalChanges,
size_label: sizeLabel,
label_color: labelColor,
has_review_guidance: hasReviewGuidance,
};
console.log(`PR #${result.pr_number}: ${totalChanges} LOC, ${sizeLabel}`);
fs.mkdirSync('pr-size', { recursive: true });
fs.writeFileSync('pr-size/result.json', JSON.stringify(result));
- name: Upload size result
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: pr-size-result
path: pr-size/
retention-days: 1
defaults:
run:
shell: bash -euxo pipefail {0}
-195
View File
@@ -1,195 +0,0 @@
# PR Size Check — Label & Comment
#
# Triggered by workflow_run after pr-size-check.yml completes.
# Downloads the size result artifact and applies labels + comments.
#
# This runs on the base repo with full GITHUB_TOKEN write access,
# so it works for both same-repo and fork PRs.
#
# Security note: The artifact is treated as untrusted data — only
# structured JSON fields (PR number, size label, color, boolean) are
# read. No artifact content is executed or interpolated into shell.
name: PR Size Label
on:
workflow_run:
workflows: ["PR Size Check"]
types: [completed]
jobs:
apply-labels:
if: >
github.repository_owner == 'zed-industries' &&
github.event.workflow_run.conclusion == 'success'
permissions:
contents: read
pull-requests: write
issues: write
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Download size result artifact
id: download
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require('fs');
const path = require('path');
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const match = allArtifacts.data.artifacts.find(a => a.name === 'pr-size-result');
if (!match) {
console.log('No pr-size-result artifact found, skipping');
core.setOutput('found', 'false');
return;
}
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: match.id,
archive_format: 'zip',
});
const temp = path.join(process.env.RUNNER_TEMP, 'pr-size');
fs.mkdirSync(temp, { recursive: true });
fs.writeFileSync(path.join(temp, 'result.zip'), Buffer.from(download.data));
core.setOutput('found', 'true');
- name: Unzip artifact
if: steps.download.outputs.found == 'true'
env:
ARTIFACT_DIR: ${{ runner.temp }}/pr-size
run: unzip "$ARTIFACT_DIR/result.zip" -d "$ARTIFACT_DIR"
- name: Apply labels and comment
if: steps.download.outputs.found == 'true'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const fs = require('fs');
const path = require('path');
const temp = path.join(process.env.RUNNER_TEMP, 'pr-size');
const resultPath = path.join(temp, 'result.json');
if (!fs.existsSync(resultPath)) {
console.log('No result.json found, skipping');
return;
}
const result = JSON.parse(fs.readFileSync(resultPath, 'utf8'));
// Validate artifact data (treat as untrusted)
const prNumber = Number(result.pr_number);
const totalChanges = Number(result.total_changes);
const sizeLabel = String(result.size_label);
const labelColor = String(result.label_color);
const hasReviewGuidance = Boolean(result.has_review_guidance);
if (!prNumber || !sizeLabel.startsWith('Size ')) {
core.setFailed(`Invalid artifact data: pr=${prNumber}, label=${sizeLabel}`);
return;
}
console.log(`PR #${prNumber}: ${totalChanges} LOC, ${sizeLabel}`);
// --- Size label (idempotent) ---
const existingLabels = (await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
})).data.map(l => l.name);
const existingSizeLabels = existingLabels.filter(l => l.startsWith('Size '));
const alreadyCorrect = existingSizeLabels.length === 1 && existingSizeLabels[0] === sizeLabel;
if (!alreadyCorrect) {
for (const label of existingSizeLabels) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: label,
});
}
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: sizeLabel,
color: labelColor,
});
} catch (e) {
if (e.status !== 422) throw e;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: [sizeLabel],
});
}
// --- Large PR handling (400+ LOC) ---
if (totalChanges >= 400) {
if (!existingLabels.includes('large-pr')) {
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'large-pr',
color: 'e99695',
});
} catch (e) {
if (e.status !== 422) throw e;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['large-pr'],
});
}
// Comment once with guidance
const MARKER = '<!-- pr-size-check -->';
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const alreadyCommented = comments.some(c => c.body.includes(MARKER));
if (!alreadyCommented) {
let body = `${MARKER}\n`;
body += `### :straight_ruler: PR Size: **${totalChanges} lines changed** (${sizeLabel})\n\n`;
body += `Please note: this PR exceeds the 400 LOC soft limit.\n`;
body += `- Consider **splitting** into separate PRs if the changes are separable\n`;
body += `- Ensure the PR description includes a **guided tour** in the "How to Review" section so reviewers know where to start\n`;
if (hasReviewGuidance) {
body += `\n:white_check_mark: "How to Review" section appears to include guidance — thank you!\n`;
}
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: body,
});
}
}
console.log(`PR #${prNumber}: labeled ${sizeLabel}, done`);
defaults:
run:
shell: bash -euxo pipefail {0}