fuzzyworld

tower / files / tools / build-pages.sh

build-pages.sh

Blame: the commit that last changed each line · file · history

342ab851 2026-09-28 Pavel Safronov1#!/bin/sh
2# Build the GitLab Pages site: the default branch at the root, and every other branch
3# at /<branch>/, so any branch can be looked at in a browser without a dev server.
4#
5# GitLab's own way to do this (parallel deployments) is Premium-only, and this project
6# is on the Free plan. So the default branch's pipeline builds every branch into one
7# site, and a push to any other branch triggers a default-branch pipeline to rebuild it
8# (.gitlab-ci.yml). Assets are relative (vite.config.ts), so a build works in a folder.
9#
10# Every folder also gets a branches.json listing what was built, with links relative to
11# that folder, which is what the app's branch picker reads (src/ui/app.ts).
12#
13# sh tools/build-pages.sh [out] run from the repository root; out defaults to public
14set -eu
15
16DEFAULT="${CI_DEFAULT_BRANCH:-main}"
17OUT="${1:-public}"
18WORK="$(mktemp -d)"
19trap 'rm -rf "$WORK"' EXIT
20
21# The default branch, as checked out.
22npm ci
23npm run build
24rm -rf "$OUT"
25mv dist "$OUT"
26# What was built, as branch:folder pairs; the default branch's folder is the root.
27# Neither a git branch name nor a folder name can hold a colon or a space.
28built="$DEFAULT:"
29
30# Every other branch GitLab has, each into its own folder. CI clones shallowly and
31# fetches only the branch it is building, so fetch the rest first.
32git fetch --quiet --prune origin '+refs/heads/*:refs/remotes/origin/*'
33for branch in $(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin); do
34 case "$branch" in "$DEFAULT"|HEAD) continue ;; esac
35 # A folder name a URL can carry: fix/worker-loading becomes fix-worker-loading.
36 name="$(printf '%s' "$branch" | tr 'A-Z' 'a-z' | tr -c 'a-z0-9.\n-' '-')"
37 if [ -e "$OUT/$name" ]; then
38 echo "skipping $branch: /$name/ is already part of the site"
39 continue
40 fi
41 echo "building $branch into /$name/"
42 git worktree add --quiet --detach "$WORK/$name" "origin/$branch"
43 # A branch that does not build is left out rather than failing the whole site.
44 if (cd "$WORK/$name" && npm ci && npm run build); then
45 mv "$WORK/$name/dist" "$OUT/$name"
46 built="$built $branch:$name"
47 else
48 echo "WARNING: $branch did not build; it has no preview this time"
49 fi
50 git worktree remove --force "$WORK/$name"
51done
52
53# branches.json in every folder: each branch, a link to it from here, and which is this.
54write_branches() {
55 here="$1"
56 up=""
57 [ -n "$here" ] && up="../"
58 sep=""
59 {
60 printf '['
61 for pair in $built; do
62 b="${pair%%:*}"
63 f="${pair#*:}"
64 href="$up${f:+$f/}"
65 [ -n "$href" ] || href="./"
66 cur=false
67 [ "$f" = "$here" ] && cur=true
68 printf '%s\n {"branch": "%s", "href": "%s", "current": %s}' "$sep" "$b" "$href" "$cur"
69 sep=","
70 done
71 printf '\n]\n'
72 } > "$OUT/${here:+$here/}branches.json"
73}
74for pair in $built; do
75 write_branches "${pair#*:}"
76done