Dev #8
@@ -1368,6 +1368,23 @@ Publishing is a separate step from building, and the separation is the point: th
|
|||||||
the site server reads is only touched once a build has succeeded, so a failed build
|
the site server reads is only touched once a build has succeeded, so a failed build
|
||||||
leaves the previous site up rather than replacing it with a half-written one.
|
leaves the previous site up rather than replacing it with a half-written one.
|
||||||
|
|
||||||
|
**Two gates before the build starts.** The first waits for `/api/health` to answer 200,
|
||||||
|
because `After=` orders a start and does not wait for a port. The second counts
|
||||||
|
`/api/mints` and refuses to build below `MIN_MINTS_FOR_BUILD`, default 20.
|
||||||
|
|
||||||
|
The second exists because health answering 200 and the index being complete are
|
||||||
|
different claims. A year of ~31-event backfills left a perfectly healthy API serving a
|
||||||
|
real, correct, complete list of eight mints; a build against that succeeds, prerenders
|
||||||
|
eight cards, and `rsync --delete-after` replaces fifty-five with eight. The gate runs as
|
||||||
|
an `ExecStartPre`, so a refusal aborts the unit before `pnpm build` — and publishing is
|
||||||
|
`ExecStartPost`, after the build — which means the previously published site is never
|
||||||
|
touched. The `OnFailure=` alert says why.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Raise or lower it for this host without editing the unit:
|
||||||
|
sudo systemctl edit cashumints-web # [Service] / Environment=MIN_MINTS_FOR_BUILD=40
|
||||||
|
```
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
# /etc/systemd/system/cashumints-web.service
|
# /etc/systemd/system/cashumints-web.service
|
||||||
[Unit]
|
[Unit]
|
||||||
@@ -1401,6 +1418,40 @@ Environment=PUBLIC_API_URL=
|
|||||||
# rather than letting the first fetch die on ECONNREFUSED. /api/health answers 503 until
|
# rather than letting the first fetch die on ECONNREFUSED. /api/health answers 503 until
|
||||||
# it is genuinely ready, and curl -f treats that as a failure, so the loop keeps waiting.
|
# it is genuinely ready, and curl -f treats that as a failure, so the loop keeps waiting.
|
||||||
ExecStartPre=/usr/bin/timeout 90 /bin/sh -c 'until curl -sf -o /dev/null http://127.0.0.1:8788/api/health; do sleep 1; done'
|
ExecStartPre=/usr/bin/timeout 90 /bin/sh -c 'until curl -sf -o /dev/null http://127.0.0.1:8788/api/health; do sleep 1; done'
|
||||||
|
|
||||||
|
# Then: does the API actually have an index to build a site out of?
|
||||||
|
#
|
||||||
|
# Health answering 200 says the process is up and its last backfill read something. It
|
||||||
|
# does not say how many mints are in the table, and those are different questions — the
|
||||||
|
# year of ~31-event backfills had a healthy API serving a real, complete, correct list of
|
||||||
|
# eight mints. A build against that succeeds, prerenders eight cards, and rsync happily
|
||||||
|
# replaces fifty-five with eight.
|
||||||
|
#
|
||||||
|
# So count the list before spending twenty minutes building from it. Below the floor
|
||||||
|
# this exits non-zero, systemd abandons the unit at ExecStartPre, and — because publishing
|
||||||
|
# is ExecStartPost, after the build — the previously published site is never touched. The
|
||||||
|
# site stays exactly as it was and the OnFailure alert says why.
|
||||||
|
#
|
||||||
|
# Counted by the `"host":` key, one per item, rather than by counting `{`: the list
|
||||||
|
# payload carries a nested object per mint (its NUT capability switches), so brace
|
||||||
|
# counting would report roughly double. No jq: it is not installed on this host and a
|
||||||
|
# build gate should not add a dependency to run.
|
||||||
|
#
|
||||||
|
# `Q` is a double-quote character, built with printf rather than written literally,
|
||||||
|
# because this whole command is already inside systemd's single quotes and a quote of
|
||||||
|
# either kind in the grep pattern would end the argument early.
|
||||||
|
#
|
||||||
|
# A curl that fails for any reason leaves `n` empty, `$${n:-0}` reads that as zero, and
|
||||||
|
# zero is below every floor — so an API that fell over between the health check above and
|
||||||
|
# this line refuses the build rather than sailing through it.
|
||||||
|
Environment=MIN_MINTS_FOR_BUILD=20
|
||||||
|
ExecStartPre=/bin/sh -c 'Q=$$(printf "\\042"); \
|
||||||
|
n=$$(curl -sf --max-time 30 http://127.0.0.1:8788/api/mints | grep -o "$${Q}host$${Q}:" | wc -l); \
|
||||||
|
if [ "$${n:-0}" -lt "$$MIN_MINTS_FOR_BUILD" ]; then \
|
||||||
|
printf "<3>%s\\n" "refusing to build: /api/mints returned $${n:-0} mints, floor is $$MIN_MINTS_FOR_BUILD. Previous site left untouched."; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
printf "%s\\n" "build gate: $$n mints, floor $$MIN_MINTS_FOR_BUILD"'
|
||||||
# Check `which pnpm` on the host: a corepack or pnpm-home install sits outside /usr/bin,
|
# Check `which pnpm` on the host: a corepack or pnpm-home install sits outside /usr/bin,
|
||||||
# and systemd's PATH does not include it.
|
# and systemd's PATH does not include it.
|
||||||
ExecStart=/usr/bin/pnpm build
|
ExecStart=/usr/bin/pnpm build
|
||||||
|
|||||||
@@ -52,6 +52,40 @@ Environment=PUBLIC_API_URL=
|
|||||||
# rather than letting the first fetch die on ECONNREFUSED. /api/health answers 503 until
|
# rather than letting the first fetch die on ECONNREFUSED. /api/health answers 503 until
|
||||||
# it is genuinely ready, and curl -f treats that as a failure, so the loop keeps waiting.
|
# it is genuinely ready, and curl -f treats that as a failure, so the loop keeps waiting.
|
||||||
ExecStartPre=/usr/bin/timeout 90 /bin/sh -c 'until curl -sf -o /dev/null http://127.0.0.1:8788/api/health; do sleep 1; done'
|
ExecStartPre=/usr/bin/timeout 90 /bin/sh -c 'until curl -sf -o /dev/null http://127.0.0.1:8788/api/health; do sleep 1; done'
|
||||||
|
|
||||||
|
# Then: does the API actually have an index to build a site out of?
|
||||||
|
#
|
||||||
|
# Health answering 200 says the process is up and its last backfill read something. It
|
||||||
|
# does not say how many mints are in the table, and those are different questions — the
|
||||||
|
# year of ~31-event backfills had a healthy API serving a real, complete, correct list of
|
||||||
|
# eight mints. A build against that succeeds, prerenders eight cards, and rsync happily
|
||||||
|
# replaces fifty-five with eight.
|
||||||
|
#
|
||||||
|
# So count the list before spending twenty minutes building from it. Below the floor
|
||||||
|
# this exits non-zero, systemd abandons the unit at ExecStartPre, and — because publishing
|
||||||
|
# is ExecStartPost, after the build — the previously published site is never touched. The
|
||||||
|
# site stays exactly as it was and the OnFailure alert says why.
|
||||||
|
#
|
||||||
|
# Counted by the `"host":` key, one per item, rather than by counting `{`: the list
|
||||||
|
# payload carries a nested object per mint (its NUT capability switches), so brace
|
||||||
|
# counting would report roughly double. No jq: it is not installed on this host and a
|
||||||
|
# build gate should not add a dependency to run.
|
||||||
|
#
|
||||||
|
# `Q` is a double-quote character, built with printf rather than written literally,
|
||||||
|
# because this whole command is already inside systemd's single quotes and a quote of
|
||||||
|
# either kind in the grep pattern would end the argument early.
|
||||||
|
#
|
||||||
|
# A curl that fails for any reason leaves `n` empty, `$${n:-0}` reads that as zero, and
|
||||||
|
# zero is below every floor — so an API that fell over between the health check above and
|
||||||
|
# this line refuses the build rather than sailing through it.
|
||||||
|
Environment=MIN_MINTS_FOR_BUILD=20
|
||||||
|
ExecStartPre=/bin/sh -c 'Q=$$(printf "\\042"); \
|
||||||
|
n=$$(curl -sf --max-time 30 http://127.0.0.1:8788/api/mints | grep -o "$${Q}host$${Q}:" | wc -l); \
|
||||||
|
if [ "$${n:-0}" -lt "$$MIN_MINTS_FOR_BUILD" ]; then \
|
||||||
|
printf "<3>%s\\n" "refusing to build: /api/mints returned $${n:-0} mints, floor is $$MIN_MINTS_FOR_BUILD. Previous site left untouched."; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
printf "%s\\n" "build gate: $$n mints, floor $$MIN_MINTS_FOR_BUILD"'
|
||||||
# Check `which pnpm` on the host: a corepack or pnpm-home install sits outside /usr/bin,
|
# Check `which pnpm` on the host: a corepack or pnpm-home install sits outside /usr/bin,
|
||||||
# and systemd's PATH does not include it.
|
# and systemd's PATH does not include it.
|
||||||
ExecStart=/usr/bin/pnpm build
|
ExecStart=/usr/bin/pnpm build
|
||||||
|
|||||||
Reference in New Issue
Block a user