Cursor Agent 9251fc5c0a fix(ops): keep git reset output off deploy SHA capture
git reset --hard prints 'HEAD is now at …' to stdout, which polluted
read -r previous_sha target_sha < <(git_fetch_and_checkout) and wrote
invalid values into .evo-deploy/state.json. Redirect git stdout to
stderr inside git_fetch_and_checkout and rollback; validate SHAs before
write_state and rollback.

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
2026-09-24 04:41:42 +00:00

110 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Evo auto-deploy (evOo-local)
Idempotent deploy helper for the **source checkout** on the Evo host (`~/Projects/evOo-local`, user `cynar`). Pulls `main` (or a pinned SHA), runs migrations, restarts the existing stack, health-checks API `:3100` and web `:5173`, and rolls back on failure.
This is separate from production Docker deploys under `/srv/rakazo` (see `docs/self-host.md` and `.github/workflows/ci.yml` `deploy-production`).
## Prerequisites on Evo
1. Git checkout at `~/Projects/evOo-local` (or set `EVO_DEPLOY_DIR`).
2. `.env` with a working `DATABASE_URL` (Postgres reachable for `pnpm db:migrate`).
3. A restart path the script can use (pick one):
- **systemd (recommended for unattended deploy):** user unit, e.g. `evoo-local.service`, that starts `pnpm dev` or your stack wrapper. Set repo variable `EVO_SYSTEMD_UNIT`.
- **tmux:** session `evoo` running `pnpm dev` (matches `scripts/start-tmux.sh` on Mac; on Evo set `EVO_TMUX_SESSION` if different).
- **compose:** `infra/compose/docker-compose.yml` with `api` / `web` / `worker` (dev stack).
- **script:** executable hook at `EVO_RESTART_SCRIPT`.
4. Self-hosted GitHub Actions runner on Evo with labels `[self-hosted, evoo-ci, Linux]` (already used by CI).
No SSH keys or deploy secrets are stored in this repo. The workflow runs **on** the Evo runner against the local checkout.
## Enable the workflow
1. Merge this PR (or copy `ops/evo-deploy/` and `.github/workflows/evo-deploy.yml` to `main`).
2. In GitHub → **Settings → Secrets and variables → Actions → Variables** for `cynarAI/evOo-local`:
- `EVO_DEPLOY_ENABLED` = `true`
- Optional: `EVO_DEPLOY_DIR` (default `~/Projects/evOo-local` on the runner user)
- Optional: `EVO_RESTART_MODE` (`auto`, `systemd`, `tmux`, `compose`, `script`, `none`)
- Optional: `EVO_SYSTEMD_UNIT`, `EVO_TMUX_SESSION`, health URLs, timeouts
3. Confirm the runner user can read/write `EVO_DEPLOY_DIR` and restart services (`systemctl --user` or tmux).
Until `EVO_DEPLOY_ENABLED` is `true`, pushes to `main` do not deploy (workflow job is skipped).
## Manual / dry-run
On Evo (or any machine with the checkout and stack):
```bash
cd ~/Projects/evOo-local
# Show what would happen (no git reset, migrate, restart, or health wait)
bash ops/evo-deploy/deploy.sh --dry-run
# Deploy main tip
bash ops/evo-deploy/deploy.sh
# Pin a SHA
EVO_DEPLOY_REF=abc1234 bash ops/evo-deploy/deploy.sh
# Force systemd restart
EVO_RESTART_MODE=systemd EVO_SYSTEMD_UNIT=evoo-local.service bash ops/evo-deploy/deploy.sh
```
GitHub: **Actions → evo-deploy → Run workflow** (optional `ref`, `dry_run`, `skip_migrate`).
## Rollback behavior
### Regression: SHA capture vs. git stdout
`deploy.sh` captures `previousSha` and `targetSha` via process substitution (`read … < <(git_fetch_and_checkout)`). Git commands such as `reset --hard` print progress to **stdout** (`HEAD is now at …`), which previously polluted that capture and produced invalid `.evo-deploy/state.json` (e.g. `deployedSha: "is now at 84bc45e …"`, `previousSha: "HEAD"`). Rollback then became a no-op.
**Fix:** all git invocations inside `git_fetch_and_checkout` (and rollback) redirect stdout to stderr; only the final `printf '%s %s\n'` emits SHAs on stdout. `write_state` and rollback reject values that are not 7–40 hex characters.
1. Before `git fetch`, the script records `previousSha` (current `HEAD`).
2. After pull, migrate, and restart, it waits up to `EVO_HEALTH_TIMEOUT_SEC` (default 120s) for:
- `EVO_API_HEALTH_URL` (default `http://127.0.0.1:3100/health`)
- `EVO_WEB_HEALTH_URL` (default `http://127.0.0.1:5173/`)
3. On failure: `git reset --hard` to `previousSha`, restart services again, exit non-zero.
4. Last outcome is written to `.evo-deploy/state.json` under the checkout.
Manual rollback to a known good SHA:
```bash
cd ~/Projects/evOo-local
git reset --hard <good-sha>
EVO_RESTART_MODE=systemd EVO_SYSTEMD_UNIT=evoo-local.service bash ops/evo-deploy/deploy.sh --skip-migrate --ref HEAD
```
(or restart your unit/tmux session by hand, then verify health URLs).
## Autopilot / Grok Bot after merge
After a PR merges to `main`:
1. **Preferred:** rely on the `evo-deploy` workflow (once `EVO_DEPLOY_ENABLED=true`). No SSH step required.
2. **Fallback (manual):** SSH to Evo and run:
```bash
cd ~/Projects/evOo-local && bash ops/evo-deploy/deploy.sh
```
3. **Verify:** API `curl -fsS http://127.0.0.1:3100/health`, web `curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5173/`.
Do not commit host passwords, tokens, or SSH private keys. Configure restart targets via repo variables or env on the runner host only.
## Environment reference
| Variable | Default | Purpose |
|----------|---------|---------|
| `EVO_DEPLOY_DIR` | `$HOME/Projects/evOo-local` | Git checkout root |
| `EVO_DEPLOY_REF` | `main` | Branch name or commit SHA |
| `EVO_RESTART_MODE` | `auto` | `systemd` / `tmux` / `compose` / `script` / `none` |
| `EVO_SYSTEMD_UNIT` | _(empty)_ | Required for `systemd` mode |
| `EVO_SYSTEMD_SCOPE` | `user` | `user` or `system` |
| `EVO_TMUX_SESSION` | `evoo` | tmux session name |
| `EVO_TMUX_COMMAND` | `pnpm dev` | Command started in new tmux session |
| `EVO_MIGRATE` | `true` | Run `pnpm db:generate` + `pnpm db:migrate` |
| `EVO_INSTALL` | `true` | Run `pnpm install --frozen-lockfile` after checkout |
| `EVO_API_HEALTH_URL` | `http://127.0.0.1:3100/health` | API health probe |
| `EVO_WEB_HEALTH_URL` | `http://127.0.0.1:5173/` | Web health probe |
| `EVO_HEALTH_TIMEOUT_SEC` | `120` | Per-check timeout (seconds) |
If `EVO_RESTART_MODE=auto` cannot detect systemd, tmux, compose, or a restart script, deploy fails with a clear error — set `EVO_RESTART_MODE` and the matching unit/session explicitly.