完善(08-09-deployment-contract-single-source-harness): 收敛部署合同并补失败路径
这个提交包含在:
@@ -259,6 +259,95 @@ SYNC_ENV=0 POSTGRES_DATA_DIR=/srv/hyb-oa/pgdata \
|
||||
bash deploy/trialrun/deploy.sh
|
||||
```
|
||||
|
||||
## Scenario: Candidate Build Identity Single Source
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: changing candidate provenance, `deploy.sh`, production preflight or
|
||||
Compose build-SHA wiring.
|
||||
- Purpose: prevent the full commit, its 12-character public prefix and the two
|
||||
Compose environment keys from drifting between deployment entry points.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- Shared function:
|
||||
`deriveCandidateBuildEnv(commit) -> Object.freeze({ BUILD_SHA, VITE_BUILD_SHA })`.
|
||||
- Provenance CLI keeps `commit` and `shortCommit` and adds the same `buildEnv`
|
||||
object.
|
||||
- Consumers: `deploy/trialrun/deploy.sh` and
|
||||
`deploy/trialrun/production-preflight.mjs`.
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- The shared function accepts only a lowercase 40–64 character hexadecimal
|
||||
commit and returns both environment values in one object;
|
||||
`VITE_BUILD_SHA` is exactly the first 12 characters of `BUILD_SHA`.
|
||||
- Consumers traverse and inject the complete object. They must not call
|
||||
`git rev-parse HEAD`, slice the commit again, define an independent key list
|
||||
or selectively consume one build key.
|
||||
- `production-preflight-remote.sh` remains an independent consumption guard:
|
||||
before `docker compose config` and Prisma status, it requires both keys,
|
||||
validates both formats and checks the prefix relationship. A single local
|
||||
source does not replace transport and caller validation.
|
||||
- Preflight writes the stable redacted summary and report reference before it
|
||||
classifies a remote failure. A non-zero remote exit takes precedence over a
|
||||
missing Prisma marker.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Invalid candidate commit -> `CANDIDATE_COMMIT_INVALID` locally; no SSH.
|
||||
- Missing or malformed remote build value -> `BUILD_SHA_MISSING`,
|
||||
`PROD_TARGET_BUILD_SHA_INVALID` or `PROD_TARGET_VITE_BUILD_SHA_INVALID`;
|
||||
no Compose/Prisma call.
|
||||
- Valid values with a mismatched prefix -> `PROD_TARGET_BUILD_SHA_MISMATCH`;
|
||||
no Compose/Prisma call.
|
||||
- Compose config failure -> `PROD_TARGET_COMPOSE_CONFIG_INVALID`; no Prisma
|
||||
status or migration command.
|
||||
- Remote non-zero with `code=...` -> propagate that code even without a Prisma
|
||||
marker or with an unreadable marker body; remote non-zero without a parseable code ->
|
||||
`PRODUCTION_PREFLIGHT_REMOTE_FAILED`.
|
||||
- Remote zero without a Prisma marker -> `PRISMA_STATUS_OUTPUT_MISSING`.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: provenance derives one frozen object; both entry points inject all of
|
||||
it; the remote helper independently validates what arrived.
|
||||
- Base: the existing provenance CLI fields remain compatible and equal the two
|
||||
object values.
|
||||
- Bad: preflight uses `candidateCommit.slice(0, 12)`, or deployment reads only
|
||||
one property and reconstructs the other.
|
||||
- Bad: check the Prisma marker before a non-zero remote status and thereby hide
|
||||
the remote safety code.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- Unit-test fixed keys, immutability, the 12-character relationship and invalid
|
||||
commit inputs.
|
||||
- Execute the real production-preflight Node subprocess behind fake SSH for all
|
||||
four status/marker quadrants: non-zero with code and no marker, non-zero with
|
||||
code and marker, non-zero without code, and zero without marker. Assert the
|
||||
stable summary and report reference exist on every failure.
|
||||
- Run the remote helper behind an allowlisted fake Docker and prove build-value
|
||||
and Compose failures occur before any Prisma `exec` or status call.
|
||||
- Mutation-check both historical defects: reversing status/marker precedence
|
||||
and removing either shared build key must make the relevant tests fail.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```js
|
||||
const buildSha = candidateCommit;
|
||||
const viteBuildSha = candidateCommit.slice(0, 12);
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```js
|
||||
const candidateBuildEnv = deriveCandidateBuildEnv(candidateCommit);
|
||||
const assignments = Object.entries(candidateBuildEnv);
|
||||
```
|
||||
|
||||
## Scenario: Public Smoke Navigation Labels
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户