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:
64
CLAUDE.md
64
CLAUDE.md
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user