176 lines
8.3 KiB
JavaScript
176 lines
8.3 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import { createHash } from "node:crypto";
|
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
|
|
import {
|
|
fileSha256,
|
|
readJson,
|
|
validateCandidateReference,
|
|
validateResendEvidence,
|
|
} from "./lib/resend-release-gate.mjs";
|
|
|
|
const phaseIndex = process.argv.indexOf("--phase");
|
|
const phase = phaseIndex >= 0 ? process.argv[phaseIndex + 1] : "green";
|
|
if (!new Set(["red", "green"]).has(phase)) throw new Error(`Unsupported phase: ${phase}`);
|
|
|
|
const candidateRunId = process.env.DADA_WP7_01_RUN_ID ?? "wp7-01-candidate-20260804052447717";
|
|
const runId = process.env.DADA_TDD_RUN_ID ?? `wp7-03-${phase}-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
|
|
const runDirectory = resolve("artifacts", "tdd", runId);
|
|
const caseId = "TDD-WP7-EXT-002-real-resend";
|
|
const caseDirectory = resolve(runDirectory, "cases", caseId);
|
|
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
|
|
mkdirSync(caseDirectory, { recursive: true });
|
|
|
|
function writeJson(path, value) {
|
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
}
|
|
|
|
function runCommand(name, command) {
|
|
const startedAt = new Date().toISOString();
|
|
const result = spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", command], {
|
|
encoding: "utf8",
|
|
env: process.env,
|
|
maxBuffer: 40 * 1024 * 1024,
|
|
stdio: "inherit",
|
|
});
|
|
return {
|
|
command,
|
|
exit_code: result.status ?? 1,
|
|
finished_at: new Date().toISOString(),
|
|
name,
|
|
started_at: startedAt,
|
|
};
|
|
}
|
|
|
|
const candidateEvidenceRoot = resolve(
|
|
process.env.DADA_WP7_01_EVIDENCE_DIR ?? join("artifacts", "tdd", candidateRunId),
|
|
);
|
|
const candidate = readJson(join(candidateEvidenceRoot, "release-candidate.json"));
|
|
const candidateErrors = candidate ? validateCandidateReference(candidate) : ["candidate_record_missing"];
|
|
writeJson(resolve(caseDirectory, "candidate-reference.json"), candidate ? {
|
|
browsers: candidate.browsers.map(({ brand, full_version: fullVersion, major }) => ({ brand, full_version: fullVersion, major })),
|
|
build_commit: candidate.build_commit,
|
|
candidate_status: candidate.status,
|
|
final_release: candidate.final_release,
|
|
fixed_port: candidate.fixed_port,
|
|
run_id: candidateRunId,
|
|
schema_version: "1.0",
|
|
} : {
|
|
candidate_status: "not_available",
|
|
reason: "candidate_record_missing",
|
|
run_id: candidateRunId,
|
|
schema_version: "1.0",
|
|
});
|
|
|
|
const commands = phase === "red" ? [] : [
|
|
["workspace-build", "pnpm build:workspace-packages"],
|
|
["integration", "pnpm test:integration"],
|
|
["api", "pnpm check:openapi && pnpm exec vitest run tests/api --maxWorkers=1"],
|
|
["security", "pnpm test:security"],
|
|
["tdd-trace", "pnpm validate:tdd-trace"],
|
|
].map(([name, command]) => runCommand(name, command));
|
|
const localGatePassed = phase === "red" || commands.length > 0 && commands.every(({ exit_code }) => exit_code === 0);
|
|
|
|
const redaction = {
|
|
absolute_paths_in_evidence: false,
|
|
credentials_in_evidence: false,
|
|
forbidden_matches: 0,
|
|
mailboxes_in_evidence: false,
|
|
private_content_in_evidence: false,
|
|
schema_version: "1.0",
|
|
status: "passed",
|
|
};
|
|
const externalRoot = process.env.DADA_RESEND_EVIDENCE_DIR ? resolve(process.env.DADA_RESEND_EVIDENCE_DIR) : undefined;
|
|
const realAuthorized = process.env.DADA_RESEND_REAL_AUTHORIZED === "1";
|
|
const externalFiles = ["domain-check.json", "delivery-summary.json", "auth-result.json", "redaction.json"];
|
|
const externalEvidence = externalRoot
|
|
? Object.fromEntries(externalFiles.map((file) => [file, readJson(join(externalRoot, file))]))
|
|
: {};
|
|
const externalEvidenceMissing = externalRoot ? externalFiles.filter((file) => !externalEvidence[file]) : externalFiles;
|
|
const externalErrors = externalRoot && externalEvidenceMissing.length === 0 && candidate
|
|
? validateResendEvidence({
|
|
authResult: externalEvidence["auth-result.json"],
|
|
candidate,
|
|
deliverySummary: externalEvidence["delivery-summary.json"],
|
|
domainCheck: externalEvidence["domain-check.json"],
|
|
redaction: externalEvidence["redaction.json"],
|
|
})
|
|
: [];
|
|
|
|
const externalBlockers = [];
|
|
if (candidateErrors.length > 0) externalBlockers.push("candidate_record_unavailable_or_drifted");
|
|
if (!realAuthorized) externalBlockers.push("controlled_domain_or_real_mailbox_authorization_absent");
|
|
if (!externalRoot) externalBlockers.push("real_resend_evidence_not_supplied");
|
|
if (externalRoot && externalEvidenceMissing.length > 0) externalBlockers.push("real_resend_evidence_incomplete");
|
|
if (externalErrors.length > 0) externalBlockers.push("real_resend_evidence_invalid");
|
|
|
|
if (phase === "red") {
|
|
writeJson(resolve(caseDirectory, "red-observation.json"), {
|
|
expected_failure: "Resend SPF/DKIM, free-rule, delivery, and formal authentication evidence is absent before TASK-WP7-03.",
|
|
observed_commands: ["pnpm test:wp7-03:red"],
|
|
red_reason: "TDD-WP7-EXT-002 requires controlled real domain/mailbox evidence; mock or pre-seeded accounts are not release evidence.",
|
|
status: "red_confirmed",
|
|
});
|
|
} else if (externalRoot && externalEvidenceMissing.length === 0 && candidateErrors.length === 0 && realAuthorized && externalErrors.length === 0) {
|
|
for (const file of externalFiles) writeJson(resolve(caseDirectory, file), externalEvidence[file]);
|
|
writeJson(resolve(caseDirectory, "source-hashes.json"), {
|
|
files: Object.fromEntries(externalFiles.map((file) => [file, fileSha256(join(externalRoot, file))])),
|
|
schema_version: "1.0",
|
|
});
|
|
} else {
|
|
writeJson(resolve(caseDirectory, "blocker.json"), {
|
|
blockers: externalErrors.length > 0 ? ["real_resend_evidence_invalid"] : externalBlockers,
|
|
mock_accepted_as_evidence: false,
|
|
paid_fallback_enabled: false,
|
|
preseeded_accounts_accepted_as_evidence: false,
|
|
real_calls_started: false,
|
|
schema_version: "1.0",
|
|
status: "externally_blocked",
|
|
});
|
|
writeJson(resolve(caseDirectory, "redaction.json"), redaction);
|
|
writeJson(resolve(caseDirectory, "source-hashes.json"), { files: {}, schema_version: "1.0" });
|
|
}
|
|
|
|
const expectedEvidence = phase === "red"
|
|
? ["candidate-reference.json", "red-observation.json"]
|
|
: externalRoot && externalEvidenceMissing.length === 0 && candidateErrors.length === 0 && realAuthorized && externalErrors.length === 0
|
|
? ["candidate-reference.json", "domain-check.json", "delivery-summary.json", "auth-result.json", "redaction.json", "source-hashes.json"]
|
|
: ["candidate-reference.json", "blocker.json", "redaction.json", "source-hashes.json"];
|
|
const missingEvidence = expectedEvidence.filter((file) => !existsSync(resolve(caseDirectory, file)));
|
|
const status = phase === "red"
|
|
? localGatePassed && missingEvidence.length === 0 ? "red_confirmed" : "failed"
|
|
: !localGatePassed || missingEvidence.length > 0 ? "failed"
|
|
: realAuthorized && (!externalRoot || externalEvidenceMissing.length > 0 || externalErrors.length > 0) ? "failed"
|
|
: externalRoot && externalEvidenceMissing.length === 0 && candidateErrors.length === 0 && realAuthorized && externalErrors.length === 0 ? "passed"
|
|
: "externally_blocked";
|
|
const manifest = {
|
|
path: "tasks.manifest.json",
|
|
sha256: createHash("sha256").update(readFileSync("tasks.manifest.json")).digest("hex").toUpperCase(),
|
|
};
|
|
const result = {
|
|
acceptance_criteria: ["AC-01", "AC-33", "AC-41", "AC-47", "AC-49"],
|
|
automation: ["controlled_real", "manual_review"],
|
|
candidate_run_id: candidateRunId,
|
|
commit: spawnSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).stdout.trim(),
|
|
evidence_refs: expectedEvidence,
|
|
external_blockers: phase === "green" && status === "externally_blocked" ? externalBlockers : [],
|
|
finished_at: new Date().toISOString(),
|
|
layer: ["EXT-REAL", "MANUAL"],
|
|
manifest,
|
|
missing_evidence: missingEvidence,
|
|
phase,
|
|
requirements: ["AUTH-01", "AUTH-02", "AUTH-07"],
|
|
run_id: runId,
|
|
schema_version: "1.0",
|
|
status,
|
|
task_id: "TASK-WP7-03",
|
|
test_id: caseId,
|
|
work_package: "WP-7",
|
|
};
|
|
writeJson(resolve(caseDirectory, "commands.json"), { commands, phase, run_id: runId, schema_version: "1.0" });
|
|
writeJson(resolve(caseDirectory, "result.json"), result);
|
|
writeJson(resolve(runDirectory, "evidence.json"), { cases: [{ external_blockers: result.external_blockers, missing_evidence: missingEvidence, status, test_id: caseId }], phase, run_id: runId, status });
|
|
console.log(JSON.stringify({ external_blockers: result.external_blockers, phase, run_id: runId, status }, null, 2));
|
|
if (status === "failed") process.exit(1);
|