gitops-automation

Claude Code automation wired into Gitea + Portainer + Matrix on this VPS. See ~/.claude/plans/cozy-honking-lantern.md on the host for the full design rationale.

Three things this gives you:

  • PR review: opening/updating a PR in a watched Gitea repo gets a Claude-authored review comment.
  • GitOps redeploy: pushing to main on this repo rebuilds the claude-agent image (Gitea Actions) and redeploys the stack (Portainer webhook).
  • Chat-driven coding agent: !claude owner/repo <instruction> in the Matrix control room clones the repo, runs Claude Code, and opens a PR with the result.

Nothing here auto-merges. Every path stops at a comment or an open PR — a human clicks merge.

Prerequisites (one-time, on the VPS)

sudo usermod -aG docker william   # then start a new shell/session
docker network create web

Bring-up order

  1. DNS — confirm these resolve to 217.160.66.143: gitea.apps.williamturner.eu, portainer.apps.williamturner.eu, matrix.apps.williamturner.eu, agent.apps.williamturner.eu.

  2. Front Gitea with Traefik: edit ~/gitea/docker-compose.yml — add it to the web network and Traefik labels (mirror the matrix-homeserver block in this repo's docker-compose.yml, using port 3000 as the service port and rule Host(`gitea.apps.williamturner.eu`)). Update ~/gitea/data/gitea/conf/app.ini: DOMAIN and ROOT_URLhttps://gitea.apps.williamturner.eu/. Recreate the container, confirm the HTTPS URL works, then remove 3000:3000 from the port mapping.

  3. Front Portainer with Traefik: same pattern on ~/portainer-compose.yaml, service port 9443 (Portainer serves TLS itself on that port — either terminate TLS at Traefik with traefik.http.services.portainer.loadbalancer.server.scheme=https and serversTransport with insecure skip-verify, or simplest: also expose Portainer's plain HTTP port internally and point Traefik at that instead). Confirm https://portainer.apps.williamturner.eu works before removing 9443:9443.

  4. Create the gitops-automation repo in Gitea (via http://217.160.66.143:3000 if step 2 isn't done yet, otherwise the HTTPS URL), push this directory to it.

  5. Gitea API token: user Settings → Applications → generate a token with repo + webhook scopes. Put it in .env as GITEA_TOKEN.

  6. Gitea Actions runner: admin Settings → Actions → Runners → create registration token → .env as ACT_RUNNER_REGISTRATION_TOKEN. Also set repo-level Actions variables GITEA_HOST (e.g. gitea.apps.williamturner.eu) and secret GITEA_TOKEN (Settings → Actions → Variables/Secrets on the repo) — the workflow in .gitea/workflows/build.yml reads those.

  7. First image build (registry is empty until Actions runs once):

    cp .env.example .env   # fill in values as you go
    docker build -t "$(grep GITEA_REGISTRY_IMAGE .env | cut -d= -f2)" ./agent
    docker login <gitea-host> -u <your-username>
    docker push "$(grep GITEA_REGISTRY_IMAGE .env | cut -d= -f2)"
    
  8. Bring up the stack, ideally as a Portainer "Repository" stack pointed at this repo (so it's also the GitOps redeploy target) — or directly:

    docker compose up -d
    
  9. First boot: Matrix bot account — with MATRIX_ALLOW_REGISTRATION=true in .env, redeploy matrix-homeserver, then register the bot:

    curl -s https://matrix.apps.williamturner.eu/_matrix/client/v3/register \
      -H 'Content-Type: application/json' \
      -d '{"username":"claude-bot","password":"<pick one>","auth":{"type":"m.login.dummy"}}'
    

    This returns an access_token — put it in .env as MATRIX_BOT_TOKEN. Then set MATRIX_ALLOW_REGISTRATION=false and redeploy matrix-homeserver again.

  10. Control room: from any Matrix client logged in as yourself on this homeserver, create a room, invite @claude-bot:matrix.apps.williamturner.eu, copy the room ID into .env as MATRIX_CONTROL_ROOM_ID. Redeploy claude-agent.

  11. Portainer stack webhook: in the stack's settings, enable the webhook, copy the URL into .env/repo secrets as PORTAINER_STACK_WEBHOOK_URL.

  12. Gitea webhooks on each repo you want automation for:

    • push → mainPORTAINER_STACK_WEBHOOK_URL (only needed on this repo, for GitOps redeploy of the automation stack itself)
    • pull request (opened, synchronized) → https://agent.apps.williamturner.eu/webhooks/gitea, secret = GITEA_WEBHOOK_SECRET, on every repo you want auto-reviewed.
  13. Firewall: sudo ufw allow 80/tcp 443/tcp; once the HTTPS routes above are all confirmed working, sudo ufw delete allow 3000/tcp and sudo ufw delete allow 9443/tcp.

Smoke test

  • Open a throwaway PR on a repo with the PR webhook set → expect a Claude review comment.
  • In the Matrix control room: !claude owner/repo add a comment to the README → expect a "working on it" reply, then a PR link.
  • git push to main on this repo → expect a Gitea Actions run, then a Portainer redeploy.

Notes

  • agent/src/runner.js is the only thing that ever runs git commit/git push/git checkout — Claude Code itself is explicitly denied those tools (--disallowedTools), so even a misbehaving prompt can't push directly or touch main.
  • The Matrix bot only reacts inside MATRIX_CONTROL_ROOM_ID; keep that room invite-only.
  • .gitea/workflows/build.yml assumes the act_runner label docker — check GITEA_RUNNER_LABELS in docker-compose.yml matches what you actually registered.
S
Description
No description provided
Readme
161 KiB
Languages
JavaScript 86.2%
Dockerfile 13.8%