@@ -1,28 +1,13 @@
-## Context
+Self-Review Checklist:
-<!-- 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
+Closes #ISSUE
+
Release Notes:
- N/A or Added/Fixed/Improved ...
@@ -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}
@@ -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}