修正(08-08-production-deployment-execution): 先验生产公网身份再冻结目录
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-08 19:36:32 +08:00
父节点 eb76de7c98
当前提交 bf2631c0d4
共修改 8 个文件,包含 151 行新增和 48 行删除
@@ -138,10 +138,19 @@ 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("assertContractForOriginVerification")).toBeLessThan(
bootstrap.indexOf("verifyOriginIdentity(identity, contract, expectedOrigin);"),
);
expect(bootstrap.indexOf("bootstrapDatabaseIdentityHash(identity")).toBeLessThan(
expect(bootstrap.indexOf("verifyOriginIdentity(identity, contract, expectedOrigin);")).toBeLessThan(
bootstrap.indexOf("const expectedRemoteDir = contract.REMOTE_DIR"),
);
expect(bootstrap.indexOf("const expectedRemoteDir = contract.REMOTE_DIR")).toBeLessThan(
bootstrap.indexOf("const databaseIdentityHash = bootstrapDatabaseIdentityHash"),
);
expect(bootstrap.indexOf('if (identityOnly === "1")')).toBeLessThan(
bootstrap.indexOf("const databaseIdentityHash = bootstrapDatabaseIdentityHash"),
);
expect(bootstrap.indexOf("const databaseIdentityHash = bootstrapDatabaseIdentityHash")).toBeLessThan(
bootstrap.indexOf("spawnSync(\"bash\", [DEPLOY_EXISTING_SCRIPT]"),
);
});
@@ -296,7 +305,6 @@ describe("deployment safety contract", () => {
].join("\n");
expect(bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject)).toEqual({
deployServer: "deploy.production.invalid",
remoteDir: "/srv/hyb-oa-trialrun",
composeProjectName: "hyb-oa-trialrun",
});
expect(bootstrap.selectUniqueRefreshTarget([
@@ -319,18 +327,27 @@ describe("deployment safety contract", () => {
"POSTGRES_DATA_DIR=/srv/data/postgres",
"STORAGE_HOST_DIR=/srv/data/storage",
].join(" "));
expect(() => bootstrap.assertExactHistoricalContract(
expect(() => bootstrap.assertContractForOriginVerification(
bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject),
contract,
)).not.toThrow();
expect(() => bootstrap.assertExactHistoricalContract(
const relocatedContract = {
...contract,
REMOTE_DIR: "/srv/current-production",
REMOTE_ENV_FILE: "/srv/current-production/deploy/trialrun/.env",
};
expect(() => bootstrap.assertContractForOriginVerification(
bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject),
relocatedContract,
)).not.toThrow();
expect(() => bootstrap.assertContractForOriginVerification(
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" }));
expect(() => bootstrap.assertContractForOriginVerification(
bootstrap.parseHistoricalDeploymentIdentity(historicalTarget, historicalProject),
{ ...contract, REMOTE_ENV_FILE: "/srv/different/.env" },
)).toThrowError(expect.objectContaining({ code: "SYSTEMD_REMOTE_ENV_FILE_MISMATCH" }));
const bootstrapSource = await readFile(PRODUCTION_IDENTITY_BOOTSTRAP, "utf8");
const remoteSource = await readFile(PRODUCTION_IDENTITY_BOOTSTRAP_REMOTE, "utf8");
@@ -339,6 +356,46 @@ describe("deployment safety contract", () => {
expect(remoteSource).toContain("DB_SHA256");
expect(remoteSource).not.toContain("DB_BASE64");
expect(remoteSource).not.toContain("prisma migrate status");
expect(remoteSource.indexOf('IDENTITY_BOOTSTRAP_MODE" == "origin"')).toBeLessThan(
remoteSource.indexOf("actual_db_name="),
);
});
it("verifies both origin samples before reading the remote database name", async () => {
const fakeBin = path.join(fixtureRoot, "origin-bin");
await mkdir(fakeBin, { recursive: true });
await writeFile(path.join(fakeBin, "docker"), [
"#!/usr/bin/env bash",
"set -euo pipefail",
"joined=\"$*\"",
"if [[ \"$1\" == compose && \"${2:-}\" == version ]]; then exit 0; fi",
"if [[ \"$1\" == ps ]]; then printf 'server1\\n'; exit 0; fi",
"if [[ \"$1\" == inspect ]]; then printf 'APP_ORIGIN=https://example.invalid\\n'; exit 0; fi",
"printf 'unexpected fake docker call: %s\\n' \"$joined\" >&2",
"exit 97",
"",
].join("\n"), { mode: 0o700 });
await chmod(path.join(fakeBin, "docker"), 0o700);
const hash = createHash("sha256").update("https://example.invalid", "utf8").digest("hex");
const runBootstrapRemote = (mode) => execFileAsync("bash", [PRODUCTION_IDENTITY_BOOTSTRAP_REMOTE], {
cwd: remoteDir,
env: {
...process.env,
PATH: `${fakeBin}${path.delimiter}${process.env.PATH}`,
IDENTITY_BOOTSTRAP_MODE: mode,
REMOTE_DIR: remoteDir,
REMOTE_ENV_FILE: envFile,
COMPOSE_FILE: "deploy/trialrun/docker-compose.yml",
COMPOSE_PROJECT_NAME: "hyb-oa-trialrun",
EXPECTED_PRODUCTION_ORIGIN_SHA256: hash,
},
});
await expect(runBootstrapRemote("origin")).resolves.toMatchObject({
stdout: "[production-identity-bootstrap][PASS] origin_identity\n",
});
await expect(runBootstrapRemote("database")).rejects.toMatchObject({
stderr: expect.stringContaining("PROD_TARGET_DB_NAME_MISSING"),
});
});
it("runs the remote gate against synthetic status and migration history", async () => {