# 新机器初次部署 SOP

任何新机器（test / UAT / production / dev 替补机）首次拉项目跑 `deploy.sh up`
之前必须先完成以下两步，否则会撞 `prisma db push` 或 `git pull` 静默失败。
详细事故复盘见 `.learnings/2026-05-09-new-machine-deploy-pitfalls.md`。

跟踪自动化进 `setup-production.sh` / `deploy.sh` 的 umbrella：issue #273。

## 必做步骤（缺一不可）

### S1. 配 git credential（让 `git pull` 能 fetch 私有 Gitea）

```bash
git config --global credential.helper store
umask 077
echo "http://hongwei.zhang:${GITEA_API_TOKEN}@43.130.59.228" > ~/.git-credentials
chmod 600 ~/.git-credentials

# 验证
git ls-remote http://43.130.59.228/FFAIWorkspace/workspace 2>&1 | head -1
```

**不配会怎样**：`deploy.sh deploy` 的 `[1/8] 更新代码` 步骤 `git pull` 报
`fatal: could not read Username`，**但整个 deploy.sh 仍 exit 0**——以为成功才发
现 PM2 list 空、服务没起。

### S2. 给 PG 容器装 `citext` 扩展（在 `prisma db push` 之前）

```bash
# 启 PG 容器并 wait healthy
bash scripts/deploy/deploy.sh <env> up postgres

# 装扩展（idempotent，重跑无副作用）
docker exec ${CONTAINER_PREFIX}-postgres psql \
  -U $(docker exec ${CONTAINER_PREFIX}-postgres printenv POSTGRES_USER) \
  -d $(docker exec ${CONTAINER_PREFIX}-postgres printenv POSTGRES_DB) \
  -c "CREATE EXTENSION IF NOT EXISTS citext;"

# 验证
docker exec ${CONTAINER_PREFIX}-postgres psql -U <user> -d <db> \
  -c "SELECT extname FROM pg_extension;"
# 应看到 plpgsql + citext 两条
```

**不装会怎样**：`npm run db:push` 立刻挂：

```
Error: ERROR: type "citext" does not exist
   step=CreateTable { table_id: TableId(133) }
```

**根因**：Prisma schema 用 `@db.Citext`（不区分大小写文本，常见于 email 唯一性），
`citext` 是 PG 扩展，**不在 Prisma 迁移管辖**——Prisma 不会 auto
`CREATE EXTENSION`。dev 上是早期人工装的（容器升级 / 重建后会丢），任何新建容器
都要补。

## 验证：能 `deploy.sh up && db:push` 一把过

S1 + S2 都完成后，正常 `deploy.sh <env> up` → `cd backend && npm run db:push`
应该直接通过，不需要任何手工修复。

## 元教训：rename PR 必须扫"目标命名是否已被占用"

PR #272（dev → test cutover）只扫了"哪些 dev 引用要改成 test"，没扫"目标
`/srv/apps/ffworkspace-test/` 是否已存在"——Phase B 才发现 UAT 早就占用了同一
路径（`docs/ops/01-server-infrastructure.md` 早就写了）。

**任何 mass rename PR 的 plan 阶段必跑**：

```bash
# 现有资源是否已用目标名
ssh ubuntu@<server> 'ls /srv/apps/ | grep -i <new-name>'
grep -rn "<new-domain>" docs/ ops/
docker ps --format "{{.Names}}" | grep -i <new-prefix>
nginx -T | grep -i <new-domain>
```

不只扫"要改的"，还要扫"会碰撞的"。

## 待自动化（issue #273 跟踪）

- [ ] `setup-production.sh` 接 `GITEA_USER` + `GITEA_TOKEN` env 自动配 git credential
- [ ] `deploy.sh deploy` 入口 `git ls-remote origin >/dev/null` fail-fast
- [ ] `deploy.sh up` 的 PG step 启 PG → wait healthy → idempotent
  `CREATE EXTENSION IF NOT EXISTS citext;`
- [ ] 审 deploy.sh 各 step 错误传递（不只 git pull——`set -e` 没覆盖 / `|| true`
  吞错的地方）
