Add Forgejo deploy workflow and update deploy docs
Some checks failed
Deploy / deploy (push) Failing after 13s

This commit is contained in:
mattie726 2026-08-25 23:19:09 -07:00 committed by Barely Removable
parent 8a2651ec87
commit 0757e95ad3
3 changed files with 79 additions and 27 deletions

View file

@ -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

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ grades.db
data/
__pycache__/
*.pyc
.DS_Store

View file

@ -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