mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
Implements improvements 1-4 from the v3.8.36 release benchmark (_tasks/release-bench/v3.8.36/PLANO-MELHORIA.md): 1. Quality Ratchet decoupled from flaky coverage (ci.yml): the shard→coverage→ratchet chain meant a single flaky Coverage Shard SKIPPED the whole Quality Ratchet on the release PR (v3.8.36 #4854), so cycle drift only surfaced post-merge in #5029. The job now runs on !cancelled(); coverage download is continue-on-error and the ratchet runs --allow-missing, so the DETERMINISTIC gates (eslint/complexity/cognitive/duplication/ codeql) stay blocking even when coverage is unavailable. 2. Fast-path drift gates (quality.yml PR→release): added check:complexity, check:cognitive-complexity, and a new lightweight check:pack-policy (pack-artifact unexpected-files check WITHOUT a build, via --policy-only) so drift + stray-tarball-file regressions are caught/rebaselined PER-PR instead of cascading onto the release PR. 3. Build heap default 4096→8192 MB (build-next-isolated.mjs): the clean graph peaks ~3.9 GB and brushed the old 4 GB ceiling; 8 GB gives headroom. Comment notes heap is NOT the fix for a poisoned scope (run check:build-scope instead). 4. check:build-scope gate (new): fails if .ts/.tsx/.js/.jsx files in the tsconfig scope exceed a threshold — catches worktrees/cruft leaking into the build scope (the v3.8.36 OOM root cause: 355,215 vs 4,547 files) BEFORE it detonates next build. Wired into the fast-path.
126 lines
4.5 KiB
JavaScript
126 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { execFileSync } from "node:child_process";
|
|
import { existsSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import {
|
|
PACK_ARTIFACT_ALLOWED_EXACT_PATHS,
|
|
PACK_ARTIFACT_ALLOWED_PATH_PREFIXES,
|
|
PACK_ARTIFACT_REQUIRED_PATHS,
|
|
findMissingArtifactPaths,
|
|
findUnexpectedArtifactPaths,
|
|
} from "./pack-artifact-policy.ts";
|
|
|
|
const __filename: string = fileURLToPath(import.meta.url);
|
|
const __dirname: string = dirname(__filename);
|
|
const ROOT: string = join(__dirname, "..", "..");
|
|
const npmCommand: string = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
|
|
function runNpm(args: string[], stdio: "inherit" | "pipe" = "pipe"): string {
|
|
const npmExecPath = process.env.npm_execpath;
|
|
const command = npmExecPath ? process.execPath : npmCommand;
|
|
return execFileSync(command, [...(npmExecPath ? [npmExecPath] : []), ...args], {
|
|
cwd: ROOT,
|
|
encoding: "utf8",
|
|
stdio: stdio === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
maxBuffer: 64 * 1024 * 1024,
|
|
});
|
|
}
|
|
|
|
function ensureAppStagingReady(): void {
|
|
const missingAppRequiredPaths = PACK_ARTIFACT_REQUIRED_PATHS.filter((requiredPath) =>
|
|
requiredPath.startsWith("dist/")
|
|
).filter((requiredPath) => !existsSync(join(ROOT, requiredPath)));
|
|
|
|
if (missingAppRequiredPaths.length === 0) return;
|
|
|
|
console.log("📦 dist/ staging is missing required runtime files; running npm run build:cli...");
|
|
runNpm(["run", "build:cli"], "inherit");
|
|
}
|
|
|
|
function runPackDryRun(): any {
|
|
const output = runNpm(["pack", "--dry-run", "--json", "--ignore-scripts"]);
|
|
|
|
const jsonStart = output.indexOf("[");
|
|
const jsonEnd = output.lastIndexOf("]");
|
|
const jsonPayload =
|
|
jsonStart >= 0 && jsonEnd > jsonStart ? output.slice(jsonStart, jsonEnd + 1) : output;
|
|
const parsed = JSON.parse(jsonPayload);
|
|
const packReport = Array.isArray(parsed) ? parsed[0] : null;
|
|
|
|
if (!packReport || !Array.isArray(packReport.files)) {
|
|
throw new Error("npm pack --dry-run --json did not return the expected files[] payload.");
|
|
}
|
|
|
|
return packReport;
|
|
}
|
|
|
|
function formatBytes(bytes: number): string {
|
|
if (!Number.isFinite(bytes) || bytes < 1024) {
|
|
return `${bytes || 0} B`;
|
|
}
|
|
|
|
const units = ["KB", "MB", "GB"];
|
|
let value = bytes / 1024;
|
|
let unitIndex = 0;
|
|
|
|
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
value /= 1024;
|
|
unitIndex++;
|
|
}
|
|
|
|
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unitIndex]}`;
|
|
}
|
|
|
|
// --policy-only: skip the build (ensureAppStagingReady → build:cli) and the
|
|
// required-runtime-files check (which needs the built dist/), running ONLY the
|
|
// unexpected-files allowlist check. The unexpected files (e.g. stray bin/*.sh) are
|
|
// SOURCE files that `npm pack --dry-run` lists regardless of build, so this catches
|
|
// the "new file leaked into the tarball" regression cheaply on the fast-path (PR→release),
|
|
// instead of only on the release PR's full Package Artifact job. See incident v3.8.36 (#5029).
|
|
const POLICY_ONLY = process.argv.includes("--policy-only");
|
|
|
|
try {
|
|
if (!POLICY_ONLY) ensureAppStagingReady();
|
|
const packReport = runPackDryRun();
|
|
const artifactPaths: string[] = packReport.files.map((file: any) => file.path);
|
|
const unexpectedPaths: string[] = findUnexpectedArtifactPaths(artifactPaths, {
|
|
exactPaths: PACK_ARTIFACT_ALLOWED_EXACT_PATHS,
|
|
prefixPaths: PACK_ARTIFACT_ALLOWED_PATH_PREFIXES,
|
|
});
|
|
const missingRequiredPaths: string[] = POLICY_ONLY
|
|
? []
|
|
: findMissingArtifactPaths(artifactPaths, PACK_ARTIFACT_REQUIRED_PATHS);
|
|
|
|
console.log("📦 npm pack artifact summary");
|
|
console.log(` File: ${packReport.filename}`);
|
|
console.log(` Entry count: ${packReport.entryCount}`);
|
|
console.log(` Packed size: ${formatBytes(packReport.size)}`);
|
|
console.log(` Unpacked size: ${formatBytes(packReport.unpackedSize)}`);
|
|
|
|
if (unexpectedPaths.length > 0) {
|
|
console.error("\n❌ Unexpected files were found in the npm publish artifact:");
|
|
for (const unexpectedPath of unexpectedPaths) {
|
|
console.error(` - ${unexpectedPath}`);
|
|
}
|
|
}
|
|
|
|
if (missingRequiredPaths.length > 0) {
|
|
console.error("\n❌ Required runtime files are missing from the npm publish artifact:");
|
|
for (const missingPath of missingRequiredPaths) {
|
|
console.error(` - ${missingPath}`);
|
|
}
|
|
}
|
|
|
|
if (unexpectedPaths.length > 0 || missingRequiredPaths.length > 0) {
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log("\n✅ Pack artifact policy check passed.");
|
|
} catch (error) {
|
|
console.error(`\n❌ Pack artifact validation failed: ${error.message}`);
|
|
process.exit(1);
|
|
}
|