Render the app privacy policies from the app repos

Each policy now has exactly one copy: docs/PRIVACY.md in the app's own
repository. The pages keep their URLs and chrome and render that file
through a content collection, so the published page and the app's own
documentation cannot drift.

scripts/sync-external.mjs shallow-clones both repos into external/ from
prebuild and predev — not from CI: Coolify builds the site from the repo,
so a checkout that only ran in a Gitea job would never reach the deploy.
It falls back to the raw file if git is unavailable, and takes <APP>_REF
or <APP>_LOCAL for work against a branch or an unpushed working copy.

A missing, empty or malformed policy fails the build, verified against the
real image: the deploy stops rather than publishing an empty privacy page.
This commit is contained in:
2026-09-09 16:33:11 +02:00
parent 6311baccb7
commit 8a1eb54a45
10 changed files with 286 additions and 524 deletions
+43
View File
@@ -15,6 +15,49 @@ npm run build # output -> dist/
npm run preview # serve the built site locally
```
## App privacy policies (rendered from the app repos)
`/calendula/privacy` and `/agendula/privacy` hold **no copy** of their prose.
Each policy lives in its own app repository, as `docs/PRIVACY.md` with
`title` / `description` / `updated` frontmatter, and is the single copy of that
policy anywhere:
| Page | Source |
|------|--------|
| `/calendula/privacy` | [`jlmakiola/calendula`](https://codeberg.org/jlmakiola/calendula) → `docs/PRIVACY.md` |
| `/agendula/privacy` | [`jlmakiola/agendula`](https://codeberg.org/jlmakiola/agendula) → `docs/PRIVACY.md` |
`scripts/sync-external.mjs` clones them (shallow, single-branch, no
credentials) into `external/` before every build and dev server — it is wired
to `prebuild` and `predev`, **not** to CI, because Coolify builds the site from
the repo and a checkout that only ran in a Gitea job would never reach the
deployed page. `src/content.config.ts` exposes each file as a content
collection; the pages render it and supply the `<h1>` from `title`.
**To change a policy, edit it in the app repo, in a PR.** It reaches the live
page on the site's next build.
```sh
npm run sync:external # refresh the checkouts by hand
CALENDULA_REF=some/branch npm run build # build against a branch
AGENDULA_LOCAL=../agendula npm run dev # render a working copy, no network
```
The build **fails** if either policy is missing, empty, or malformed. That is
deliberate: a privacy page silently rendering nothing is the one failure this
arrangement exists to prevent, and it is worth a red deploy.
### Getting a policy edit onto the site
A build is what publishes it, so:
- **Floor:** the daily `scheduled-deploy` cron rebuilds every morning, so any
edit is live within a day without anyone doing anything.
- **Immediate:** add a webhook in the app repo (Codeberg → Settings → Webhooks)
pointing at the same Coolify deploy URL the cron uses, so a merge to the app's
`main` triggers a site rebuild at once. One-time setup per app repo; the
secret lives in Coolify, not here.
## Writing a post
Create a Markdown file in `src/content/blog/`, e.g. `my-post.md`: