refactor: 리펙토링

This commit is contained in:
DongHyeonka
2026-08-01 19:39:59 +09:00
parent 9c959ea2a5
commit c6da03369c
171 changed files with 20329 additions and 782 deletions
+104 -13
View File
@@ -8,12 +8,21 @@ import {
isValidCommitSha,
isValidSourceDateEpoch,
} from "./lib/build-environment.ts";
import { classifyGateStepResult } from "./lib/ci-step-result.ts";
type GateStep = Readonly<{
type GateStepBase = Readonly<{
script: string;
args?: readonly string[];
expect: "pass" | "fail";
timeoutMs?: number;
}>;
type GateStep =
| (GateStepBase & Readonly<{ expect: "pass" }>)
| (GateStepBase &
Readonly<{
expect: "fail";
expectedExitCode: number;
expectedDiagnosticId: string;
}>);
type GateDefinition = Readonly<{
name: string;
steps: readonly GateStep[];
@@ -40,6 +49,8 @@ if (!gateId || !gate) {
const output: string[] = [];
let passed = true;
const DEFAULT_STEP_TIMEOUT_MS = 30 * 60 * 1_000;
const MAX_STEP_OUTPUT_BYTES = 16 * 1024 * 1_024;
const gateEnvironment = { ...process.env };
if (gateEnvironment.CI === "true") {
@@ -92,19 +103,49 @@ if (passed) {
const result = spawnSync(
"corepack",
["pnpm", step.script, ...(step.args ?? [])],
{ encoding: "utf8", env: gateEnvironment },
{
encoding: "utf8",
env: gateEnvironment,
timeout: step.timeoutMs ?? DEFAULT_STEP_TIMEOUT_MS,
maxBuffer: MAX_STEP_OUTPUT_BYTES,
},
);
const stdout = result.stdout ?? "";
const stderr = result.stderr ?? "";
output.push(
`$ corepack pnpm ${step.script} ${(step.args ?? []).join(" ")}`.trim(),
result.stdout,
result.stderr,
stdout,
stderr,
);
const exitedSuccessfully = result.status === 0;
const expectationMet =
step.expect === "pass" ? exitedSuccessfully : !exitedSuccessfully;
if (!expectationMet) {
const expectation =
step.expect === "pass"
? ({ kind: "pass" } as const)
: ({
kind: "fail",
expectedExitCode: step.expectedExitCode,
expectedDiagnosticId: step.expectedDiagnosticId,
} as const);
const classification = classifyGateStepResult(expectation, {
status: result.status,
signal: result.signal,
stdout,
stderr,
...(result.error
? { error: { code: (result.error as NodeJS.ErrnoException).code } }
: {}),
});
output.push(`classification: ${classification.kind}`);
if (!classification.expectationMet) {
output.push(
`expectation failed: expected ${step.expect}, exit=${result.status}`,
`expectation failed: expected ${step.expect}, exit=${result.status}, signal=${result.signal ?? "none"}`,
...(step.expect === "fail"
? [
`expected negative fixture identity: exit=${step.expectedExitCode}, diagnostic=${JSON.stringify(step.expectedDiagnosticId)}`,
]
: []),
...(classification.kind === "INFRASTRUCTURE_FAILURE"
? [`infrastructure failure: ${classification.detail}`]
: []),
);
passed = false;
break;
@@ -187,11 +228,61 @@ function parseGateSteps(value: unknown, gateId: string): GateStep[] {
const args =
candidate.args === undefined
? undefined
: parseStringArray(candidate.args, `${gateId}[${index}].args`);
return {
: parseStringArray(candidate.args, `${gateId}[${index}].args`);
const timeoutMs = candidate.timeoutMs;
if (
timeoutMs !== undefined &&
(typeof timeoutMs !== "number" ||
!Number.isSafeInteger(timeoutMs) ||
timeoutMs < 1_000 ||
timeoutMs > 3_600_000)
) {
throw new TypeError(`Invalid CI gate step timeout: ${gateId}[${index}]`);
}
const base = {
script: candidate.script,
expect: candidate.expect,
...(args ? { args } : {}),
...(typeof timeoutMs === "number" ? { timeoutMs } : {}),
};
if (candidate.expect === "pass") {
if (
candidate.expectedExitCode !== undefined ||
candidate.expectedDiagnosticId !== undefined
) {
throw new TypeError(
`Passing CI gate step cannot declare failure identity: ${gateId}[${index}]`,
);
}
return { ...base, expect: "pass" as const };
}
if (
typeof candidate.expectedExitCode !== "number" ||
!Number.isSafeInteger(candidate.expectedExitCode) ||
candidate.expectedExitCode < 1 ||
candidate.expectedExitCode > 255
) {
throw new TypeError(
`Invalid expected failure exit code: ${gateId}[${index}]`,
);
}
const expectedDiagnosticId = candidate.expectedDiagnosticId;
if (
typeof expectedDiagnosticId !== "string" ||
expectedDiagnosticId.trim().length === 0 ||
expectedDiagnosticId.length > 256 ||
["\r", "\n", "\0"].some((character) =>
expectedDiagnosticId.includes(character),
)
) {
throw new TypeError(
`Invalid expected failure diagnostic: ${gateId}[${index}]`,
);
}
return {
...base,
expect: "fail" as const,
expectedExitCode: candidate.expectedExitCode,
expectedDiagnosticId,
};
});
}