summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-18 10:35:11 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-18 10:35:11 +0200
commit1e5b4927e5ae1d542d51158eb995d4f4b5a8b1c5 (patch)
tree5d2b414abbe6550eaf403c6aa4b5f75eb703fa01 /scripts
parent7a0771215d9bd52dc9d6913abee56a9f62893d3e (diff)
downloadbokf-1e5b4927e5ae1d542d51158eb995d4f4b5a8b1c5.tar.gz
bokf-1e5b4927e5ae1d542d51158eb995d4f4b5a8b1c5.zip
deploy: --dev hot-reloads the daemon via docker cp + SIGHUPv0.1.14
- bokfd re-execs its own binary on SIGHUP, closing listeners/db first - scripts/deploy.sh --dev cross-builds and copies binaries into the running container, then verifies the reported version
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/deploy.sh116
1 files changed, 88 insertions, 28 deletions
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
index c345a21..79d731f 100755
--- a/scripts/deploy.sh
+++ b/scripts/deploy.sh
@@ -1,7 +1,12 @@
#!/usr/bin/env bash
# Deploy a locally built bokf image to BOKF_HOST over SSH. See docs/DEPLOY.md.
#
-# scripts/deploy.sh [tag]
+# scripts/deploy.sh [--dev] [tag]
+#
+# Normal mode builds an image (cross-compiled on this machine when the target
+# architecture differs) and recreates the container. --dev skips the image:
+# it copies the fresh binaries into the running container and reloads the
+# daemon with SIGHUP, which re-execs them in place.
#
# BOKF_HOST (user@host) and BOKF_REMOTE_DIR are read from the environment or
# from .env in the repository root. The host needs only Docker and Compose.
@@ -23,6 +28,12 @@ fi
BOKF_REMOTE_DIR="${BOKF_REMOTE_DIR:-/srv/bokf}"
SSH=(ssh "$BOKF_HOST")
+DEV=0
+if [ "${1:-}" = "--dev" ]; then
+ DEV=1
+ shift
+fi
+
TAG="${1:-}"
if [ -z "$TAG" ]; then
TAG="$(git describe --tags --always --dirty 2>/dev/null || echo dev)"
@@ -36,11 +47,41 @@ esac
remote() { "${SSH[@]}" "$1"; }
+wait_healthy() {
+ for _ in $(seq 1 30); do
+ cid="$(remote "cd '$BOKF_REMOTE_DIR' && docker compose ps -q bokfd" 2>/dev/null || true)"
+ if [ -n "$cid" ]; then
+ st="$(remote "docker inspect --format '{{.State.Health.Status}}' '$cid'" 2>/dev/null || true)"
+ if [ "$st" = "healthy" ]; then
+ return 0
+ fi
+ fi
+ sleep 2
+ done
+ return 1
+}
+
+cross_build() {
+ echo "deploy: cross-compiling $host_arch binaries on this machine"
+ rm -rf .prebuilt
+ mkdir -p .prebuilt
+ docker build -q -f deploy/Dockerfile.cross -t bokf-cross deploy/ >/dev/null
+ docker run --rm -e "VERSION=$TAG" -v "$PWD":/src:ro \
+ -v "$PWD/.prebuilt":/out bokf-cross
+ if command -v file >/dev/null 2>&1 &&
+ ! file .prebuilt/bokfd | grep -q aarch64; then
+ echo "deploy: cross-build produced no arm64 binary" >&2
+ rm -rf .prebuilt
+ exit 1
+ fi
+}
+
echo "deploy: gate: build + tests"
make -j"$(nproc)"
make test
BOKF_BUILD="${BOKF_BUILD:-auto}"
+host_arch="$(uname -m)"
case "$BOKF_BUILD" in
auto)
host_arch="$(remote 'uname -m' 2>/dev/null || echo unknown)"
@@ -51,31 +92,64 @@ case "$BOKF_BUILD" in
BOKF_BUILD=remote
fi
;;
- local | remote) ;;
+ local | remote)
+ host_arch="$(remote 'uname -m' 2>/dev/null || echo unknown)"
+ ;;
*)
echo "deploy: BOKF_BUILD must be auto, local or remote" >&2
exit 1
;;
esac
+if [ "$DEV" = 1 ]; then
+ if ! remote "test -f '$BOKF_REMOTE_DIR/var/db/bokfd.db'"; then
+ echo "deploy: no database on $BOKF_HOST; run a normal deploy first" >&2
+ exit 1
+ fi
+ if [ "$BOKF_BUILD" = local ]; then
+ prebin="build"
+ echo "deploy: dev: using the locally built binaries"
+ else
+ cross_build
+ prebin=".prebuilt"
+ fi
+ echo "deploy: dev: copying binaries into the running container"
+ remote "mkdir -p /tmp/bokf-dev"
+ scp -q "$prebin/bokfd" "$prebin/bokfctl" "$prebin/bokftui" \
+ "$BOKF_HOST:/tmp/bokf-dev/"
+ remote "cd '$BOKF_REMOTE_DIR'
+ cid=\$(docker compose ps -q bokfd)
+ test -n \"\$cid\" || { echo 'deploy: bokfd is not running' >&2; exit 1; }
+ docker cp /tmp/bokf-dev/bokfd \"\$cid:/usr/local/bin/bokfd\"
+ docker cp /tmp/bokf-dev/bokfctl \"\$cid:/usr/local/bin/bokfctl\"
+ docker cp /tmp/bokf-dev/bokftui \"\$cid:/usr/local/bin/bokftui\"
+ docker kill --signal=HUP \"\$cid\""
+ if [ "$prebin" = ".prebuilt" ]; then
+ rm -rf .prebuilt
+ fi
+ ver=""
+ for _ in $(seq 1 15); do
+ ver="$(remote "cd '$BOKF_REMOTE_DIR' && docker exec \$(docker compose ps -q bokfd) bokfctl meta 2>/dev/null" |
+ sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' || true)"
+ [ "$ver" = "$TAG" ] && break
+ sleep 1
+ done
+ if [ "$ver" = "$TAG" ]; then
+ echo "deploy: dev reloaded, daemon reports $ver"
+ exit 0
+ fi
+ echo "deploy: dev reload did not come back with $TAG (got '${ver:-nothing}')" >&2
+ remote "cd '$BOKF_REMOTE_DIR' && docker compose logs --tail=30 bokfd" >&2 || true
+ exit 1
+fi
+
if [ "$BOKF_BUILD" = local ]; then
echo "deploy: building image bokf:$TAG locally"
docker build --build-arg "VERSION=$TAG" -t "bokf:$TAG" .
echo "deploy: shipping image to $BOKF_HOST"
docker save "bokf:$TAG" | gzip | "${SSH[@]}" 'gunzip | docker load'
else
- echo "deploy: cross-compiling $host_arch binaries on this machine"
- rm -rf .prebuilt
- mkdir -p .prebuilt
- docker build -q -f deploy/Dockerfile.cross -t bokf-cross deploy/ >/dev/null
- docker run --rm -e "VERSION=$TAG" -v "$PWD":/src:ro \
- -v "$PWD/.prebuilt":/out bokf-cross
- if command -v file >/dev/null 2>&1 &&
- ! file .prebuilt/bokfd | grep -q aarch64; then
- echo "deploy: cross-build produced no arm64 binary" >&2
- rm -rf .prebuilt
- exit 1
- fi
+ cross_build
echo "deploy: assembling image bokf:$TAG on $BOKF_HOST"
tar -cf - \
--exclude=./.git --exclude=./build --exclude=./var --exclude=./.env \
@@ -106,20 +180,6 @@ fi
echo "deploy: starting containers"
remote "cd '$BOKF_REMOTE_DIR' && docker compose up -d --no-build"
-wait_healthy() {
- for _ in $(seq 1 30); do
- cid="$(remote "cd '$BOKF_REMOTE_DIR' && docker compose ps -q bokfd" 2>/dev/null || true)"
- if [ -n "$cid" ]; then
- st="$(remote "docker inspect --format '{{.State.Health.Status}}' '$cid'" 2>/dev/null || true)"
- if [ "$st" = "healthy" ]; then
- return 0
- fi
- fi
- sleep 2
- done
- return 1
-}
-
if wait_healthy; then
echo "deploy: bokf:$TAG is healthy on $BOKF_HOST"
exit 0