orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1.PHONY: run build frontend pdfs og clean
2
3# Geist ships as .ttf inside the bun package, which is the only form Typst can
4# read; `make frontend` is what puts it there.
5GEIST = frontend/node_modules/geist/dist/fonts/geist-sans
6
7# Vite watches and rebuilds build/dist while Go serves it from disk. Templates are
8# embedded, so a template change needs a Go restart; CSS and JS changes only
9# need a browser reload.
10#
11# Go waits for the manifest rather than sleeping a guessed number of seconds.
12# The server treats a missing manifest as fatal, so the startup order matters,
13# and this site's first Vite build takes about six seconds because of the
14# Bootstrap SCSS: any fixed sleep short enough to be pleasant would lose the
15# race on a cold build/dist.
16run:
17 cd frontend && bun run dev & \
18 i=0; while [ ! -f build/dist/.vite/manifest.json ]; do \
19 i=$$((i+1)); \
20 if [ $$i -gt 120 ]; then echo "vite did not produce a manifest in 60s" >&2; exit 1; fi; \
21 sleep 0.5; \
22 done; \
23 go run .
24
25frontend:
26 cd frontend && bun install --frozen-lockfile && bun run build
27
28# Compile every post to a PDF. Part of `build` so it cannot be forgotten, and
29# its own target for when only a post changed.
30#
31# Needs the typst CLI on PATH. The Docker build fetches a pinned release; a
32# local checkout without it serves 404 on the PDF route.
33pdfs:
34 go run . -pdfs ./build/pdfs -typst-root . -typst-fonts $(GEIST)
35
36# The social cards, one PNG per post plus the site card. Same compiler, same
37# font and the same build-time reasoning as the PDFs.
38og:
39 go run . -og ./build/og -typst-root . -typst-fonts $(GEIST)
40
41# The release binary. -tags embed swaps assets_disk.go for assets_embed.go, so
42# the Vite bundle, the post PDFs and the social cards are compiled into the
43# executable; the posts are embedded either way. That makes
44# ../../bin/blog.bythewood.me the entire blog in one file.
45#
46# frontend, pdfs and og are requirements rather than ordering: the embed
47# directive reads build/ at compile time and fails if it is not there.
48build: frontend pdfs og
49 go build -tags embed -trimpath -ldflags="-s -w" -o ../../bin/blog.bythewood.me .
50
51clean:
52 rm -rf build frontend/node_modules