mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
## Summary
Bump the pinned Rust toolchain from 1.95.0 to 1.98.1 so release builds
work on macOS 27 hosts.
Rust ≤ 1.97 strips Apple-target artifacts with its bundled
`llvm-objcopy`, which leaves the Mach-O `LC_SYMTAB` string pool
(`__LINKEDIT`) only 4-byte aligned. macOS 27's dyld refuses to `dlopen`
such images (`mis-aligned LINKEDIT string pool`). Because cargo applies
the release profile's default `strip = "debuginfo"` to proc-macro dylibs
too, `cargo build --release` of any crate that uses sqlx macros
(`buzz-db`, `buzz-relay`, `buzz-admin`, …) fails on macOS 27: rustc
cannot load `libsqlx_macros-*.dylib`. Dev builds pass because the dev
profile does not strip, which is why this only shows up as a
release/packaging failure.
The fix (llvm/llvm-project#203680, pulled into rustc by
rust-lang/rust#158410 for rust-lang/rust#157750) first shipped in Rust
1.98.0 and was never backported to 1.97.x. `block/buzz-app` pinned
1.98.1 for the same failure in block/buzz-app#529. With this bump the
`CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_STRIP=none` / custom-linker
workarounds people have been using locally are no longer needed.
### What changes
Two commits: the toolchain bump (with the desktop clippy fixes), then
the workspace clippy fixes.
| Location | Change |
|---|---|
| `rust-toolchain.toml` | `channel = "1.95.0"` → `"1.98.1"` (profile
unchanged) |
| `Dockerfile`, `Dockerfile.push-gateway` | `ARG RUST_VERSION=1.95` →
`1.98` |
| `Dockerfile.sprig` | `rust:1.98-alpine3.22` with the manifest-list
digest verified on Docker Hub (shared by the `1.98` and `1.98.1` tags) |
| `.github/workflows/release.yml` | `dtolnay/rust-toolchain` pinned to
the tip of its `1.98.1` branch (`18f04874`) for the Windows desktop
release job |
| `benchmarks/harbor-buzz-orchestra` | `RUST_IMAGE = rust:1.98-alpine` |
| `_ci-rust.yml`, `windows-canary.yml`, `app_state_tests.rs` | stale
"1.95" comments reworded; the `unknown_lints` allow stays with a
version-agnostic rationale |
| `desktop/src-tauri` | lints clippy 1.98 newly raises under `-D
warnings`: `chunks_exact_to_as_chunks` at five fixed-width PCM/UTF-16
decoders, `needless_borrows_for_generic_args` on the `Copy` `norm`
closure in `config_bridge/reader.rs`, and a redundant `Engine as _`
import in `hpke_key_backup_tests.rs` that `use super::*` already
provides |
| Workspace crates (9) | the 30 lints clippy 1.98 newly raises in `cargo
clippy --workspace --all-targets -- -D warnings`, listed below |
Workspace lint fixes:
| Lint | Sites | Fix |
|---|---|---|
| `result_large_err` (20) | `buzz-relay` `api/git/transport.rs` (8),
`api/git/settings.rs` (6), `api/gifs.rs`, `api/workflows.rs`,
`api/profiles.rs`; `buzz-push-gateway` `verify_installation_assertion` |
Clippy 1.98 now checks `async fn`s, so every `async fn … -> Result<_,
axum::response::Response>` trips it. These follow the existing allow
pattern in `api/media.rs` and `nip_fi_http.rs` ("Response is the natural
error type for axum handlers"): module-level in the two git files,
per-function elsewhere. Boxing `Response` would touch every handler for
no runtime benefit. |
| `chunks_exact_to_as_chunks` (3) | `buzz-media` MP4 brand scan;
`buzz-voice` npy f32 reader and stereo test fixture | `chunks_exact(N)`
→ `as_chunks::<N>().0` |
| `question_mark` (3) | `buzz-acp` queue flush fallback and
slash-command mention skip; `buzz-agent` `find_git_root` | `match … {
None => return None }` → `?` |
| `useless_format` (2) | `buzz-auth` nip_fi startup test policies |
`format!("…")` → `"…".to_string()` |
| `useless_borrows_in_formatting` (1) | `buzz-pairing-cli` secret print
| drop the borrow |
| `manual_noop_waker` (1) | `buzz-sdk` broker test `block_on` |
`Waker::noop()` (stable since 1.85) |
All `as_chunks` rewrites keep `chunks_exact`'s remainder-dropping
semantics, and `as_chunks` is stable since 1.88, which is the MSRV.
One change in `api/git/transport.rs` is not a lint fix. Staging that
file trips the pre-commit secret scanner on its existing test-only ES256
fixture (`GIT_TEST_EC_PEM`), and the scanner only honours a suppression
marker on the flagged line. So the `\`-continued string literal is
rewritten as a byte-identical `concat!` whose `BEGIN` line carries
`sadscan:disable kingfisher.privkey.2`.
Left as is on purpose: `Cargo.toml` `rust-version = "1.88.0"` (MSRV
floor, a separate concern), the README/CONTRIBUTING "1.88+" text, Hermit
(no Rust packages there), the lockfiles, and the `(1.95.0)` comment in
`scripts/start-isolated-test-relay.sh` (the pre-commit secret scanner
flags that file's pre-existing dev-only `DATABASE_URL` whenever it is
staged).
### Related issue
Fixes #8074
### Testing
All under rustc/clippy 1.98.1 on a macOS 27 host (Darwin 27.0.0,
aarch64), with no `RUSTFLAGS` / `SDKROOT` / strip overrides in the
environment:
- `cargo build --release -p buzz-db` (the #8074 repro: the release
profile strips the proc-macro dylib): **builds**. The resulting
`libsqlx_macros-*.dylib` (14 MB) has `LC_SYMTAB stroff % 8 == 0` per
`otool -l`. The same build on 1.95.0 died with `mis-aligned LINKEDIT
string pool`.
- `just clippy` (workspace, `-D warnings`) and `cargo fmt --check`
(workspace and `desktop/src-tauri`): pass
- `just desktop-tauri-clippy` (default and `mesh-llm` feature sets):
pass
- `just test-unit`: every crate passes. The `buzz-acp` binary failed
only under pre-push lane contention; rerun solo it is 1060 passed, 0
failed.
- `just desktop-tauri-test`: 3422 passed, 0 failed
- After the lint fixes: lib/bin unit tests for `buzz-acp`, `buzz-agent`,
`buzz-auth`, `buzz-media`, `buzz-voice`, `buzz-sdk`, `buzz-pairing-cli`,
and `buzz-relay`'s `api:
:transport` tests, including the
Postgres-backed
`enforce_mode_git_pack_routes_missing_assertion_exact_bytes`, which
parses the rewritten PEM: pass
`cfg(windows)` code was not linted locally (no Windows target
installed). CI's Windows Rust job runs workspace clippy for
`x86_64-pc-windows-msvc`, and that passes on this branch. CI also builds
the relay, push-gateway and sprig images on the new `rust:1.98` bases
for amd64 and arm64.
No runtime behavior changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
188 lines
9.0 KiB
Docker
188 lines
9.0 KiB
Docker
# syntax=docker/dockerfile:1.7
|
|
#
|
|
# Public Buzz relay image — published as ghcr.io/block/buzz:<tag>.
|
|
#
|
|
# Builds the `buzz-relay` binary (Rust 1.98) and the `buzz-web` static bundle
|
|
# (pnpm + vite), then assembles them into a small debian-slim runtime with
|
|
# `git` available (the relay shells out to git for repo hydrate / receive-pack
|
|
# / upload-pack — see crates/buzz-relay/src/api/git).
|
|
#
|
|
# Multi-arch is handled by running this same Dockerfile on native amd64 and
|
|
# native arm64 runners (see .github/workflows/docker.yml). The Dockerfile
|
|
# itself is platform-agnostic; do not add --platform pins.
|
|
|
|
ARG RUST_VERSION=1.98
|
|
ARG NODE_VERSION=24
|
|
ARG DEBIAN_VERSION=bookworm
|
|
|
|
# Optional extra CA bundle for builds behind a TLS-intercepting corporate proxy
|
|
# (e.g. a Cloudflare/Zscaler gateway that re-signs TLS). Empty by default, so
|
|
# public CI builds are unaffected. Point it at a PEM file in the build context:
|
|
# docker build --build-arg EXTRA_CA_CERTS=path/to/proxy-ca.pem ...
|
|
# Consumed by the network-touching stages below (cargo + pnpm).
|
|
ARG EXTRA_CA_CERTS=
|
|
|
|
# Optional npm registry for builds where the public registry is unreachable or
|
|
# policy-blocked (e.g. a corporate mirror / Artifactory). Empty default = public
|
|
# npmjs, so public CI builds are unaffected. Consumed by the web-builder stage.
|
|
ARG NPM_REGISTRY=
|
|
|
|
# ─── Stage 1: cargo-chef base ───────────────────────────────────────────────
|
|
FROM rust:${RUST_VERSION}-${DEBIAN_VERSION} AS chef
|
|
# Trust an optional corporate-proxy CA before any network fetch (no-op if unset).
|
|
ARG EXTRA_CA_CERTS
|
|
COPY --chmod=0644 ${EXTRA_CA_CERTS:-Dockerfile} /tmp/extra-ca/src
|
|
RUN if [ -n "${EXTRA_CA_CERTS}" ]; then \
|
|
cp /tmp/extra-ca/src /usr/local/share/ca-certificates/extra-proxy-ca.crt \
|
|
&& update-ca-certificates \
|
|
&& echo "CARGO_HTTP_CAINFO=/etc/ssl/certs/ca-certificates.crt" >> /etc/environment; \
|
|
fi
|
|
ENV CARGO_HTTP_CAINFO=/etc/ssl/certs/ca-certificates.crt
|
|
RUN cargo install cargo-chef --locked --version 0.1.71
|
|
WORKDIR /build
|
|
|
|
# ─── Stage 2: plan dependency graph ─────────────────────────────────────────
|
|
# Only the manifests are needed to compute the recipe; this layer rebuilds
|
|
# only when Cargo.{toml,lock} or crate manifests change, not on every source
|
|
# edit.
|
|
FROM chef AS planner
|
|
COPY . .
|
|
RUN cargo chef prepare --recipe-path recipe.json
|
|
|
|
# ─── Stage 3: cook dependencies, then build the binary ──────────────────────
|
|
FROM chef AS builder
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
build-essential \
|
|
cmake \
|
|
pkg-config \
|
|
libssl-dev \
|
|
ca-certificates \
|
|
git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
# Keep enough DWARF for native profilers to resolve optimized code to source
|
|
# locations. The normal runtime strips it below; runtime-debug retains it.
|
|
ENV CARGO_PROFILE_RELEASE_DEBUG=line-tables-only
|
|
COPY --from=planner /build/recipe.json recipe.json
|
|
# Cook the full workspace recipe — relay deps include workspace siblings, so
|
|
# scoping to -p buzz-relay misses transitive deps and re-builds them later.
|
|
RUN cargo chef cook --release --recipe-path recipe.json
|
|
COPY . .
|
|
# Compile immutable artifact identity into the relay. Defaults preserve local
|
|
# and third-party builds that do not run in provenance-aware CI.
|
|
ARG BUZZ_SOURCE_SHA=unknown
|
|
ARG BUZZ_BUILD_ID=local
|
|
ARG BUZZ_BUILD_URL=unknown
|
|
ENV BUZZ_SOURCE_SHA=${BUZZ_SOURCE_SHA} \
|
|
BUZZ_BUILD_ID=${BUZZ_BUILD_ID} \
|
|
BUZZ_BUILD_URL=${BUZZ_BUILD_URL}
|
|
RUN cargo build --release --locked -p buzz-relay --bin buzz-relay \
|
|
-p buzz-admin --bin buzz-admin \
|
|
-p buzz-pair-relay --bin buzz-pair-relay
|
|
|
|
# Derive the normal release binaries from the same optimized ELF files as the
|
|
# debug image so the two variants cannot drift at code-generation time.
|
|
FROM builder AS stripped-binaries
|
|
RUN strip target/release/buzz-relay \
|
|
&& strip target/release/buzz-admin \
|
|
&& strip target/release/buzz-pair-relay
|
|
|
|
# ─── Stage 4: web bundle (pnpm + vite) ──────────────────────────────────────
|
|
# Independent of the Rust layers so a CSS change doesn't bust Rust cache and
|
|
# vice versa.
|
|
FROM node:${NODE_VERSION}-${DEBIAN_VERSION}-slim AS web-builder
|
|
WORKDIR /build
|
|
# Trust an optional corporate-proxy CA so corepack + pnpm can fetch over an
|
|
# intercepting TLS gateway (no-op if EXTRA_CA_CERTS is unset).
|
|
ARG EXTRA_CA_CERTS
|
|
COPY --chmod=0644 ${EXTRA_CA_CERTS:-Dockerfile} /tmp/extra-ca/src
|
|
RUN if [ -n "${EXTRA_CA_CERTS}" ]; then \
|
|
apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
|
&& cp /tmp/extra-ca/src /usr/local/share/ca-certificates/extra-proxy-ca.crt \
|
|
&& update-ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*; \
|
|
fi
|
|
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
|
|
# Point npm + corepack at an optional mirror (no-op when NPM_REGISTRY is unset).
|
|
# corepack reads COREPACK_NPM_REGISTRY to fetch the pinned pnpm; pnpm/npm read
|
|
# the .npmrc registry for dependency installs.
|
|
ARG NPM_REGISTRY
|
|
ENV COREPACK_NPM_REGISTRY=${NPM_REGISTRY}
|
|
# When using a mirror, disable corepack's npmjs signature check: the mirror
|
|
# republishes tarballs without the public registry's provenance signatures, so
|
|
# strict verification fails ("No compatible signature found"). Only relaxed on
|
|
# the mirror path — public builds (NPM_REGISTRY unset) keep strict verification.
|
|
RUN if [ -n "${NPM_REGISTRY}" ]; then \
|
|
echo "registry=${NPM_REGISTRY}" > /build/.npmrc \
|
|
&& echo "COREPACK_INTEGRITY_KEYS=0" >> /etc/environment; \
|
|
fi
|
|
ENV COREPACK_INTEGRITY_KEYS=${NPM_REGISTRY:+0}
|
|
RUN corepack enable
|
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
COPY patches/ patches/
|
|
COPY web/package.json web/
|
|
COPY admin-web/package.json admin-web/
|
|
RUN pnpm install --frozen-lockfile --filter buzz-web --filter buzz-admin-web
|
|
COPY web/ web/
|
|
COPY admin-web/ admin-web/
|
|
RUN pnpm -C web build && pnpm -C admin-web build
|
|
|
|
# ─── Stage 5: shared runtime ────────────────────────────────────────────────
|
|
FROM debian:${DEBIAN_VERSION}-slim AS runtime-base
|
|
|
|
# OCI annotations: required for GHCR to auto-link the image to this repo and
|
|
# inherit its visibility. org.opencontainers.image.source is the load-bearing
|
|
# one — without it GHCR keeps the image private even when the repo is public.
|
|
LABEL org.opencontainers.image.title="Buzz" \
|
|
org.opencontainers.image.description="WebSocket relay server for the Buzz communications platform" \
|
|
org.opencontainers.image.source="https://github.com/block/buzz" \
|
|
org.opencontainers.image.url="https://github.com/block/buzz" \
|
|
org.opencontainers.image.documentation="https://github.com/block/buzz#readme" \
|
|
org.opencontainers.image.licenses="Apache-2.0"
|
|
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
git \
|
|
openssl \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& groupadd --system --gid 1000 buzz \
|
|
&& useradd --system --uid 1000 --gid 1000 --home-dir /var/lib/buzz \
|
|
--create-home --shell /usr/sbin/nologin buzz
|
|
|
|
COPY --from=web-builder /build/web/dist /srv/buzz/web
|
|
COPY --from=web-builder /build/admin-web/dist /srv/buzz/admin-web
|
|
|
|
# The invite landing page is always served from the bundled web UI. Repository
|
|
# browser routes require the separate BUZZ_SERVE_GIT_WEB_GUI=true opt-in. The
|
|
# admin bundle is inert until BUZZ_ADMIN_HOST is configured.
|
|
ENV BUZZ_WEB_DIR=/srv/buzz/web \
|
|
BUZZ_ADMIN_WEB_DIR=/srv/buzz/admin-web
|
|
|
|
# 3000: app (WS + REST) · 8080: /_liveness, /_readiness · 9102: /metrics
|
|
EXPOSE 3000 8080 9102
|
|
|
|
# deploy/compose mounts a volume here; pre-created so it inherits buzz:buzz.
|
|
RUN mkdir -p /data/git && chown buzz:buzz /data/git
|
|
|
|
USER buzz:buzz
|
|
WORKDIR /var/lib/buzz
|
|
|
|
ENTRYPOINT ["/usr/local/bin/buzz-relay"]
|
|
|
|
# Optimized binaries with line-table debug information for native profiling.
|
|
# Published under debug-* tags; runtime behavior otherwise matches the normal
|
|
# image exactly.
|
|
FROM runtime-base AS runtime-debug
|
|
COPY --from=builder /build/target/release/buzz-relay /usr/local/bin/buzz-relay
|
|
COPY --from=builder /build/target/release/buzz-admin /usr/local/bin/buzz-admin
|
|
COPY --from=builder /build/target/release/buzz-pair-relay /usr/local/bin/buzz-pair-relay
|
|
|
|
# Keep the stripped runtime as the final/default Dockerfile target so existing
|
|
# `docker build .` callers and release tags retain their current behavior.
|
|
FROM runtime-base AS runtime
|
|
COPY --from=stripped-binaries /build/target/release/buzz-relay /usr/local/bin/buzz-relay
|
|
COPY --from=stripped-binaries /build/target/release/buzz-admin /usr/local/bin/buzz-admin
|
|
COPY --from=stripped-binaries /build/target/release/buzz-pair-relay /usr/local/bin/buzz-pair-relay
|