docs: update CLAUDE.md with Gitea CI setup and infra details

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-31 11:24:48 +02:00
parent 8d030b64a1
commit 2837f2af38

View File

@@ -64,7 +64,7 @@ Categories use Obsidian-style path notation (`lang/de`, `tech/programming`). The
Funkwhale stores album art on Wasabi S3 with presigned URLs that expire after ~1 hour. Serving those URLs directly causes broken images on the listening page after the first hour.
`lib/cache-funkwhale-image.js` downloads cover art at build time and serves it from a stable local path:
- **Cache dir:** `.cache/funkwhale-images/` (gitignored, persisted between CI runs via `actions/cache`)
- **Cache dir:** `.cache/funkwhale-images/` (gitignored, persisted between CI runs — the self-hosted runner workspace is reused across runs, so the cache survives without any explicit upload/restore step)
- **Public path:** `/images/funkwhale-cache/<md5-of-url-path>.<ext>`
- **Cache key:** MD5 of the URL *path* (stable across re-signings — query params are stripped)
- **No TTL:** files are kept forever; `gcFunkwhaleImages()` deletes any file not referenced by the current build's data
@@ -96,12 +96,56 @@ All values come from env vars. The `SITE_SOCIAL` env var uses pipe-and-comma enc
## Deploy workflow
1. Push to `main` on `github.com:svemagie/blog`
2. GitHub Action runs `npm install && npm run build`
3. Rsync pushes `_site/` to Indiekit server
4. `content/.indiekit/` is excluded from `--delete` to preserve IndieKit state
CI runs on a self-hosted Gitea instance (`gitea` jail, `10.100.0.90:3000`), org `giersig.eu`, repo `indiekit-blog`. The runner is `act_runner` in host mode (label `freebsd:host`), running as the `git` user on FreeBSD.
The build runs with all env vars injected from GitHub Secrets / Cloudron app settings.
**Trigger:** push to `main`, or `workflow_dispatch`. Workflow file: `.github/workflows/deploy.yml`.
**Steps:**
1. `actions/checkout@v4`
2. `npm ci`
3. Build or restore sharp for FreeBSD (see below)
4. Fetch `homepage.json` from node jail via SSH → `content/.indiekit/homepage.json`
5. `npm run build:css`
6. Write `.env` from secrets using `printf` (no interpolation issues)
7. `npm run build` with env vars injected
8. Rsync `_site/` to `deploy` user on host → `/usr/local/bastille/jails/web/root/usr/local/www/blog/`
9. Trigger syndication webhook at `http://10.100.0.20:3000/syndicate` (internal URL — public URL fails due to hairpin NAT)
### Sharp on FreeBSD
There is no prebuilt `sharp` binary for FreeBSD. The CI step builds from source on first run, then caches the `.node` binary:
- **Cache location:** `/usr/local/git/.cache/sharp-freebsd/sharp-freebsd-x64-{VERSION}.node`
- **Build:** `npm install node-addon-api node-gyp && npm install sharp --build-from-source` (requires `libvips` — installed via `pkg install vips` in the gitea jail)
- **Restore:** copies cached binary into `node_modules/sharp/build/Release/` — skips ~3 min compile
### Rsync deploy user
Rsync deploys as `deploy` user (uid=1002, in `www` group). The `smg` user (uid=1001) cannot write to the blog dir directly — it is owned `www:www`. The runner's SSH key must be in `/home/deploy/.ssh/authorized_keys`.
### BSD shell gotchas in the workflow
The runner shell is POSIX sh (not bash/tcsh). Watch for GNU-only constructs:
- `head -n -1` (skip last line) **does not work on BSD** — use `sed '$d'` instead
- `tail -1` works fine (positive count only needed)
### Repo names (Gitea)
- `giersig.eu/indiekit-blog` — this repo (Eleventy + content). Local dir: `indiekit-blog`
- `giersig.eu/indiekit-server` — IndieKit server (Micropub, AP, etc.). Local dir: `indiekit-server`
### Pushing workflow changes
The server runs tcsh which mangles long `echo`/`printf` commands. Use a Python heredoc from the server to push via Gitea API:
```python
python3 << 'PYEOF'
import urllib.request, json, base64
# Read file, get SHA, PUT new content to Gitea contents API
PYEOF
```
Always generate base64 from the local file with `base64 -i file | tr -d '\n'` — never copy b64 strings from session history (they can contain corruption).
---
@@ -142,6 +186,13 @@ GITHUB_USERNAME svemagie
BLUESKY_HANDLE svemagie
```
The following are set directly in `.github/workflows/deploy.yml` (not secrets) because they are internal network addresses only reachable from the CI runner:
```
INDIEKIT_URL http://10.100.0.20:3000 # node jail — IndieKit API, Funkwhale proxy, etc.
FUNKWHALE_INSTANCE http://10.100.0.40:5000 # Funkwhale jail
```
---
## What's diverged from upstream (summary)
@@ -159,3 +210,4 @@ BLUESKY_HANDLE svemagie
- **Soft-delete filtering** — posts with `deleted: true` excluded from all collections
- **Content-warning support** — collapsible content on post pages, hidden content on listings
- **Upstream drift check script** — `scripts/check-upstream-widget-drift.mjs`
- **Self-hosted Gitea CI** — replaced GitHub Actions; `act_runner` on FreeBSD, sharp built from source with persistent binary cache, rsync via `deploy` user, syndication webhook via internal jail URL