From 1a08d2efda48d4a847900df2b06d1871e8be62c5 Mon Sep 17 00:00:00 2001 From: gov Date: Fri, 6 Nov 2026 15:18:01 -0500 Subject: [PATCH] build: release a static image with scripts/release.sh The image now packages a cross-compiled musl binary on distroless instead of compiling inside Docker. The script builds it, pushes : and :latest to git.cosocial.info, and tags the commit. Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 9 ++++----- Dockerfile | 21 ++++++--------------- README.md | 21 +++++++++++++++++++++ rust-toolchain.toml | 2 ++ scripts/release.sh | 31 +++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 20 deletions(-) create mode 100755 scripts/release.sh diff --git a/.dockerignore b/.dockerignore index 944919c..9b14591 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,4 @@ -target -.git -.env -*.sqlite -*.sqlite-* +# only the release binary and the city list go into the image +* +!target/x86_64-unknown-linux-musl/release/cities-labeler +!data diff --git a/Dockerfile b/Dockerfile index 0537ed3..f67fd3d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,12 @@ -# Debian release pinned on both stages so the binary's glibc matches at runtime. -# The image tag pins the compiler, so rust-toolchain.toml (and its editor -# components) is intentionally not copied in. -FROM rust:1.91-slim-bookworm AS build -WORKDIR /src -COPY Cargo.toml Cargo.lock ./ -COPY src ./src -RUN cargo build --release --bin cities-labeler - -FROM debian:bookworm-slim -RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates \ - && rm -rf /var/lib/apt/lists/* -COPY --from=build /src/target/release/cities-labeler /usr/local/bin/cities-labeler +# Release image: packages the static binary built by scripts/release.sh. +# No compiler runs here, so the image builds in seconds on any machine. +FROM gcr.io/distroless/static-debian12:nonroot +COPY target/x86_64-unknown-linux-musl/release/cities-labeler /usr/local/bin/cities-labeler COPY data /app/data ENV CITIES_PATH=/app/data/cities.json \ DB_PATH=/data/labeler.sqlite \ PORT=4100 +# matches the owner of the ansible-managed data directory USER 1000:1000 EXPOSE 4100 -CMD ["cities-labeler"] +ENTRYPOINT ["/usr/local/bin/cities-labeler"] diff --git a/README.md b/README.md index f8eed59..cdf112f 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,24 @@ cargo run --bin setup -- declare # label definitions; gives the account a Subs cargo run --bin setup -- did # run after deploying; requires labeler account's main password + emailed code ``` +## Release + +Releases are made manually with the Rust toolchain. `scripts/release.sh` cross-compiles a static +linux/amd64 binary, packages it into a container image, pushes +`git.cosocial.info/cosocial-canada/atproto-cosocial-labeler:` and `:latest`, and tags the +commit as `v`. It refuses to run with uncommitted changes or an existing tag. + +Prerequisites (once): + +```sh +brew install zig # linker and C compiler for cross-compiling +cargo install --locked cargo-zigbuild +docker login git.cosocial.info # token with the write:package scope +``` + +The pinned toolchain and its `x86_64-unknown-linux-musl` target come from `rust-toolchain.toml`. +Docker must be running. Then: + +```sh +scripts/release.sh 0.1.0 +``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f700ada..6ffda72 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,3 +2,5 @@ channel = "1.91.0" profile = "minimal" components = ["rustfmt", "clippy", "rust-analyzer", "rust-src"] +# release builds cross-compile a static binary (see scripts/release.sh) +targets = ["x86_64-unknown-linux-musl"] diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..4157132 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Cuts a release: cross-compiles a static linux/amd64 binary, pushes it as a +# container image (: and :latest), and tags the commit. +# +# scripts/release.sh 0.1.0 +# +# Needs: zig + cargo-zigbuild, Docker running, and +# `docker login git.cosocial.info` with a write:package token. +set -euo pipefail + +version="${1:?usage: scripts/release.sh }" +image="git.cosocial.info/cosocial-canada/atproto-cosocial-labeler" +target="x86_64-unknown-linux-musl" + +if [[ -n "$(git status --porcelain)" ]]; then + echo "working tree is dirty; commit first" >&2 + exit 1 +fi +if git rev-parse -q --verify "refs/tags/v$version" >/dev/null; then + echo "tag v$version already exists" >&2 + exit 1 +fi + +cargo zigbuild --release --target "$target" --bin cities-labeler +docker build --platform linux/amd64 -t "$image:$version" -t "$image:latest" . +docker push "$image:$version" +docker push "$image:latest" + +git tag -a "v$version" -m "v$version" +git push origin "v$version" +echo "released $image:$version"