Files
buzz/scripts/check-file-sizes-core.mjs
cls 9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
feat: import Chinese-localized Buzz source snapshot
Signed-off-by: cls_宁波本机 <908705107@qq.com>
2026-08-13 18:34:25 +08:00

175 lines
5.1 KiB
JavaScript

import { execFileSync } from "node:child_process";
import { promises as fs } from "node:fs";
import path from "node:path";
function git(args, cwd, options = {}) {
return execFileSync("git", args, {
cwd,
encoding: "utf8",
maxBuffer: 10 * 1024 * 1024,
...options,
});
}
function toPosixPath(relativePath) {
return relativePath.split(path.sep).join("/");
}
export function countLines(content) {
if (content.length === 0) {
return 0;
}
return content.split(/\r?\n/).length;
}
export function allowedLineCount(baseLines, maxLines) {
return baseLines == null || baseLines <= maxLines ? maxLines : baseLines;
}
export function evaluateFileSize({ baseLines, candidateLines, maxLines }) {
const limit = allowedLineCount(baseLines, maxLines);
return { limit, violates: candidateLines > limit };
}
function findRule(rules, relativePath) {
return rules.find((rule) => relativePath.startsWith(`${rule.root}/`));
}
export function resolveBaseRef(repoRoot, env = process.env) {
if (env.CHECK_FILE_SIZES_BASE) {
return env.CHECK_FILE_SIZES_BASE;
}
if (env.GITHUB_ACTIONS === "true") {
return "HEAD^1";
}
try {
const mergeBase = git(
["merge-base", "origin/main", "HEAD"],
repoRoot,
).trim();
const head = git(["rev-parse", "HEAD"], repoRoot).trim();
return mergeBase === head ? "HEAD" : mergeBase;
} catch (error) {
throw new Error(
"Could not resolve the file-size base from origin/main. Fetch origin/main or set CHECK_FILE_SIZES_BASE to an explicit commit.",
{ cause: error },
);
}
}
export function parseChangedFiles(output) {
const fields = output.split("\0");
const changes = [];
for (let index = 0; index < fields.length - 1; ) {
const status = fields[index++];
if (status.startsWith("R") || status.startsWith("C")) {
changes.push({
status: status[0],
oldPath: fields[index++],
path: fields[index++],
});
} else {
changes.push({ status: status[0], path: fields[index++] });
}
}
return changes;
}
function changedProjectFiles({ repoRoot, projectRelative, baseRef }) {
const output = git(
["diff", "--name-status", "-z", "-M", baseRef, "--", projectRelative],
repoRoot,
);
const changes = parseChangedFiles(output);
const trackedPaths = new Set(changes.map((change) => change.path));
const untracked = git(
["ls-files", "--others", "--exclude-standard", "-z", "--", projectRelative],
repoRoot,
)
.split("\0")
.filter(Boolean);
for (const filePath of untracked) {
if (!trackedPaths.has(filePath)) {
changes.push({ status: "A", path: filePath });
}
}
return changes;
}
function readBaseFile(repoRoot, baseRef, filePath) {
return git(["show", `${baseRef}:${filePath}`], repoRoot, {
encoding: null,
}).toString("utf8");
}
export async function runFileSizeCheck({ projectRoot, rules, label }) {
// Every governed project is a direct child of the repository root. Derive
// these paths without Git so hook-provided repository environment variables
// cannot collapse the project pathspec to an empty string.
const repoRoot = path.dirname(projectRoot);
const projectRelative = toPosixPath(path.basename(projectRoot));
const baseRef = resolveBaseRef(repoRoot);
// Fail clearly instead of silently turning a missing/shallow base into a pass.
git(["cat-file", "-e", `${baseRef}^{commit}`], repoRoot);
const violations = [];
for (const change of changedProjectFiles({
repoRoot,
projectRelative,
baseRef,
})) {
if (change.status === "D") continue;
const relativePath = toPosixPath(
path.relative(projectRelative, change.path),
);
const rule = findRule(rules, relativePath);
if (!rule || !rule.extensions.has(path.extname(relativePath))) continue;
const candidatePath = path.join(repoRoot, change.path);
const candidateLines = countLines(await fs.readFile(candidatePath, "utf8"));
const basePath = change.oldPath ?? change.path;
const baseContent =
change.status === "A" ? null : readBaseFile(repoRoot, baseRef, basePath);
const baseLines = baseContent == null ? null : countLines(baseContent);
const result = evaluateFileSize({
baseLines,
candidateLines,
maxLines: rule.maxLines,
});
if (result.violates) {
violations.push({
relativePath,
baseLines,
candidateLines,
limit: result.limit,
});
}
}
if (violations.length === 0) return;
console.error(`${label} file size ratchet failed (base ${baseRef}):`);
for (const violation of violations) {
const before = violation.baseLines == null ? "new" : violation.baseLines;
const delta =
violation.baseLines == null
? ""
: ` (${violation.candidateLines - violation.baseLines >= 0 ? "+" : ""}${violation.candidateLines - violation.baseLines})`;
console.error(
`- ${violation.relativePath}: ${before} -> ${violation.candidateLines}${delta} lines (allowed ${violation.limit})`,
);
}
console.error(
"Keep new files at or below the limit; files already over it may not grow.",
);
process.exitCode = 1;
}