修复(08-08-production-deployment-execution): 持久化远端部署阶段日志
这个提交包含在:
@@ -1460,6 +1460,9 @@ bash scripts/qa/run-automatic-backup-contract.sh
|
||||
- Aggregate read-only preflight: `deploy/trialrun/check-existing-deployment.sh`.
|
||||
- Root-owned forced-command source/installer: `scripts/refresh-deployment-contract.sh` and `deploy/trialrun/install-deployment-contract-refresh.sh`.
|
||||
- Remote preflight: `deploy/trialrun/preflight-remote.sh` executed over SSH before rsync.
|
||||
- Persistent long-stage evidence: repo-relative
|
||||
`artifacts/deployment-runtime/<stage>-<12-char-sha>.log` for `build-init`,
|
||||
`run-init` and `build-switch-server`.
|
||||
- Canonical parameter source: allowlisted `Environment=` values from `hyb-oa-automatic-backup-daily.service`.
|
||||
- Stable image stages: `base`, `client_deps`, `client_build`, `server_deps`, `server_build`.
|
||||
|
||||
@@ -1479,6 +1482,20 @@ bash scripts/qa/run-automatic-backup-contract.sh
|
||||
- After that partial-success state, repair the classified root/program/unit/timer problem, invoke the forced-command `refresh` directly, then rerun aggregate preflight. The application already passed release smoke; do not redeploy it merely to repair the backup build contract, and do not bypass the expected `backup_build` failure while it is stale.
|
||||
- `apk add` and both lockfile `npm ci` steps precede `BUILD_SHA` / `VITE_BUILD_SHA`. Build identity may invalidate application output, never stable dependency installation.
|
||||
- Seed and smoke output reports readiness, counts and booleans; it does not print full login identifiers, contact values, WeCom identifiers, redirect URI or generated credentials.
|
||||
- The three long remote mutation stages write stdout/stderr directly to a
|
||||
remote file instead of relying on the SSH stream as the only copy. The log
|
||||
directory is mode `0700`; each fresh log is mode `0600`. Stage names are a
|
||||
fixed allowlist at call sites, and the filename uses the provenance-verified
|
||||
12-character candidate prefix.
|
||||
- A stage success/failure emits only stage, exact child exit code and a
|
||||
repository-relative `logRef`. Full raw logs remain remote and uncommitted;
|
||||
after failure, retrieve only the needed tail read-only and redact any
|
||||
sensitive content before writing task/chat evidence. Do not automatically
|
||||
dump init/seed logs into ordinary output.
|
||||
- Logs are created after the verified-source rsync. A later rsync may remove
|
||||
old runtime artifacts, so retrieve a failed stage log before any approved
|
||||
retry. Keeping an interactive execution handle is not a substitute for this
|
||||
second copy.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
@@ -1495,16 +1512,28 @@ bash scripts/qa/run-automatic-backup-contract.sh
|
||||
- Post-deploy installer still embeds the previous build SHA, any managed timer is inactive, or the real backup/watchdog fails runtime identity or off-host freshness -> deployment recovery remains incomplete even when `/ready` is green.
|
||||
- A later deployment attempt is blocked by `backup_build` after `DEPLOYED_BUT_CONTRACT_REFRESH_FAILED` -> expected fail-closed behavior; repair with direct forced-command `refresh`, not another application release.
|
||||
- Lockfile unchanged but a new SHA reruns dependency installation -> Dockerfile contract test fails.
|
||||
- Long stage exits non-zero -> preserve its original status and relative log
|
||||
reference, stop under `set -e`, and do not enter the next stage.
|
||||
- SSH stream disappears -> use the already-written remote log for diagnosis;
|
||||
do not repeat the deployment merely to recreate console output.
|
||||
- Runtime log directory/file mode is broader than `0700`/`0600`, stage output
|
||||
has no log reference, or a bare build/init/server-switch call bypasses the
|
||||
wrapper -> deployment evidence contract fails locally.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: wrapper reads the frozen unit contract, preflight passes, Git archive syncs and unchanged dependency layers are cached.
|
||||
- Good: a long stage writes its complete remote log, returns a stable relative
|
||||
reference and preserves the exact non-zero child status even if the
|
||||
interactive stream is incomplete.
|
||||
- Good: ordinary and forced-command identities pass separately, all aggregate items are reported, deployment passes, root helper updates only fixed build drop-ins, and four timers remain enabled/active.
|
||||
- Good: a broken-root recovery creates a clean published-commit source tree, proves one environment candidate against running containers without exposing values, copies it with mode `0600`, deploys with `SYNC_ENV=0`, then updates the backup contracts and completes a real off-host backup.
|
||||
- Base: first initialization uses reviewed explicit parameters and `DEPLOY_DATA_MODE=initialize`; it does not pretend an existing unit is available.
|
||||
- Bad: inspect a running container's `working_dir`, append another `deploy/trialrun`, then discover the mistake only after rsync.
|
||||
- Bad: choose one of several stale `.env` files by path or modification time, use `SYNC_ENV=1`, or stop after `/ready` while the backup unit still expects the previous build.
|
||||
- Bad: grant NOPASSWD to a repository installer, point forced-command at a deploy-user-writable script, reuse an unrestricted root key, stop aggregation after its first failure, or downgrade refresh failure to a warning.
|
||||
- Bad: run `docker compose build/run/up` as bare SSH-stream-only calls, lose the
|
||||
terminal tail, then retry production solely to recover diagnostics.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
@@ -1513,6 +1542,9 @@ bash scripts/qa/run-automatic-backup-contract.sh
|
||||
- `node deploy/trialrun/validate-env-contract.js`.
|
||||
- `cd server && npm test -- tests/unit/scripts/deployment-safety-contract.test.js`.
|
||||
- Synthetic fixtures must prove wrong root, missing/symlink env and invalid data fail; static ordering must prove preflight precedes rsync and dependency stages precede build args.
|
||||
- Static deployment tests must prove all three long stages use the persistent
|
||||
wrapper, output is redirected to the mode-`0600` file, the error branch
|
||||
captures `$?` immediately, and the former bare calls are absent.
|
||||
- A controlled recovery must additionally record: exact published source commit, per-key PASS set/date without values, byte-copy/owner/mode PASS, new-SHA service/timer contract PASS, a successful complete backup, and a successful off-host freshness check. Old candidates remain untouched until a separately approved cleanup.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
@@ -1538,6 +1570,14 @@ DEPLOY_SERVER=<approved-ssh-target> \
|
||||
# recovery runbook to prove one complete backup and off-host freshness.
|
||||
```
|
||||
|
||||
```bash
|
||||
# Wrong: the SSH stream is the only build evidence.
|
||||
"${compose_cmd[@]}" build init
|
||||
|
||||
# Correct: the remote file is authoritative and the stream carries a safe ref.
|
||||
run_logged_deploy_stage "build-init" "${compose_cmd[@]}" build init
|
||||
```
|
||||
|
||||
## Scenario: Production Migration-First Target Gate
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
在新工单中引用
屏蔽一个用户