Skip to content

Add headless t3 serve pairing output #2909

Add headless t3 serve pairing output

Add headless t3 serve pairing output #2909

Workflow file for this run

name: PR Size
on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review, converted_to_draft]
permissions:
contents: read
jobs:
prepare-config:
name: Prepare PR size config
runs-on: ubuntu-24.04
outputs:
labels_json: ${{ steps.config.outputs.labels_json }}
steps:
- id: config
name: Build PR size label config
uses: actions/github-script@v8
with:
result-encoding: string
script: |
const managedLabels = [
{
name: "size:XS",
color: "0e8a16",
description: "0-9 effective changed lines (test files excluded in mixed PRs).",
},
{
name: "size:S",
color: "5ebd3e",
description: "10-29 effective changed lines (test files excluded in mixed PRs).",
},
{
name: "size:M",
color: "fbca04",
description: "30-99 effective changed lines (test files excluded in mixed PRs).",
},
{
name: "size:L",
color: "fe7d37",
description: "100-499 effective changed lines (test files excluded in mixed PRs).",
},
{
name: "size:XL",
color: "d93f0b",
description: "500-999 effective changed lines (test files excluded in mixed PRs).",
},
{
name: "size:XXL",
color: "b60205",
description: "1,000+ effective changed lines (test files excluded in mixed PRs).",
},
];
core.setOutput("labels_json", JSON.stringify(managedLabels));
sync-label-definitions:
name: Sync PR size label definitions
needs: prepare-config
if: github.event_name != 'pull_request_target'
runs-on: ubuntu-24.04
permissions:
contents: read
issues: write
steps:
- name: Ensure PR size labels exist
uses: actions/github-script@v8
env:
PR_SIZE_LABELS_JSON: ${{ needs.prepare-config.outputs.labels_json }}
with:
script: |
const managedLabels = JSON.parse(process.env.PR_SIZE_LABELS_JSON ?? "[]");
for (const label of managedLabels) {
try {
const { data: existing } = await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
});
if (
existing.color !== label.color ||
(existing.description ?? "") !== label.description
) {
await github.rest.issues.updateLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description,
});
}
} catch (error) {
if (error.status !== 404) {
throw error;
}
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label.name,
color: label.color,
description: label.description,
});
} catch (createError) {
if (createError.status !== 422) {
throw createError;
}
}
}
}
label:
name: Label PR size
needs: prepare-config
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-24.04
permissions:
contents: read
issues: read
pull-requests: write
concurrency:
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 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.
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 sumNumstat = (text) =>
text
.split("\n")
.filter(Boolean)
.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 total + additions + deletions;
}, 0);
const resolveSizeLabel = (totalChangedLines) => {
if (totalChangedLines < 10) {
return "size:XS";
}
if (totalChangedLines < 30) {
return "size:S";
}
if (totalChangedLines < 100) {
return "size:M";
}
if (totalChangedLines < 500) {
return "size:L";
}
if (totalChangedLines < 1000) {
return "size:XL";
}
return "size:XXL";
};
execFileSync("git", ["fetch", "--no-tags", "origin", baseSha], {
stdio: "inherit",
});
execFileSync(
"git",
["fetch", "--no-tags", "origin", `+refs/pull/${issueNumber}/head:${headTrackingRef}`],
{
stdio: "inherit",
},
);
const resolvedHeadSha = execFileSync("git", ["rev-parse", headTrackingRef], {
encoding: "utf8",
}).trim();
if (resolvedHeadSha !== headSha) {
core.warning(
`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 diffArgs = [
"diff",
"--numstat",
"--ignore-all-space",
"--ignore-blank-lines",
`${baseSha}...${resolvedHeadSha}`,
];
const totalChangedLines = sumNumstat(
execFileSync(
"git",
diffArgs,
{ encoding: "utf8" },
),
);
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);
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
per_page: 100,
});
for (const label of currentLabels) {
if (!managedLabelNames.has(label.name) || label.name === nextLabelName) {
continue;
}
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
name: label.name,
});
} catch (removeError) {
if (removeError.status !== 404) {
throw removeError;
}
}
}
if (!currentLabels.some((label) => label.name === nextLabelName)) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: [nextLabelName],
});
}
const classification =
nonTestChangedLines === 0
? testChangedLines > 0
? "test-only PR"
: "no line changes"
: testChangedLines > 0
? "test lines excluded"
: "all non-test changes";
core.info(
`PR #${issueNumber}: ${nonTestChangedLines} non-test lines, ${testChangedLines} test lines, ${changedLines} effective lines -> ${nextLabelName} (${classification})`,
);