fix(preview): one sync at a time, follow pushes, one router per preview #16

Merged
genji1024 merged 3 commits from fix/preview-sync-lock into main 2026-10-14 11:17:50 +09:00
Collaborator

Closes #15

Changes

  • docs/preview/sync.sh: takes flock -n on data/.lock; a second run while one is going prints another sync is running and exits 0. Cleans up by removing data/.incoming whole and recreating it. Starts the unpack container with this run's downloads mounted read-only at /in and the archive's name in ZIP, instead of bind-mounting the archive (Docker creates a missing bind-mount source as a directory, which is what stopped every later run).
  • docs/preview/unpack.sh: unpacks /in/$ZIP; refuses a name that is empty, has a slash or anything but a-z0-9.-.
  • One router per preview (koto-preview-N): sync.sh writes them to data/.routers.yaml and passes it to compose next to compose.yaml, which keeps only the service. Traefik requests one certificate per router for all of its hostnames, so with one rule listing every preview each certificate named all previews (pr-14's carried pr-16 too) and every pull request opening or closing re-issued all of them against Let's Encrypt's 50/week for genji1024.com.
  • docs/preview/fetch.sh: previews now follow pushes. The runs query had event=pull_request, but Forgejo filters on its internal event name, pull_request_sync for a run after a push, even though the run reports "pull_request"; so every preview kept its pull request's first build (#14 was still serving b96db95, run 191, after 2aae0b4 passed as run 193). The event is now checked on the returned runs.
  • compose.yaml comments and README (running by hand next to the timer is safe; the certificate paragraph now matches).

Verification

With the same docker compose stand-in as #10 (busybox wget/unzip, jq), against forgejo.genji1024.com with #14 open:

  • Starting from the broken state (data/.incoming/pr-14.zip a directory): the run recovers, downloads #14's build (run 191, artifact 8), serves it and builds the rule.

  • Two runs at once: the second prints another sync is running and exits 0; the first completes normally.

  • The name check accepts pr-14.zip and rejects pr-1/../x.zip, an empty name and one with a space; sh -n passes.

  • Routers: generated data/.routers.yaml for previews 14 and 16 and rendered docker compose -f compose.yaml -f data/.routers.yaml config: the web service carries koto-preview-14 and koto-preview-16, each with its own Host(), websecure, certresolver: le, and the shared koto-preview service.

  • End to end, sync.sh in a scratch copy with Docker, against forgejo.genji1024.com: downloaded and served #14 at 2aae0b4 (run 193) and #16 at 455f1bf (run 194), wrote both routers, and started nginx with them. A second run downloaded nothing and left the container as it was.

After merging, the next run re-creates the web container with the two routers, and Traefik requests one certificate for each open preview.

On the host, rm -rf ~/koto/docs/preview/data/.incoming clears the current broken state without waiting for this change.

Closes #15 ## Changes - **`docs/preview/sync.sh`**: takes `flock -n` on `data/.lock`; a second run while one is going prints `another sync is running` and exits 0. Cleans up by removing `data/.incoming` whole and recreating it. Starts the unpack container with this run's downloads mounted read-only at `/in` and the archive's name in `ZIP`, instead of bind-mounting the archive (Docker creates a missing bind-mount source as a directory, which is what stopped every later run). - **`docs/preview/unpack.sh`**: unpacks `/in/$ZIP`; refuses a name that is empty, has a slash or anything but `a-z0-9.-`. - **One router per preview** (`koto-preview-N`): `sync.sh` writes them to `data/.routers.yaml` and passes it to compose next to `compose.yaml`, which keeps only the service. Traefik requests one certificate per router for all of its hostnames, so with one rule listing every preview each certificate named all previews (pr-14's carried pr-16 too) and every pull request opening or closing re-issued all of them against Let's Encrypt's 50/week for genji1024.com. - **`docs/preview/fetch.sh`**: previews now follow pushes. The runs query had `event=pull_request`, but Forgejo filters on its internal event name, `pull_request_sync` for a run after a push, even though the run reports `"pull_request"`; so every preview kept its pull request's first build (#14 was still serving b96db95, run 191, after 2aae0b4 passed as run 193). The event is now checked on the returned runs. - `compose.yaml` comments and README (running by hand next to the timer is safe; the certificate paragraph now matches). ## Verification With the same `docker compose` stand-in as #10 (busybox `wget`/`unzip`, jq), against forgejo.genji1024.com with #14 open: - Starting from the broken state (`data/.incoming/pr-14.zip` a directory): the run recovers, downloads #14's build (run 191, artifact 8), serves it and builds the rule. - Two runs at once: the second prints `another sync is running` and exits 0; the first completes normally. - The name check accepts `pr-14.zip` and rejects `pr-1/../x.zip`, an empty name and one with a space; `sh -n` passes. - Routers: generated `data/.routers.yaml` for previews 14 and 16 and rendered `docker compose -f compose.yaml -f data/.routers.yaml config`: the web service carries `koto-preview-14` and `koto-preview-16`, each with its own `Host()`, `websecure`, `certresolver: le`, and the shared `koto-preview` service. - End to end, `sync.sh` in a scratch copy with Docker, against forgejo.genji1024.com: downloaded and served #14 at 2aae0b4 (run 193) and #16 at 455f1bf (run 194), wrote both routers, and started nginx with them. A second run downloaded nothing and left the container as it was. After merging, the next run re-creates the web container with the two routers, and Traefik requests one certificate for each open preview. On the host, `rm -rf ~/koto/docs/preview/data/.incoming` clears the current broken state without waiting for this change.
fix(preview): one sync at a time, and no file bind mounts
All checks were successful
ci / check (pull_request) Successful in 1m4s
f1b98e1085
A run started by hand while the timer's was going deleted the archive the
other was about to unpack; Docker then created the missing bind-mount
source as a directory, and every later run stopped on it (#15).

- Lock with flock: a second run says so and exits.
- The unpack container gets this run's downloads read-only at /in and
  the archive's name in ZIP, instead of the file itself.
- Clean up by removing data/.incoming whole, so whatever is left there
  is gone on the next run.

Closes #15
bot-genji1024 changed title from fix(preview): one sync at a time, and no file bind mounts to fix(preview): one sync at a time, no file bind mounts, one router per preview 2026-10-14 11:09:13 +09:00
fix(preview): one router, so one certificate, per preview
All checks were successful
ci / check (pull_request) Successful in 1m2s
455f1bf5ff
Traefik requests one certificate per router, for all of its hostnames.
With every preview in one rule, each certificate named all the previews,
and a pull request opening or closing got a new certificate for all of
them, counting against Let's Encrypt's weekly limit for the domain.
sync.sh now writes one router per preview to data/.routers.yaml and
passes it to compose next to compose.yaml.
bot-genji1024 changed title from fix(preview): one sync at a time, no file bind mounts, one router per preview to fix(preview): one sync at a time, follow pushes, one router per preview 2026-10-14 11:12:10 +09:00
fix(preview): pick up builds of pushes to an open pull request
All checks were successful
ci / check (pull_request) Successful in 50s
63d5c3f96d
fetch.sh asked for runs with event=pull_request. Forgejo filters on its
internal event name, which is pull_request_sync for a run after a push
to the pull request, though the run reports "pull_request" either way;
so a preview kept the build from when its pull request was opened.
Check the event on the returned runs instead.
genji1024 approved these changes 2026-10-14 11:17:30 +09:00
genji1024 deleted branch fix/preview-sync-lock 2026-10-14 11:17:51 +09:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
genji1024/koto!16
No description provided.