发布可以跟上只多了文档的测试环境头

四个生产工作流改用同一条发布修订:最近一次代码门禁成功的提交可以发布,后面只多文档不再挡住。生产环境拒绝测试验证码。63 个还没上生产的迁移写了分级、演练脚本和首发步骤。没有点任何生产按钮。

自测:发布修订 7 通过,演练安全 3 通过,tsc 0 错,lint 0 error。本机空库演练在服务角色读人物表处按设计停止(退出码 4,其他人物已确认行数为 0)。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-14 10:24:40 +08:00
co-authored by Grok (agent loop)
parent f4c53e7f9f
commit 00e6efdbfa
23 changed files with 1470 additions and 174 deletions
+3 -3
View File
@@ -140,7 +140,7 @@ It fails while `frontend/src/lib/legal-entity.ts` still holds placeholder operat
## Manual production deployment with Gitea Actions
GitHub production deployment is retired. Production changes are released only by manually dispatching `.gitea/workflows/deploy-production.yml`. The workflow requires an exact SHA shared by `main` and `staging`, an exact-SHA staging push gate and image manifest, the manual release gate, and matching public staging health. It deploys immutable registry digests and never builds application images on the production host.
GitHub production deployment is retired. Production changes are released only by manually dispatching `.gitea/workflows/deploy-production.yml`. `main` must be a fast-forward to the current staging head `H`. The deployed SHA is `R`, the latest successful staging push of `backend-quality-gate`, and `deploy/is-docs-only-range.sh R H` must exit 0 (exit 1 or 2 both refuse). Controller artifacts and image digests come from `R`. Public staging health must report `R`. The manual release gate may record `H` when `H` is docs-only ahead of `R`. Rollback still requires an explicit confirmation, a gate run, and ancestry inside reviewed `main` history. The workflow deploys immutable registry digests and never builds application images on the production host.
The `internal` verification mode is for pre-DNS checks on the new host. The `public` mode is for the same SHA after authoritative DNS and Caddy TLS converge. Neither mode imports data, applies pending migrations, or changes DNS.
@@ -281,13 +281,13 @@ Use this order for every staging revision:
5. Confirm `https://staging.jyotisha.chat/api/health` and verify that its deployment SHA is the SHA from step 2.
6. After health verification, create the local encrypted backup described below.
**Deploy Production** and **Migrate Production Database** stay fully manual. They still require a hand-filled 40-character SHA plus `allow_rollback` / recovery attestation (`restore_verified` is a person's guarantee that the recovery point restores). Do not copy the staging automatic migrate-then-deploy shortcut onto production.
**Deploy Production** and **Migrate Production Database** stay fully manual. They still require a hand-filled 40-character SHA. For a forward release that SHA is `R` (the latest gated staging push that is docs-only behind the shared `main`/`staging` head), plus `allow_rollback` / recovery attestation (`restore_verified` is a person's guarantee that the recovery point restores). Do not copy the staging automatic migrate-then-deploy shortcut onto production.
The deploy and migration workflows share the `staging-mutation` Actions concurrency group, and their live-tree sync plus Compose work runs under `/opt/jyotisha-staging/.state/mutation.lock`. The synchronized tree explicitly preserves `/backups/`, `.env*`, `.state`, and `.incoming`. The read-only checker still exits before app changes when a migration is pending. After the automatic pre-deploy migrate that should be rare. A failed automatic migration fails the quality gate and does not dispatch deployment. Application rollback restores the previously recorded digest references and SHA, falling back to validated local image IDs only when transitioning from the pre-foundation local-image deployment; it does not roll back database state.
### Production recovery point before schema migration
Use Gitea Actions → **Create Production Recovery Point** from the exact current `main` release SHA before every production schema migration. The manual workflow requires `main`, `staging`, public staging health, and the successful release gate to identify the same SHA. It uses the pinned production SSH identity, shares the `production-mutation` lock, and runs `deploy/run-production-recovery.sh` with shell tracing disabled.
Use Gitea Actions → **Create Production Recovery Point** before every production schema migration. Dispatch it from current `main` (the staging head `H`) and set `deploy_sha` to `R`. `main` and `staging` must be the same SHA, `R` must be a docs-only ancestor of that SHA, public staging health must report `R`, and the release gate must have succeeded for `R` or for that same head. It uses the pinned production SSH identity, shares the `production-mutation` lock, and runs `deploy/run-production-recovery.sh` with shell tracing disabled. The migration rehearsal has no Gitea button; the product runs `deploy/rehearse-production-migrations.sh` from the runbook.
The host script writes an AES-256-CBC/PBKDF2 encrypted custom-format PostgreSQL dump under `/opt/jyotisha-production/backups`, restores it into a uniquely named disposable database, validates non-sensitive row/table counts, removes only that disposable database, and writes a mode-`0600` verification manifest. Before acquiring the shared host lock it validates that `.state` and `backups` are real directories, then reuses the existing passwordless Docker boundary to run the already-loaded PostgreSQL image with no network, a read-only root filesystem, all capabilities dropped except `CHOWN`, and only the two directories plus the verified existing lock inode bind-mounted. That helper exposes the regular `mutation.lock` directly at `/mutation.lock`, restores that inode before changing its mode-`0700` parent directory, then restores the two directory mount points to the current `deploy` UID/GID. The direct file mount avoids depending on traversal through a parent left half-repaired by an earlier failed run, without adding `DAC_OVERRIDE`; it never recursively changes backup files or removes or replaces a lock inode. The workflow retrieves only the encrypted dump and verification metadata, verifies the SHA-256 digest, and uploads them as a 30-day Gitea Actions artifact with compression disabled. The artifact-backed `recovery_reference`, `recovery_created_at`, and `restore_verified=true` output are the inputs for **Migrate Production Database**. Do not use the attestation if backup, restore, retrieval, digest validation, ownership normalization, or artifact upload fails.