feat(cd): add continuous deployment on push to master
Some checks failed
CI / ruff (push) Successful in 8s
CI / pytest (push) Successful in 36m19s
CI / architecture (push) Successful in 11s
CI / dependency-scanning (push) Successful in 27s
CI / audit (push) Successful in 9s
CI / docs (push) Failing after 59s
CI / deploy (push) Failing after 3s
Some checks failed
CI / ruff (push) Successful in 8s
CI / pytest (push) Successful in 36m19s
CI / architecture (push) Successful in 11s
CI / dependency-scanning (push) Successful in 27s
CI / audit (push) Successful in 9s
CI / docs (push) Failing after 59s
CI / deploy (push) Failing after 3s
Deploy job SSHes to production after ruff/pytest/architecture pass, running scripts/deploy.sh (stash, pull, docker rebuild, migrate, health check). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ Complete step-by-step guide for deploying Wizamart on a Hetzner Cloud VPS.
|
||||
- **Setup date**: 2026-02-11
|
||||
|
||||
!!! success "Progress — 2026-02-12"
|
||||
**Completed (Steps 1–15):**
|
||||
**Completed (Steps 1–16):**
|
||||
|
||||
- Non-root user `samir` with SSH key
|
||||
- Server hardened (UFW firewall, SSH root login disabled, fail2ban)
|
||||
@@ -45,10 +45,10 @@ Complete step-by-step guide for deploying Wizamart on a Hetzner Cloud VPS.
|
||||
- AAAA (IPv6) records added for all wizard.lu domains
|
||||
- mkdocs build clean (zero warnings) — all 32 orphan pages added to nav
|
||||
- Pre-commit documented in `docs/development/code-quality.md`
|
||||
- **Step 16: Continuous deployment** — auto-deploy on push to master via `scripts/deploy.sh` + Gitea Actions
|
||||
|
||||
**Next steps:**
|
||||
|
||||
- [ ] Step 16: Continuous deployment — auto-deploy on push to master
|
||||
- [ ] Step 17: Backups — verify Hetzner backup scope, add PostgreSQL pg_dump
|
||||
- [ ] Step 18: Monitoring & observability — Prometheus, Grafana, uptime checks, alerting
|
||||
|
||||
@@ -670,7 +670,83 @@ sudo systemctl status gitea-runner
|
||||
|
||||
Verify the runner shows as **Online** in Gitea: **Site Administration > Actions > Runners**.
|
||||
|
||||
## Step 16: Verify Full Deployment
|
||||
## Step 16: Continuous Deployment
|
||||
|
||||
Automate deployment on every successful push to master. The Gitea Actions runner and the app both run on the same server, so the deploy job SSHes from the CI Docker container to `127.0.0.1`.
|
||||
|
||||
```
|
||||
push to master
|
||||
├── ruff ──────┐
|
||||
├── pytest ────┤
|
||||
└── architecture ─┤
|
||||
└── deploy (SSH → scripts/deploy.sh)
|
||||
├── git stash / pull / pop
|
||||
├── docker compose up -d --build
|
||||
├── alembic upgrade heads
|
||||
└── health check (retries)
|
||||
```
|
||||
|
||||
### 16.1 Generate Deploy SSH Key (on server)
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "gitea-deploy@wizard.lu" -f ~/.ssh/deploy_ed25519 -N ""
|
||||
cat ~/.ssh/deploy_ed25519.pub >> ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
### 16.2 Add Gitea Secrets
|
||||
|
||||
In **Repository Settings > Actions > Secrets**, add:
|
||||
|
||||
| Secret | Value |
|
||||
|---|---|
|
||||
| `DEPLOY_SSH_KEY` | Contents of `~/.ssh/deploy_ed25519` (private key) |
|
||||
| `DEPLOY_HOST` | `127.0.0.1` |
|
||||
| `DEPLOY_USER` | `samir` |
|
||||
| `DEPLOY_PATH` | `/home/samir/apps/orion` |
|
||||
|
||||
### 16.3 Deploy Script
|
||||
|
||||
The deploy script lives at `scripts/deploy.sh` in the repository. It:
|
||||
|
||||
1. Stashes local changes (preserves `.env`)
|
||||
2. Pulls latest code (`--ff-only`)
|
||||
3. Pops stash to restore local changes
|
||||
4. Rebuilds and restarts Docker containers (`docker compose --profile full up -d --build`)
|
||||
5. Runs database migrations (`alembic upgrade heads`)
|
||||
6. Health checks `http://localhost:8001/health` with 12 retries (60s total)
|
||||
|
||||
Exit codes: `0` success, `1` git pull failed, `2` docker compose failed, `3` migration failed, `4` health check failed.
|
||||
|
||||
### 16.4 CI Workflow
|
||||
|
||||
The deploy job in `.gitea/workflows/ci.yml` runs only on master push, after `ruff`, `pytest`, and `architecture` pass:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
needs: [ruff, pytest, architecture]
|
||||
steps:
|
||||
- name: Deploy to production
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
port: 22
|
||||
command_timeout: 10m
|
||||
script: cd ${{ secrets.DEPLOY_PATH }} && bash scripts/deploy.sh
|
||||
```
|
||||
|
||||
### 16.5 Manual Fallback
|
||||
|
||||
If CI is down, deploy manually:
|
||||
|
||||
```bash
|
||||
cd ~/apps/orion && bash scripts/deploy.sh
|
||||
```
|
||||
|
||||
### 16.6 Verify
|
||||
|
||||
```bash
|
||||
# All app containers running
|
||||
@@ -771,29 +847,15 @@ git stash pop
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Deploy updates (pull & rebuild)
|
||||
### Deploy updates
|
||||
|
||||
After pushing code to Gitea from local:
|
||||
Deployments happen automatically when pushing to master (see [Step 16](#step-16-continuous-deployment)). For manual deploys:
|
||||
|
||||
```bash
|
||||
cd ~/apps/orion && git pull && docker compose --profile full up -d --build
|
||||
cd ~/apps/orion && bash scripts/deploy.sh
|
||||
```
|
||||
|
||||
If there are local changes on the server (e.g. `.env` edits), stash first:
|
||||
|
||||
```bash
|
||||
cd ~/apps/orion
|
||||
git stash # Save local config changes
|
||||
git pull
|
||||
git stash pop # Re-apply local config
|
||||
docker compose --profile full up -d --build
|
||||
```
|
||||
|
||||
If the update includes database migrations:
|
||||
|
||||
```bash
|
||||
docker compose --profile full exec -e PYTHONPATH=/app api python -m alembic upgrade heads
|
||||
```
|
||||
The script handles stashing local changes, pulling, rebuilding containers, running migrations, and health checks.
|
||||
|
||||
### View logs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user