|
| 1 | +#!/bin/sh |
| 2 | +# |
| 3 | +# How the container starts. |
| 4 | +# |
| 5 | +# With no TSBB_CHECKOUT_DIR it runs the code baked into the image, which is |
| 6 | +# what an image is for: it is updated by building and deploying a new one. |
| 7 | +# |
| 8 | +# With TSBB_CHECKOUT_DIR pointing into a volume, the container instead keeps a |
| 9 | +# git checkout THERE and runs from it. On first boot it clones the repository |
| 10 | +# at the image's own commit and installs from the image's own package store, |
| 11 | +# so the first start needs no download and passes the healthcheck like any |
| 12 | +# other. From then on the board updates itself: the server fetches each new |
| 13 | +# release into the checkout, exits, and this script starts it again from the |
| 14 | +# new code — so a board on a platform that only redeploys images still follows |
| 15 | +# releases within minutes, and survives a container restart because the |
| 16 | +# checkout is on the volume, not in the container. |
| 17 | +set -eu |
| 18 | + |
| 19 | +IMAGE_ROOT="${TSBB_IMAGE_ROOT:-/app}" |
| 20 | +DIR="${TSBB_CHECKOUT_DIR:-}" |
| 21 | +REPO="${TSBB_UPDATE_REPO:-profullstack/tsbb}" |
| 22 | + |
| 23 | +if [ -z "$DIR" ]; then |
| 24 | + cd "$IMAGE_ROOT" |
| 25 | + exec node apps/server/src/index.ts |
| 26 | +fi |
| 27 | + |
| 28 | +if [ ! -d "$DIR/.git" ]; then |
| 29 | + echo "[tsbb] first boot: cloning $REPO into $DIR" |
| 30 | + rm -rf "$DIR" |
| 31 | + git clone --quiet "https://github.com/$REPO" "$DIR" |
| 32 | + # Start from exactly the commit this image was built from, so the checkout |
| 33 | + # and the image agree, so the install below finds everything in the store. |
| 34 | + if [ -n "${RAILWAY_GIT_COMMIT_SHA:-}" ]; then |
| 35 | + git -C "$DIR" -c advice.detachedHead=false checkout --quiet "$RAILWAY_GIT_COMMIT_SHA" \ |
| 36 | + || echo "[tsbb] commit ${RAILWAY_GIT_COMMIT_SHA} is not in the clone; staying on the default branch" |
| 37 | + fi |
| 38 | + # The image's install left every package in pnpm's store, so linking them |
| 39 | + # into the new checkout is seconds and needs no network. Copying the image's |
| 40 | + # node_modules instead does not work: pnpm sees a tree made for another path |
| 41 | + # and stops to ask whether it may rebuild it. CI=true is the "yes" it wants |
| 42 | + # if the lockfile has drifted and it must. |
| 43 | + echo "[tsbb] installing dependencies from the image's store" |
| 44 | + ( cd "$DIR" && CI=true pnpm install --frozen-lockfile --ignore-scripts --offline ) \ |
| 45 | + || ( cd "$DIR" && CI=true pnpm install --frozen-lockfile --ignore-scripts ) |
| 46 | +fi |
| 47 | + |
| 48 | +cd "$DIR" |
| 49 | +echo "[tsbb] running from $DIR at $(git rev-parse --short HEAD) ($(git describe --tags --always 2>/dev/null))" |
| 50 | + |
| 51 | +# The updater exits after installing a release; this loop is the supervisor |
| 52 | +# that brings the new code up. A crash waits a few seconds so a broken build |
| 53 | +# cannot spin the container. |
| 54 | +export TSBB_RESTART=exit |
| 55 | +while :; do |
| 56 | + if node apps/server/src/index.ts; then |
| 57 | + echo "[tsbb] server exited; starting again from $(git rev-parse --short HEAD)" |
| 58 | + else |
| 59 | + code=$? |
| 60 | + echo "[tsbb] server exited with status $code; starting again in 5s" |
| 61 | + sleep 5 |
| 62 | + fi |
| 63 | +done |
0 commit comments