# Wiring deploy.sh into Gitea Two practical patterns, both supported: ## Option A — Gitea webhook (push event → cURL on the server) Simplest. A push to `main` causes Gitea to POST to a small endpoint on the homelab server, which then runs `deploy.sh`. ### A.1. Add a webhook secret in Gitea - Repository → Settings → Webhooks → Add Webhook → Gitea - Target URL: `https://deploy.example.com/hooks/gitea` (your reverse-proxied endpoint that runs the script) - HTTP method: `POST` - POST content type: `application/json` - Secret: a long random string - Trigger on: "Push events" - Branch filter: `main` (or `gitea-sync`) - Save and note the secret. ### A.2. Drop a tiny receiver on the homelab server A 5-line systemd-timer-friendly shell receiver is enough. For example: ```bash #!/usr/bin/env bash # /usr/local/bin/gitea-deploy-webhook set -euo pipefail LOG=/var/log/gitea-deploy.log echo "[$(date -Iseconds)] trigger" >> "$LOG" sudo -u deploy /srv/app/deploy/deploy.sh >> "$LOG" 2>&1 ``` Expose it via a separate `server { ... }` block in nginx that listens on something obscure and `allow`/`deny`s only Gitea's source IPs. Or put it behind a Cloudflare Tunnel / Tailscale Funnel. The exact exposure model is up to you. ### A.3. Hardening the secret If you want the receiver to verify Gitea's HMAC, add this check (works with `GITEA_WEBHOOK_SECRET` matching what you set in the UI): ```bash SECRET='paste-your-secret-here' sig=$(printf '%s' "$HTTP_RAW_BODY" | openssl dgst -sha256 -hmac "$SECRET" -binary | xxd -p -c 256) expected=$(printf 'sha256=%s' "$sig") [[ "$HTTP_X_GITEA_SIGNATURE" == "$expected" ]] || { echo "bad sig"; exit 1; } ``` (Your receiver framework — `webhook`, `socat`, `caddy` plugin — handles header propagation differently; adapt accordingly.) --- ## Option B — Gitea Actions runner (self-hosted) The Gitea-native CI path. Most flexible: only deploy when the runner is on the homelab. ### B.1. Register a self-hosted runner On the homelab, follow https://docs.gitea.com/usage/actions/act_runner and register a runner labeled `self-hosted,homelab`. ### B.2. Workflow file (already in the repo) The workflow lives at [.gitea/workflows/deploy.yml](../.gitea/workflows/deploy.yml) in the repo root. It triggers on push to `main` / `gitea-sync` and calls `./deploy/deploy.sh`. Highlights: - **`on.push.paths`** filter — only deploys when source, deploy config, or the workflow itself changes. Drop the block to deploy on every commit. - **`workflow_dispatch`** — manual trigger from the Gitea UI with optional inputs for `api_url`, `project_name`, `skip_prune`. Useful for blue/green deploys (`project_name: prod-app-green`). - **`concurrency.group`** — at most one deploy runs at a time (the script also has its own `flock`). - **`runs-on: [self-hosted, homelab]`** — must match the labels you gave the runner. - **Preflight step** — verifies `docker`, `docker compose`, `flock`, `ss`, `curl` exist and the required files are present, BEFORE the script takes its lock. - **Post-deploy `healthcheck.sh --nginx`** — independent smoke test in addition to the script's own healthcheck. - **Failure annotation** — tail of `deploy.log` printed on failure so the Actions log shows what went wrong without a separate SSH session. ### B.3. Optional: path filter tweaks To deploy on docs-only commits, drop the `paths:` block under `on.push`. To deploy on PR merges only, replace the trigger with: ```yaml on: push: branches: [main] ``` ### B.4. Required secrets - `GITEA_TOKEN` (a personal access token with `write:repository` scope) — only needed for the "annotate the commit" step (which posts the new ports as a commit comment). The rest of the workflow works without any secrets. The deploy itself reads `.env.production` from the workspace, so no app secrets need to be plumbed through Actions (the runner has filesystem access to `/srv/app/`). --- ## Comparison | | Webhook (A) | Actions (B) | |---|---|---| | Setup cost | Trivial | Moderate (runner install) | | Logging | `deploy.log` only | Gitea Actions UI + `deploy.log` | | Conditional triggers | Receiver must parse the body | YAML in the workflow | | Multi-repo | One webhook per repo | One workflow per repo | | Source of truth | Webhook delivery | Runner job history | For a single homelab repo, **Option B is recommended** — you get the Actions UI for free and the runner already lives on the box, so there's no extra network surface area to secure.