From 0757e95ad3edb9346f2e1c2def43f84a96f75395 Mon Sep 17 00:00:00 2001 From: mattie726 Date: Tue, 25 Aug 2026 23:19:09 -0700 Subject: [PATCH] Add Forgejo deploy workflow and update deploy docs --- .forgejo/workflows/deploy.yml | 28 +++++++++++++ .gitignore | 1 + CLAUDE.md | 77 +++++++++++++++++++++++------------ 3 files changed, 79 insertions(+), 27 deletions(-) create mode 100644 .forgejo/workflows/deploy.yml diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..2509475 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,28 @@ +name: Deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # The job container only sees its own checkout — the live app lives + # at /mnt/user/appdata/card-grader on the host, bind-mounted into + # this container at the same path (see runner container.options) so + # `docker compose`'s relative volume paths resolve against the real + # host directory rather than an ephemeral one. `data/` holds the + # production DB and photos and must survive every deploy. + - name: Sync source to deploy path + run: | + rsync -a --delete \ + --exclude='.git' \ + --exclude='data' \ + ./ /mnt/user/appdata/card-grader/ + + - name: Rebuild and restart + working-directory: /mnt/user/appdata/card-grader + run: docker compose up -d --build diff --git a/.gitignore b/.gitignore index f3d31ca..2805576 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ grades.db data/ __pycache__/ *.pyc +.DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index 32fe622..ef933c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,37 +76,60 @@ copies of the repo (local Mac, server) are kept in sync by committing on both sides. There is currently no automated push-to-deploy path connected — see below. -**Pending: restricted git-push deploy.** A setup script -(`setup-card-grader-deploy.sh`, delivered to the user, not yet run) creates -a scoped `carddeploy` user on the Unraid box for exactly this purpose: +**Superseded approach, fully torn down: git-shell restricted deploy.** A +`carddeploy` service account (login shell `git-shell`, one bare repo, a +`post-receive` hook triggering a single root-owned deploy script via one +narrowly-scoped `sudo` rule) was built, tested, and verified working +end-to-end — then explicitly torn down at the user's request in favor of +Forgejo (below). Nothing from it remains: the account, sudoers rule, bare +repo, `/boot/config/go` persistence block, and the `sshd_config` +`AllowUsers` addition were all removed and verified reverted. The local git +remote and SSH keypair for it were deleted too. If this pattern (restricted +git-shell account + sudo-scoped deploy script) is useful as a reference for +scoping *other* access requests later, it's a solid model — it was +abandoned for tooling reasons (moving to a real git host), not because it +didn't work. -- Login shell is `git-shell` — accepts git push/pull only, nothing else (no - interactive shell, no arbitrary commands, no SFTP). -- A `post-receive` hook (root-owned, mode 755 — not writable by the - `carddeploy` account, and not something `git push` can overwrite via the - protocol regardless) triggers a single root-owned deploy script via one - narrowly-scoped `sudo` rule (`NOPASSWD` for that *exact script path* - only — deliberately never raw `docker`/the `docker` group, both of which - are root-equivalent on the whole box via the daemon socket). -- The deploy script does `git checkout -f main` into the live app directory - and `docker compose up -d --build`. -- State (the bare repo, the deploy script, the sudoers source file) lives - under `/mnt/user/appdata/card-grader-deploy/` on the array — Unraid boots - from USB into RAM, so anything living only under `/` would vanish on - reboot. Setup is reinstalled idempotently via `/boot/config/go` (Unraid's - official boot-time persistence hook) so it survives reboots. +**In progress: Forgejo.** Decision made 2026-08-25 to self-host Forgejo +(soft-fork of Gitea) as the git remote and CI/CD trigger, replacing the +git-shell approach entirely. Plan, run by the user themselves (not by +Claude — user explicitly wants to execute server-side setup steps +personally): -Once the user runs that script and confirms, the local SSH config alias -`unraid-cardgrader` (in `~/.ssh/config`, already pointed at the new -`carddeploy` user and a fresh dedicated keypair -`~/.ssh/id_ed25519_cardgrader_deploy`) will be usable, and the remaining -step is adding the bare repo as a git remote here and doing a first push to -verify the pipeline end-to-end. **Do not assume this pipeline is live** — -confirm with the user before relying on it. +1. Forgejo itself runs as its own Docker container on the same Unraid box + (`/mnt/user/appdata/forgejo`), proxied at `git.hippofam.com` via Nginx + Proxy Manager. +2. This repo gets a `forgejo` remote and is pushed there (full history, + not a fresh copy). +3. Forgejo Actions (instance + repo level) is enabled, and a self-hosted + runner is registered **at the repo level** (not instance-wide) as its + own container (`/mnt/user/appdata/forgejo-runner`), with the host + Docker socket mounted in so it can rebuild the `card-grader` container. +4. A `.forgejo/workflows/deploy.yml` workflow (checkout + `docker compose + up -d --build` on push to `main`) does the actual deploy. + +**Known tradeoff, not yet resolved:** unlike the git-shell mailbox (which +could only ever trigger one hardcoded script), a CI runner executes +whatever a workflow YAML says — mounting the Docker socket gives it control +over *any* container on the box, not just this one. The access boundary +becomes "who can push a workflow file to this repo" rather than a sudoers +rule. If tighter isolation is wanted later, a `docker-socket-proxy` in +front of the runner (scoped to just the `card-grader` compose project) was +proposed but not yet built — revisit if this matters more once the basic +pipeline is working. + +**Status:** walkthrough delivered to the user 2026-08-25; not yet executed. +**Do not assume any of this is live** — confirm current state before +relying on it or making claims about deploy mechanism to the user. A prior root SSH key/access to the whole box was explicitly revoked by the -user; the replacement above is intentionally scoped to *only* updating this -one container, not general Unraid/Docker access. +user before any of the above; every access grant since then (including a +one-time temporary root key used only to run the git-shell setup script, +itself removed immediately after) has been narrowly scoped and time-boxed +on request. Treat that as the standing expectation for any future +server-access ask on this project — default to the smallest scope that +does the job, time-box it, and confirm teardown/persistence explicitly +rather than leaving it open-ended. ## Conventions and constraints established for this project