diff options
| -rw-r--r-- | Dockerfile | 34 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | deploy/Dockerfile.cross | 12 | ||||
| -rw-r--r-- | deploy/cross-build.sh | 13 | ||||
| -rwxr-xr-x | deploy/docker-entrypoint.sh | 2 | ||||
| -rw-r--r-- | docs/DEPLOY.md | 29 | ||||
| -rwxr-xr-x | scripts/deploy.sh | 52 |
7 files changed, 49 insertions, 100 deletions
@@ -1,35 +1,25 @@ # syntax=docker/dockerfile:1 -FROM debian:bookworm-slim AS build +FROM alpine:3.24 AS build # VERSION is declared after the expensive layers: the legacy builder # invalidates every layer that follows a changed build argument. -RUN apt-get update \ - && apt-get install -y --no-install-recommends build-essential libncurses-dev \ - libssl-dev \ - && rm -rf /var/lib/apt/lists/* +RUN apk add --no-cache build-base openssl-dev WORKDIR /src COPY . . ARG VERSION=0.1.0-dev -# With .prebuilt/ the binaries are cross-compiled on the development machine -# (see deploy/Dockerfile.cross); otherwise build and test from source. -RUN if [ -x .prebuilt/bokfd ]; then \ - mkdir -p build && \ - cp .prebuilt/bokfd .prebuilt/bokfctl .prebuilt/bokftui build/; \ - else \ - make -j"$(nproc)" VERSION="$VERSION" && make test VERSION="$VERSION"; \ - fi +RUN make -j"$(nproc)" backend VERSION="$VERSION" \ + && make test-core VERSION="$VERSION" \ + && strip build/bokfd build/bokfctl -FROM debian:bookworm-slim AS runtime -RUN apt-get update \ - && apt-get install -y --no-install-recommends libncursesw6 libssl3 \ - ca-certificates util-linux \ - && rm -rf /var/lib/apt/lists/* \ - && useradd --system --uid 10001 --home-dir /var/lib/bokfd \ - --shell /usr/sbin/nologin bokfd \ +# The runtime image carries the daemon and bokfctl only. The ncurses TUI is a +# frontend: build it from source on the machine you sit at. +FROM alpine:3.24 AS runtime +RUN apk add --no-cache libssl3 ca-certificates util-linux \ + && addgroup -S bokfd \ + && adduser -S -D -H -u 10001 -G bokfd -s /sbin/nologin bokfd \ && install -d -o bokfd -g bokfd /var/lib/bokfd /var/lib/bokfd/backup \ /var/lib/bokfd/export /var/lib/bokfd/certs /run/bokfd -COPY --from=build /src/build/bokfd /src/build/bokfctl /src/build/bokftui \ - /usr/local/bin/ +COPY --from=build /src/build/bokfd /src/build/bokfctl /usr/local/bin/ COPY --from=build /src/data/bas_k2.csv /src/data/bas_k3.csv \ /usr/local/share/bokf/ COPY deploy/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh @@ -25,6 +25,8 @@ CORE_OBJ = $(patsubst %.c,$(BUILD)/%.o,$(CORE_SRC)) all: $(BUILD)/bokfd $(BUILD)/bokfctl $(BUILD)/bokftui +backend: $(BUILD)/bokfd $(BUILD)/bokfctl + SSL_LIBS = -lssl -lcrypto $(BUILD)/bokfd: $(BUILD)/src/bokfd.o $(CORE_OBJ) $(VENDOR_OBJ) @@ -54,6 +56,9 @@ test: $(BUILD)/test_core $(BUILD)/test_tui $(BUILD)/test_core $(BUILD)/test_tui +test-core: $(BUILD)/test_core + $(BUILD)/test_core + $(BUILD)/vendor/%.o: vendor/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) $(DEPFLAGS) $(CPPFLAGS) $(DEFS) -w -c $< -o $@ @@ -84,4 +89,4 @@ install: all clean: rm -rf $(BUILD) -.PHONY: all test install clean +.PHONY: all backend test test-core install clean diff --git a/deploy/Dockerfile.cross b/deploy/Dockerfile.cross deleted file mode 100644 index 9a568cf..0000000 --- a/deploy/Dockerfile.cross +++ /dev/null @@ -1,12 +0,0 @@ -# Cross-compile the arm64 binaries on an amd64 host. The runtime image is -# assembled later on the target host from the produced binaries. -FROM debian:bookworm -RUN dpkg --add-architecture arm64 \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - make ca-certificates gcc-aarch64-linux-gnu \ - libncurses-dev:arm64 libssl-dev:arm64 \ - && rm -rf /var/lib/apt/lists/* -COPY cross-build.sh /usr/local/bin/cross-build -RUN chmod 0755 /usr/local/bin/cross-build -ENTRYPOINT ["/usr/local/bin/cross-build"] diff --git a/deploy/cross-build.sh b/deploy/cross-build.sh deleted file mode 100644 index 7c90b0f..0000000 --- a/deploy/cross-build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# Cross-compile bokfd/bokfctl/bokftui for aarch64. Expects the source -# read-only at /src, writes the binaries to /out and takes the version from -# $VERSION. Run through deploy/Dockerfile.cross. -set -eu - -make -C /src -j"$(nproc)" BUILD=/tmp/build \ - CC=aarch64-linux-gnu-gcc \ - CFLAGS="-O2 -g -L/usr/lib/aarch64-linux-gnu" \ - VERSION="${VERSION:-0.1.0-dev}" - -mkdir -p /out -cp /tmp/build/bokfd /tmp/build/bokfctl /tmp/build/bokftui /out/ diff --git a/deploy/docker-entrypoint.sh b/deploy/docker-entrypoint.sh index 3cab4bd..d63f9dd 100755 --- a/deploy/docker-entrypoint.sh +++ b/deploy/docker-entrypoint.sh @@ -12,7 +12,7 @@ mkdir -p "$BOKFD_BACKUP_DIR" "$BOKFD_EXPORT_DIR" "$(dirname "$BOKFD_SOCKET")" \ chown -R bokfd:bokfd /var/lib/bokfd "$(dirname "$BOKFD_SOCKET")" case "${1:-}" in - bokfd | bokfctl | bokftui | /*) + bokfd | bokfctl | /*) exec setpriv --reuid=bokfd --regid=bokfd --clear-groups "$@" ;; *) diff --git a/docs/DEPLOY.md b/docs/DEPLOY.md index 7694add..ed105f8 100644 --- a/docs/DEPLOY.md +++ b/docs/DEPLOY.md @@ -56,8 +56,6 @@ Normal deploys build an image and recreate the container. While developing, `scripts/deploy.sh --dev` skips the image entirely: - the gate (`make` + `make test`) still runs locally, -- on an architecture mismatch the binaries are cross-compiled here - (`deploy/Dockerfile.cross`, ~20 s on a PC), - the binaries are copied into the running container with `docker cp`, - the daemon is reloaded with `SIGHUP`, which re-execs the binary in place (in-memory sessions are lost, clients reconnect), @@ -65,7 +63,9 @@ Normal deploys build an image and recreate the container. While developing, No image is built and the container is not recreated; a later normal deploy replaces the copied binaries. Use a descriptive tag, e.g. -`scripts/deploy.sh --dev v0.2.0-rc1`. +`scripts/deploy.sh --dev v0.2.0-rc1`. Hot reload requires the host to share +the dev machine's architecture; on a mismatch `--dev` falls back to a full +remote build. ## Deploying upgrades @@ -87,11 +87,12 @@ The script: 5. on failure, puts the previous `BOKF_TAG` back and rolls back to the image that is still loaded on the host. -Cross-architecture builds are automatic: `uname -m` is compared over SSH and -a mismatch switches to a remote build. Override with `BOKF_BUILD=local` or -`BOKF_BUILD=remote` (also settable in `.env`). A remote build pulls the -Debian base image inside a container, so the host needs outbound network -access but still no toolchain. +Architecture mismatches are automatic: `uname -m` is compared over SSH and a +mismatch switches to a remote build of the same Alpine image (compiled inside +the host's Docker, so the host still needs no toolchain). Override with +`BOKF_BUILD=local` or `BOKF_BUILD=remote` (also settable in `.env`). The +runtime image is Alpine and carries `bokfd` + `bokfctl` only; the ncurses TUI +is a frontend and never shipped. Tags are `git describe` output unless passed. Tag releases (`v*`) so rollback and support have meaningful versions. The rollback image must still exist on @@ -121,17 +122,19 @@ to `main`. The normal `scripts/deploy.sh` flow does not need any of this. ## Clients -Run clients inside the container — no host toolchain needed: +The image ships only the daemon and `bokfctl`, which covers the healthcheck +and quick admin calls: ```sh -docker compose exec bokfd bokftui # interactive TUI docker compose exec -e BOKFD_PASSWORD='<pw>' bokfd \ bokfctl --user admin fiscal_year.list ``` -The clients honor `BOKFD_SOCKET`; a host-installed client can also point at -`var/run/bokfd.sock`, but that file is owned by uid 10001, so the host user -must be in that group (or use `sudo`). +The TUI (`bokftui`) is a frontend: build it from source on the machine you sit +at (`make bokftui`, needs libncursesw) and point it at the host, normally the +TLS listener. A host-installed client can also point at `var/run/bokfd.sock`, +but that file is owned by uid 10001, so the host user must be in that group +(or use `sudo`). The TUI remembers the server and user (never the password) in `$XDG_CONFIG_HOME/bokf/tui.conf` (`~/.config/bokf/tui.conf`), written after diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 79d731f..ad5350c 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -3,10 +3,10 @@ # # 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. +# Normal mode builds an image and recreates the container, building natively on +# the host when the target architecture differs. --dev (same architecture only) +# 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. @@ -61,21 +61,6 @@ wait_healthy() { 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 @@ -88,7 +73,7 @@ case "$BOKF_BUILD" in if [ "$(uname -m)" = "$host_arch" ]; then BOKF_BUILD=local else - echo "deploy: host is $host_arch, cross-building here" + echo "deploy: host is $host_arch, building the image on the host" BOKF_BUILD=remote fi ;; @@ -101,32 +86,20 @@ case "$BOKF_BUILD" in ;; esac -if [ "$DEV" = 1 ]; then +if [ "$DEV" = 1 ] && [ "$BOKF_BUILD" = local ]; 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" + echo "deploy: dev: copying the locally built binaries into the running container" remote "mkdir -p /tmp/bokf-dev" - scp -q "$prebin/bokfd" "$prebin/bokfctl" "$prebin/bokftui" \ - "$BOKF_HOST:/tmp/bokf-dev/" + scp -q build/bokfd build/bokfctl "$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" | @@ -143,20 +116,23 @@ if [ "$DEV" = 1 ]; then exit 1 fi +if [ "$DEV" = 1 ]; then + echo "deploy: dev: host is $host_arch, hot reload needs a matching" \ + "architecture; doing a full deploy" >&2 +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 - cross_build - echo "deploy: assembling image bokf:$TAG on $BOKF_HOST" + echo "deploy: assembling image bokf:$TAG natively on $BOKF_HOST" tar -cf - \ --exclude=./.git --exclude=./build --exclude=./var --exclude=./.env \ --exclude='./*.se' --exclude='./*.db' --exclude='./*.db-wal' \ --exclude='./*.db-shm' --exclude=./docs . | "${SSH[@]}" "docker build --build-arg 'VERSION=$TAG' -t 'bokf:$TAG' -" - rm -rf .prebuilt fi remote "mkdir -p '$BOKF_REMOTE_DIR'" |
