实现(08-08-production-deployment-execution): 校准生产身份只读门禁
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-08 17:33:25 +08:00
父节点 6ccb9cd5e3
当前提交 f11e2f2c25
共修改 11 个文件,包含 666 行新增和 30 行删除
@@ -14,6 +14,8 @@ const REFRESH_HELPER = path.join(ROOT_DIR, "scripts/refresh-deployment-contract.
const REFRESH_INSTALLER = path.join(ROOT_DIR, "deploy/trialrun/install-deployment-contract-refresh.sh");
const PRODUCTION_PREFLIGHT = path.join(ROOT_DIR, "deploy/trialrun/production-preflight.mjs");
const PRODUCTION_PREFLIGHT_REMOTE = path.join(ROOT_DIR, "deploy/trialrun/production-preflight-remote.sh");
const PRODUCTION_IDENTITY_BOOTSTRAP = path.join(ROOT_DIR, "deploy/trialrun/production-identity-bootstrap.mjs");
const PRODUCTION_IDENTITY_BOOTSTRAP_REMOTE = path.join(ROOT_DIR, "deploy/trialrun/production-identity-bootstrap-remote.sh");
describe("deployment safety contract", () => {
let fixtureRoot;
@@ -108,13 +110,21 @@ describe("deployment safety contract", () => {
const wrapper = await readFile(path.join(ROOT_DIR, "deploy/trialrun/deploy-existing.sh"), "utf8");
const preflight = await readFile(PRODUCTION_PREFLIGHT, "utf8");
const remote = await readFile(PRODUCTION_PREFLIGHT_REMOTE, "utf8");
const bootstrap = await readFile(PRODUCTION_IDENTITY_BOOTSTRAP, "utf8");
expect(wrapper).toContain("EXPECTED_PRODUCTION_ORIGIN");
expect(wrapper).toContain("EXPECTED_PRODUCTION_DB_NAME");
expect(wrapper).toContain("EXPECTED_PRODUCTION_DB_NAME_SHA256");
expect(wrapper).toContain("EXPECTED_PENDING_MIGRATIONS");
expect(wrapper).toContain("EXPECTED_DEPLOY_SERVER");
expect(wrapper).toContain("EXPECTED_REMOTE_DIR");
expect(wrapper).toContain("EXPECTED_COMPOSE_PROJECT_NAME");
expect(wrapper).toContain("PRODUCTION_READ_ONLY_PREFLIGHT");
expect(wrapper.indexOf('node "$ROOT_DIR/$PRODUCTION_PREFLIGHT_SCRIPT"')).toBeLessThan(
wrapper.indexOf('bash "$ROOT_DIR/deploy/trialrun/deploy.sh"'),
);
expect(wrapper.indexOf('if [[ "${PRODUCTION_READ_ONLY_PREFLIGHT:-0}" == "1" ]]')).toBeLessThan(
wrapper.indexOf('bash "$ROOT_DIR/deploy/trialrun/deploy.sh"'),
);
expect(preflight).toContain("CANDIDATE_MIGRATIONS_CSV");
expect(preflight).toContain("PRISMA_STATUS_OUTPUT_MISSING");
expect(preflight).toContain("PRISMA_STATUS_OUTPUT_UNREADABLE");
@@ -128,6 +138,12 @@ describe("deployment safety contract", () => {
expect(remote).toContain("rollbackWindow=OPEN");
expect(remote).not.toMatch(/\b(prisma migrate reset|prisma db push|prisma migrate resolve)\b/);
expect(remote.indexOf("PRISMA_STATUS_BEGIN")).toBeLessThan(remote.indexOf("_prisma_migrations"));
expect(bootstrap.indexOf("assertExactHistoricalContract")).toBeLessThan(
bootstrap.indexOf("bootstrapDatabaseIdentityHash"),
);
expect(bootstrap.indexOf("bootstrapDatabaseIdentityHash(identity")).toBeLessThan(
bootstrap.indexOf("spawnSync(\"bash\", [DEPLOY_EXISTING_SCRIPT]"),
);
});
it("rejects loopback/staging production identities without invoking SSH", async () => {
@@ -147,6 +163,53 @@ describe("deployment safety contract", () => {
expect(() => target.validateProductionDbName("hyb_oa_regression")).toThrowError(
expect.objectContaining({ code: "PROD_TARGET_DB_NON_PRODUCTION_IDENTITY" }),
);
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "deploy.production.invalid",
expectedRemoteDir: "/srv/hyb-oa-trialrun",
expectedComposeProjectName: "hyb-oa-trialrun",
})).not.toThrow();
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "deploy.production.invalid",
expectedRemoteDir: "/srv/different",
expectedComposeProjectName: "hyb-oa-trialrun",
})).toThrowError(expect.objectContaining({ code: "REMOTE_DIR_MISMATCH" }));
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "different.production.invalid",
expectedRemoteDir: "/srv/hyb-oa-trialrun",
expectedComposeProjectName: "hyb-oa-trialrun",
})).toThrowError(expect.objectContaining({ code: "DEPLOY_SERVER_MISMATCH" }));
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "deploy.production.invalid",
expectedRemoteDir: "/srv/hyb-oa-trialrun",
expectedComposeProjectName: "different",
})).toThrowError(expect.objectContaining({ code: "COMPOSE_PROJECT_NAME_MISMATCH" }));
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "deploy.production.invalid",
expectedRemoteDir: "/srv/hyb-oa-trialrun",
})).toThrowError(expect.objectContaining({ code: "EXPECTED_COMPOSE_PROJECT_NAME_MISSING" }));
expect(() => target.validateExactProductionIdentity({
deployServer: "deploy.staging.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
expectedDeployServer: "deploy.staging.invalid",
expectedRemoteDir: "/srv/hyb-oa-trialrun",
expectedComposeProjectName: "hyb-oa-trialrun",
})).toThrowError(expect.objectContaining({ code: "DEPLOY_SERVER_NON_PRODUCTION_IDENTITY" }));
expect(() => target.validatePrismaStatusText("")).toThrowError(
expect.objectContaining({ code: "PRISMA_STATUS_OUTPUT_UNREADABLE" }),
);
@@ -188,16 +251,19 @@ describe("deployment safety contract", () => {
env: {
...process.env,
PATH: `${fakeBin}${path.delimiter}${process.env.PATH}`,
DEPLOY_SERVER: "deploy.example.invalid",
DEPLOY_SERVER: "deploy.production.invalid",
REMOTE_DIR: remoteDir,
REMOTE_ENV_FILE: envFile,
COMPOSE_FILE: "deploy/trialrun/docker-compose.yml",
COMPOSE_PROJECT_NAME: "production_app",
COMPOSE_PROJECT_NAME: "hyb-oa-trialrun",
POSTGRES_DATA_DIR: postgresDir,
STORAGE_HOST_DIR: storageDir,
EXPECTED_PRODUCTION_ORIGIN: "https://oa.hyinda.com",
EXPECTED_PRODUCTION_DB_NAME: "hyb_oa",
EXPECTED_PRODUCTION_DB_NAME_SHA256: createHash("sha256").update("hyb_oa", "utf8").digest("hex"),
EXPECTED_PENDING_MIGRATIONS: "",
EXPECTED_DEPLOY_SERVER: "deploy.production.invalid",
EXPECTED_REMOTE_DIR: remoteDir,
EXPECTED_COMPOSE_PROJECT_NAME: "hyb-oa-trialrun",
CANDIDATE_COMMIT: candidateCommit,
PRODUCTION_PREFLIGHT_REPORT_DIR: reportDir,
FAKE_SSH_CAPTURE: sshCapture,
@@ -218,6 +284,63 @@ describe("deployment safety contract", () => {
expect(sshCommand).toContain("EXPECTED_PRODUCTION_DB_NAME_SHA256");
});
it("bootstraps only an exact historical identity and a unique refresh probe", async () => {
const bootstrap = await import(pathToFileURL(PRODUCTION_IDENTITY_BOOTSTRAP).href);
const historicalTarget = [
'DEPLOY_SERVER="${DEPLOY_SERVER:-deploy.production.invalid}"',
'REMOTE_DIR="${REMOTE_DIR:-/srv/hyb-oa-trialrun}"',
].join("\n");
const historicalProject = [
'REMOTE_DIR="${REMOTE_DIR:-/srv/hyb-oa-trialrun}"',
'COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-hyb-oa-trialrun}"',
].join("\n");
expect(bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject)).toEqual({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
});
expect(bootstrap.selectUniqueRefreshTarget([
{ target: "ordinary", status: 1, stdout: "" },
{ target: "refresh", status: 0, stdout: "[deployment-contract-refresh][PASS] probe\n" },
])).toBe("refresh");
expect(() => bootstrap.selectUniqueRefreshTarget([])).toThrowError(
expect.objectContaining({ code: "REFRESH_IDENTITY_NOT_FOUND" }),
);
expect(() => bootstrap.selectUniqueRefreshTarget([
{ target: "refresh-a", status: 0, stdout: "[deployment-contract-refresh][PASS] probe" },
{ target: "refresh-b", status: 0, stdout: "[deployment-contract-refresh][PASS] probe" },
])).toThrowError(expect.objectContaining({ code: "REFRESH_IDENTITY_NOT_UNIQUE" }));
const contract = bootstrap.parseDeploymentContract([
"REMOTE_DIR=/srv/hyb-oa-trialrun",
"REMOTE_ENV_FILE=/srv/hyb-oa-trialrun/deploy/trialrun/.env",
"COMPOSE_FILE=deploy/trialrun/docker-compose.yml",
"COMPOSE_PROJECT_NAME=hyb-oa-trialrun",
"POSTGRES_DATA_DIR=/srv/data/postgres",
"STORAGE_HOST_DIR=/srv/data/storage",
].join(" "));
expect(() => bootstrap.assertExactHistoricalContract(
bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject),
contract,
)).not.toThrow();
expect(() => bootstrap.assertExactHistoricalContract(
bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject),
{ ...contract, COMPOSE_PROJECT_NAME: "different" },
)).toThrowError(expect.objectContaining({ code: "SYSTEMD_COMPOSE_PROJECT_MISMATCH" }));
expect(() => bootstrap.parseHistoricalDeploymentIdentity(
historicalTarget,
historicalProject.replace("/srv/hyb-oa-trialrun", "/srv/different"),
)).toThrowError(expect.objectContaining({ code: "HISTORICAL_REMOTE_DIR_DIVERGED" }));
const bootstrapSource = await readFile(PRODUCTION_IDENTITY_BOOTSTRAP, "utf8");
const remoteSource = await readFile(PRODUCTION_IDENTITY_BOOTSTRAP_REMOTE, "utf8");
expect(bootstrapSource).not.toContain("console.log(databaseIdentityHash)");
expect(bootstrapSource).not.toContain("console.log(identity)");
expect(remoteSource).toContain("DB_SHA256");
expect(remoteSource).not.toContain("DB_BASE64");
expect(remoteSource).not.toContain("prisma migrate status");
});
it("runs the remote gate against synthetic status and migration history", async () => {
const fakeBin = path.join(fixtureRoot, "remote-bin");
await mkdir(fakeBin, { recursive: true });
@@ -277,7 +400,7 @@ describe("deployment safety contract", () => {
REMOTE_DIR: remoteDir,
REMOTE_ENV_FILE: envFile,
COMPOSE_FILE: "deploy/trialrun/docker-compose.yml",
COMPOSE_PROJECT_NAME: "production_app",
COMPOSE_PROJECT_NAME: "hyb-oa-trialrun",
POSTGRES_DATA_DIR: postgresDir,
STORAGE_HOST_DIR: storageDir,
EXPECTED_PRODUCTION_ORIGIN_SHA256: hash("https://oa.example.com"),