From 1e32b9512b007c478bc591ee84ec608b1bcfbe93 Mon Sep 17 00:00:00 2001 From: Mike Hume <1088457+mhumeSF@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:53:26 -0400 Subject: [PATCH 1/4] build(deb): add debian package build Everything installs under package-private paths: /usr/lib/monad-services binaries and libraries, monad-services-* units, /etc/default/monad-services config. Debian policy forbids touching /usr/local, and the monad package owns the archive binary and unit names there, so the private paths satisfy policy and let the two packages co-install. The monad user is provisioned outside the package. --- .dockerignore | 3 + .gitignore | 1 + Makefile | 31 +++++ debian/DEBIAN/conffiles | 1 + debian/DEBIAN/control.in | 10 ++ debian/DEBIAN/postinst | 10 ++ debian/DEBIAN/postrm | 13 ++ debian/DEBIAN/prerm | 12 ++ debian/etc/monad-services/env | 3 + .../monad-services-archive-checker.service | 17 +++ .../system/monad-services-archiver.service | 17 +++ .../monad-services-block-writer.service | 17 +++ .../system/monad-services-indexer.service | 17 +++ docker/debian-package/Dockerfile | 45 +++++++ scripts/build-binaries | 25 ++++ scripts/build-deb | 122 ++++++++++++++++++ scripts/package-version | 10 ++ 17 files changed, 354 insertions(+) create mode 100644 .dockerignore create mode 100644 Makefile create mode 100644 debian/DEBIAN/conffiles create mode 100644 debian/DEBIAN/control.in create mode 100755 debian/DEBIAN/postinst create mode 100755 debian/DEBIAN/postrm create mode 100755 debian/DEBIAN/prerm create mode 100644 debian/etc/monad-services/env create mode 100644 debian/usr/lib/systemd/system/monad-services-archive-checker.service create mode 100644 debian/usr/lib/systemd/system/monad-services-archiver.service create mode 100644 debian/usr/lib/systemd/system/monad-services-block-writer.service create mode 100644 debian/usr/lib/systemd/system/monad-services-indexer.service create mode 100644 docker/debian-package/Dockerfile create mode 100755 scripts/build-binaries create mode 100755 scripts/build-deb create mode 100755 scripts/package-version diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4578d1c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +target +dist diff --git a/.gitignore b/.gitignore index ad67955..9aec134 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # will have compiled files and executables debug target +dist # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c30d6bc --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +SHELL := /bin/sh + +CONTAINER_ENGINE ?= $(shell command -v podman 2>/dev/null || command -v docker 2>/dev/null) +ifeq ($(origin PACKAGE_VERSION),undefined) +PACKAGE_VERSION := $(shell ./scripts/package-version) +endif +DEB_OUTPUT_DIR ?= dist + +.PHONY: build deb deb-host clean + +build: + ./scripts/build-binaries + +# Needs the docker/debian-package toolchain on the host; `make deb` needs only a container engine. +deb-host: + PACKAGE_VERSION=$(PACKAGE_VERSION) DEB_OUTPUT_DIR=$(DEB_OUTPUT_DIR) ./scripts/build-deb + +deb: + @test -n "$(CONTAINER_ENGINE)" || { echo "Install Docker or Podman, or set CONTAINER_ENGINE."; exit 1; } + @mkdir -p $(DEB_OUTPUT_DIR); iidfile=$$(mktemp); \ + trap 'rm -f "$$iidfile"' EXIT; \ + $(CONTAINER_ENGINE) build \ + --iidfile "$$iidfile" \ + --build-arg PACKAGE_VERSION=$(PACKAGE_VERSION) \ + -f docker/debian-package/Dockerfile . && \ + container_id=$$($(CONTAINER_ENGINE) create "$$(cat "$$iidfile")" true) && \ + trap '$(CONTAINER_ENGINE) rm -f $$container_id >/dev/null; rm -f "$$iidfile"' EXIT && \ + $(CONTAINER_ENGINE) cp "$$container_id":/out/. $(DEB_OUTPUT_DIR) + +clean: + rm -rf $(DEB_OUTPUT_DIR) diff --git a/debian/DEBIAN/conffiles b/debian/DEBIAN/conffiles new file mode 100644 index 0000000..06d5bf0 --- /dev/null +++ b/debian/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/monad-services/env diff --git a/debian/DEBIAN/control.in b/debian/DEBIAN/control.in new file mode 100644 index 0000000..7094365 --- /dev/null +++ b/debian/DEBIAN/control.in @@ -0,0 +1,10 @@ +Package: monad-services +Version: @PACKAGE_VERSION@ +Section: net +Priority: optional +Architecture: amd64 +Maintainer: Category Labs +Depends: @DEPENDS@, ca-certificates +Description: Monad archive and indexing services + Archive, validation, indexing, and block-writing binaries maintained by the + monad-services repository. diff --git a/debian/DEBIAN/postinst b/debian/DEBIAN/postinst new file mode 100755 index 0000000..d2c4735 --- /dev/null +++ b/debian/DEBIAN/postinst @@ -0,0 +1,10 @@ +#!/bin/sh +set -eu + +case "$1" in + configure) + if [ -d /run/systemd/system ]; then + systemctl daemon-reload || true + fi + ;; +esac diff --git a/debian/DEBIAN/postrm b/debian/DEBIAN/postrm new file mode 100755 index 0000000..8417f25 --- /dev/null +++ b/debian/DEBIAN/postrm @@ -0,0 +1,13 @@ +#!/bin/sh +set -eu + +case "$1" in + remove|purge) + if [ -d /run/systemd/system ]; then + systemctl daemon-reload || true + for unit in monad-services-archiver monad-services-archive-checker monad-services-indexer monad-services-block-writer; do + systemctl reset-failed "$unit.service" >/dev/null 2>&1 || true + done + fi + ;; +esac diff --git a/debian/DEBIAN/prerm b/debian/DEBIAN/prerm new file mode 100755 index 0000000..24c7c94 --- /dev/null +++ b/debian/DEBIAN/prerm @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +case "$1" in + remove|deconfigure) + if [ -d /run/systemd/system ]; then + for unit in monad-services-archiver monad-services-archive-checker monad-services-indexer monad-services-block-writer; do + systemctl stop "$unit.service" || true + done + fi + ;; +esac diff --git a/debian/etc/monad-services/env b/debian/etc/monad-services/env new file mode 100644 index 0000000..506d8f8 --- /dev/null +++ b/debian/etc/monad-services/env @@ -0,0 +1,3 @@ +# Configuration for the monad-services units. Variables set here are +# expanded into each unit's ExecStart line; a unit fails to start when a +# variable it names is missing. diff --git a/debian/usr/lib/systemd/system/monad-services-archive-checker.service b/debian/usr/lib/systemd/system/monad-services-archive-checker.service new file mode 100644 index 0000000..a329031 --- /dev/null +++ b/debian/usr/lib/systemd/system/monad-services-archive-checker.service @@ -0,0 +1,17 @@ +[Unit] +Description=Monad Archive Checker (monad-services) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=monad +Group=monad +EnvironmentFile=-/etc/monad-services/env +ExecStart=/usr/lib/monad-services/monad-archive-checker +Restart=always +RestartSec=5 +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target diff --git a/debian/usr/lib/systemd/system/monad-services-archiver.service b/debian/usr/lib/systemd/system/monad-services-archiver.service new file mode 100644 index 0000000..701fa8a --- /dev/null +++ b/debian/usr/lib/systemd/system/monad-services-archiver.service @@ -0,0 +1,17 @@ +[Unit] +Description=Monad Archiver (monad-services) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=monad +Group=monad +EnvironmentFile=-/etc/monad-services/env +ExecStart=/usr/lib/monad-services/monad-archiver +Restart=always +RestartSec=5 +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target diff --git a/debian/usr/lib/systemd/system/monad-services-block-writer.service b/debian/usr/lib/systemd/system/monad-services-block-writer.service new file mode 100644 index 0000000..514bee4 --- /dev/null +++ b/debian/usr/lib/systemd/system/monad-services-block-writer.service @@ -0,0 +1,17 @@ +[Unit] +Description=Monad Block Writer (monad-services) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=monad +Group=monad +EnvironmentFile=-/etc/monad-services/env +ExecStart=/usr/lib/monad-services/monad-block-writer +Restart=always +RestartSec=5 +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target diff --git a/debian/usr/lib/systemd/system/monad-services-indexer.service b/debian/usr/lib/systemd/system/monad-services-indexer.service new file mode 100644 index 0000000..4896c47 --- /dev/null +++ b/debian/usr/lib/systemd/system/monad-services-indexer.service @@ -0,0 +1,17 @@ +[Unit] +Description=Monad Indexer (monad-services) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=monad +Group=monad +EnvironmentFile=-/etc/monad-services/env +ExecStart=/usr/lib/monad-services/monad-indexer +Restart=always +RestartSec=5 +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target diff --git a/docker/debian-package/Dockerfile b/docker/debian-package/Dockerfile new file mode 100644 index 0000000..d24ad2f --- /dev/null +++ b/docker/debian-package/Dockerfile @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:1 +FROM ubuntu:26.04 AS package + +ARG PACKAGE_VERSION +ENV DEBIAN_FRONTEND=noninteractive \ + CARGO_NET_GIT_FETCH_WITH_CLI=true \ + RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:/usr/local/rustup/toolchains/1.91.1-x86_64-unknown-linux-gnu/bin:${PATH} \ + CC=gcc-15 \ + CXX=g++-15 \ + LIBCLANG_PATH=/usr/lib/llvm-19/lib \ + CMAKE_GENERATOR=Ninja \ + TRIEDB_TARGET=triedb_driver \ + ASMFLAGS=-march=haswell \ + CFLAGS=-march=haswell \ + CXXFLAGS=-march=haswell + +# build-essential carries the packaging tools (including dpkg-shlibdeps); +# patchelf removes build-tree runpaths before dependency analysis. +RUN apt-get update && apt-get install -y --no-install-recommends \ + binutils build-essential ca-certificates clang-19 cmake curl g++-15 gcc-15 git \ + libabsl-dev libarchive-dev libbenchmark-dev libboost-all-dev libbrotli-dev libcap-dev \ + libcgroup-dev libclang-19-dev libcli11-dev libcrypto++-dev libgmp-dev \ + libgmock-dev libgtest-dev libhugetlbfs-dev libmagicenum-dev libmimalloc-dev \ + libsecp256k1-dev libssl-dev \ + libstdc++-15-dev libtbb-dev liburing-dev libzstd-dev ninja-build patchelf pkg-config \ + && rm -rf /var/lib/apt/lists/* + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --profile minimal --default-toolchain 1.91.1 + +# .dockerignore strips .git, so scripts/package-version cannot run here. +RUN test -n "$PACKAGE_VERSION" + +WORKDIR /src +COPY . . + +RUN --mount=type=cache,id=monad-services-cargo-registry,target=/usr/local/cargo/registry \ + --mount=type=cache,id=monad-services-cargo-git,target=/usr/local/cargo/git \ + --mount=type=cache,id=monad-services-target,target=/src/target \ + PACKAGE_VERSION="${PACKAGE_VERSION}" DEB_OUTPUT_DIR=/out ./scripts/build-deb + +FROM scratch +COPY --from=package /out /out diff --git a/scripts/build-binaries b/scripts/build-binaries new file mode 100755 index 0000000..aaaeddb --- /dev/null +++ b/scripts/build-binaries @@ -0,0 +1,25 @@ +#!/usr/bin/env sh +set -eu + +root_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) + +# --- toolchain (matches the release build, in docker/debian-package/Dockerfile) --- +export CC="${CC:-gcc-15}" +export CXX="${CXX:-g++-15}" +export CMAKE_GENERATOR="${CMAKE_GENERATOR:-Ninja}" +# monad-triedb's build script builds libtriedb_driver.so from this target. +export TRIEDB_TARGET="${TRIEDB_TARGET:-triedb_driver}" +# Arch flags reach the cargo build only; the cmake toolchain file owns its own. +asmflags="${ASMFLAGS:--march=haswell}" +cflags="${CFLAGS:--march=haswell}" +cxxflags="${CXXFLAGS:--march=haswell}" + +cd "$root_dir" + +# --- rust binaries --- +# monad-triedb and the execution bindings it needs are built by Cargo build +# scripts out of the pinned monad-bft git dependency; there is no local +# execution checkout to build separately. +ASMFLAGS="$asmflags" CFLAGS="$cflags" \ +CXXFLAGS="$cxxflags -DQUILL_ACTIVE_LOG_LEVEL=QUILL_LOG_LEVEL_CRITICAL" \ +cargo build --locked --release --package monad-archive --bins diff --git a/scripts/build-deb b/scripts/build-deb new file mode 100755 index 0000000..9c34563 --- /dev/null +++ b/scripts/build-deb @@ -0,0 +1,122 @@ +#!/usr/bin/env sh +set -eu + +root_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +output_dir=${DEB_OUTPUT_DIR:-"$root_dir/dist"} +strip_symbols=${STRIP_SYMBOLS:-0} +# Stage outside target/, which is a shared cache mount in container builds. +stage_root=$(mktemp -d) +trap 'rm -rf "$stage_root"' EXIT +stage_dir="$stage_root/debian/monad-services" + +# --- version --- +version=${PACKAGE_VERSION:-$("$root_dir/scripts/package-version")} +case "$version" in + *[!0-9A-Za-z.+:~\-]*) echo "Invalid Debian version: $version" >&2; exit 2 ;; +esac + +cd "$root_dir" + +# --- build --- +"$root_dir/scripts/build-binaries" + +# --- stage binaries --- +# Everything installs into the package-private /usr/lib/monad-services: +# Debian policy (FHS, section 9.1) forbids packages from touching +# /usr/local, and the monad package ships the same binary names and +# library copies under /usr/local, so the private directory is also what +# lets the two packages be co-installed. The binaries find the libraries +# through RUNPATH, set below, instead of ldconfig. +lib_dir="$stage_dir/usr/lib/monad-services" +mkdir -p "$stage_dir/DEBIAN" "$lib_dir" + +for binary in monad-archiver monad-archive-checker monad-indexer monad-block-writer; do + install -m 0755 "target/release/$binary" "$lib_dir/$binary" +done + +# --- stage libraries --- +# Bundle libtriedb_driver.so, which is loaded with dlopen at runtime and so +# never appears as a declared dependency, plus every library that the staged +# binaries or the driver resolve from inside this source tree. Those are +# build products the target host does not have. ldd lists dependencies +# transitively, so one pass is enough, and dpkg-shlibdeps below fails the +# build if anything is missed here. +triedb=$(find target/release -name 'libtriedb_driver.so' -exec ls -Lt {} + | head -n 1) +test -n "$triedb" || { echo "libtriedb_driver.so not found under target/release" >&2; exit 2; } +cp "$triedb" "$lib_dir/" +libs=$(ldd "$lib_dir"/* \ + | awk -v root="$root_dir/" '$2 == "=>" && index($3, root) == 1 { print $3 }' \ + | sort -u) +seen=" " +for lib in $libs; do + base=${lib##*/} + case $seen in + *" $base "*) + echo "Bundled library basename collision: $base ($lib)" >&2 + exit 2 + ;; + esac + seen="$seen$base " + cp "$lib" "$lib_dir/" +done + +# The 26.04 builder's libsecp256k1 soname exists on no fleet host; bundle it +# like Jenkins. A copy staged from the build tree above takes precedence. +secp=/usr/lib/x86_64-linux-gnu/libsecp256k1.so.6 +if [ -e "$secp" ] && [ ! -e "$lib_dir/${secp##*/}" ]; then + cp -L "$secp" "$lib_dir/" +fi + +if [ "$strip_symbols" = 1 ]; then + find "$lib_dir" -type f ! -name '*.so*' -exec strip --strip-all {} \; + find "$lib_dir" -type f -name '*.so*' -exec strip --strip-unneeded {} \; +fi + +# --- check linkage & compute library dependencies --- +# Strip build-tree runpaths, then run dpkg-shlibdeps to generate Depends. +# +# For every library our binaries need, dpkg-shlibdeps locates the file and +# maps it to a package: our own libraries (such as libtriedb_driver.so) +# resolve inside the debian/monad-services staging tree and need no Depends +# entry; system libraries become versioned Depends. +# +# Runpaths into the build tree have to go first, or a library that's absent +# on the target host still resolves here and the check passes wrongly. +find "$lib_dir" -type f \ + -exec patchelf --remove-rpath {} \; + +# dpkg-shlibdeps only warns about libraries it cannot find, so fail on them here. +unresolved=$(LD_LIBRARY_PATH="$lib_dir" \ + ldd "$lib_dir"/* 2>/dev/null \ + | grep 'not found' || true) +if [ -n "$unresolved" ]; then + printf 'Unresolved shared libraries after staging:\n%s\n' "$unresolved" >&2 + exit 2 +fi + +printf 'Source: monad-services\n\nPackage: monad-services\nArchitecture: amd64\nDescription: stub\n' \ + > "$stage_root/debian/control" +lib_deps=$(cd "$stage_root" && dpkg-shlibdeps -O \ + -ldebian/monad-services/usr/lib/monad-services \ + debian/monad-services/usr/lib/monad-services/*) +lib_deps=${lib_deps#shlibs:Depends=} + +# --- set runpaths --- +# The private directory is outside the ldconfig search path, so everything +# carries a $ORIGIN RUNPATH: the binaries sit next to the libraries, dlopen +# of libtriedb_driver.so honours the calling executable's RUNPATH, and the +# libraries reference each other the same way. This keeps the monad +# package's copies of the same libraries from ever resolving into these +# services, or vice versa. +find "$lib_dir" -type f \ + -exec patchelf --set-rpath '$ORIGIN' {} \; + +# --- debian metadata --- +cp -a debian/. "$stage_dir/" +sed -e "s/@PACKAGE_VERSION@/$version/" -e "s%@DEPENDS@%$lib_deps%" \ + debian/DEBIAN/control.in > "$stage_dir/DEBIAN/control" +rm "$stage_dir/DEBIAN/control.in" + +# --- package --- +mkdir -p "$output_dir" +dpkg-deb --root-owner-group --build "$stage_dir" "$output_dir/monad-services_${version}_amd64.deb" diff --git a/scripts/package-version b/scripts/package-version new file mode 100755 index 0000000..24b9083 --- /dev/null +++ b/scripts/package-version @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +# Single source of truth for the Debian package version, used by the Makefile +# and as scripts/build-deb's fallback. Requires a git checkout; container +# builds have no .git and must receive PACKAGE_VERSION explicitly. +set -eu + +root_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +timestamp=$(git -C "$root_dir" log -1 --format=%cd --date=format:%Y%m%d-%H%M) +sha=$(git -C "$root_dir" rev-parse --short=7 HEAD) +printf '0.0-%s-%s\n' "$timestamp" "$sha" From 2d54fffbdd6e8ba5a0e91dcbfa6eb5a8b33f033d Mon Sep 17 00:00:00 2001 From: Mike Hume <1088457+mhumeSF@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:21:34 -0400 Subject: [PATCH 2/4] build(deb): ship the policy copyright and changelog Policy 12.5 and 12.7 require both under /usr/share/doc; the changelog is a stub pointing at the git history. --- debian/usr/share/doc/monad-services/copyright | 9 +++++++++ scripts/build-deb | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 debian/usr/share/doc/monad-services/copyright diff --git a/debian/usr/share/doc/monad-services/copyright b/debian/usr/share/doc/monad-services/copyright new file mode 100644 index 0000000..bfbd674 --- /dev/null +++ b/debian/usr/share/doc/monad-services/copyright @@ -0,0 +1,9 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: monad-services +Source: https://github.com/category-labs/monad-services + +Files: * +Copyright: 2026 Category Labs, Inc. +License: GPL-3 + On Debian systems, the complete text of the GNU General Public + License version 3 can be found in /usr/share/common-licenses/GPL-3. diff --git a/scripts/build-deb b/scripts/build-deb index 9c34563..57a7c68 100755 --- a/scripts/build-deb +++ b/scripts/build-deb @@ -117,6 +117,13 @@ sed -e "s/@PACKAGE_VERSION@/$version/" -e "s%@DEPENDS@%$lib_deps%" \ debian/DEBIAN/control.in > "$stage_dir/DEBIAN/control" rm "$stage_dir/DEBIAN/control.in" +# Policy 12.7 wants a compressed Debian changelog next to the static +# copyright file; the git history is the real changelog, so ship a stub +# entry that points at it. +printf 'monad-services (%s) unstable; urgency=medium\n\n * See https://github.com/category-labs/monad-services for the change history.\n\n -- Category Labs %s\n' \ + "$version" "$(date -R)" \ + | gzip -9n > "$stage_dir/usr/share/doc/monad-services/changelog.Debian.gz" + # --- package --- mkdir -p "$output_dir" dpkg-deb --root-owner-group --build "$stage_dir" "$output_dir/monad-services_${version}_amd64.deb" From d21b09e7798facd8eda90ea790e901e80b696349 Mon Sep 17 00:00:00 2001 From: Mike Hume <1088457+mhumeSF@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:42:35 -0400 Subject: [PATCH 3/4] build(deb): split the toolchain into a builder image docker/builder carries everything make build needs, mirroring monad-bft; the debian-package layer adds only packaging tools on top. --- Makefile | 11 ++++++++--- docker/builder/Dockerfile | 30 +++++++++++++++++++++++++++++ docker/debian-package/Dockerfile | 33 +++++++++----------------------- 3 files changed, 47 insertions(+), 27 deletions(-) create mode 100644 docker/builder/Dockerfile diff --git a/Makefile b/Makefile index c30d6bc..093e654 100644 --- a/Makefile +++ b/Makefile @@ -5,22 +5,27 @@ ifeq ($(origin PACKAGE_VERSION),undefined) PACKAGE_VERSION := $(shell ./scripts/package-version) endif DEB_OUTPUT_DIR ?= dist +BUILDER_IMAGE ?= monad-services-builder:local -.PHONY: build deb deb-host clean +.PHONY: build builder deb deb-host clean build: ./scripts/build-binaries -# Needs the docker/debian-package toolchain on the host; `make deb` needs only a container engine. +# Needs the docker/builder toolchain on the host; `make deb` needs only a container engine. deb-host: PACKAGE_VERSION=$(PACKAGE_VERSION) DEB_OUTPUT_DIR=$(DEB_OUTPUT_DIR) ./scripts/build-deb -deb: +builder: @test -n "$(CONTAINER_ENGINE)" || { echo "Install Docker or Podman, or set CONTAINER_ENGINE."; exit 1; } + $(CONTAINER_ENGINE) build -t $(BUILDER_IMAGE) -f docker/builder/Dockerfile . + +deb: builder @mkdir -p $(DEB_OUTPUT_DIR); iidfile=$$(mktemp); \ trap 'rm -f "$$iidfile"' EXIT; \ $(CONTAINER_ENGINE) build \ --iidfile "$$iidfile" \ + --build-arg BUILDER_IMAGE=$(BUILDER_IMAGE) \ --build-arg PACKAGE_VERSION=$(PACKAGE_VERSION) \ -f docker/debian-package/Dockerfile . && \ container_id=$$($(CONTAINER_ENGINE) create "$$(cat "$$iidfile")" true) && \ diff --git a/docker/builder/Dockerfile b/docker/builder/Dockerfile new file mode 100644 index 0000000..0a8b0bf --- /dev/null +++ b/docker/builder/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:26.04 + +ENV DEBIAN_FRONTEND=noninteractive \ + RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:${PATH} \ + CC=gcc-15 \ + CXX=g++-15 \ + LIBCLANG_PATH=/usr/lib/llvm-19/lib \ + CMAKE_GENERATOR=Ninja \ + TRIEDB_TARGET=triedb_driver \ + ASMFLAGS=-march=haswell \ + CFLAGS=-march=haswell \ + CXXFLAGS=-march=haswell + +RUN apt-get update && apt-get install -y --no-install-recommends \ + binutils ca-certificates clang-19 cmake curl g++-15 gcc-15 git \ + libabsl-dev libarchive-dev libbenchmark-dev libboost-all-dev libbrotli-dev libcap-dev \ + libcgroup-dev libclang-19-dev libcli11-dev libcrypto++-dev libgmp-dev \ + libgmock-dev libgtest-dev libhugetlbfs-dev libmagicenum-dev libmimalloc-dev \ + libsecp256k1-dev libssl-dev \ + libstdc++-15-dev libtbb-dev liburing-dev libzstd-dev ninja-build pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# The toolchain version comes from rust-toolchain.toml, which the rustup +# shims on PATH also consult at cargo time, so image and build can't drift. +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --profile minimal --default-toolchain none +COPY rust-toolchain.toml /tmp/rust-toolchain/rust-toolchain.toml +RUN cd /tmp/rust-toolchain && rustup toolchain install && rm -rf /tmp/rust-toolchain diff --git a/docker/debian-package/Dockerfile b/docker/debian-package/Dockerfile index d24ad2f..c738119 100644 --- a/docker/debian-package/Dockerfile +++ b/docker/debian-package/Dockerfile @@ -1,35 +1,20 @@ # syntax=docker/dockerfile:1 -FROM ubuntu:26.04 AS package + +# Toolchain comes from docker/builder/Dockerfile. `make deb` builds it +# locally; override to reuse a prebuilt copy. +ARG BUILDER_IMAGE=monad-services-builder:local +FROM ${BUILDER_IMAGE} AS package ARG PACKAGE_VERSION ENV DEBIAN_FRONTEND=noninteractive \ - CARGO_NET_GIT_FETCH_WITH_CLI=true \ - RUSTUP_HOME=/usr/local/rustup \ - CARGO_HOME=/usr/local/cargo \ - PATH=/usr/local/cargo/bin:/usr/local/rustup/toolchains/1.91.1-x86_64-unknown-linux-gnu/bin:${PATH} \ - CC=gcc-15 \ - CXX=g++-15 \ - LIBCLANG_PATH=/usr/lib/llvm-19/lib \ - CMAKE_GENERATOR=Ninja \ - TRIEDB_TARGET=triedb_driver \ - ASMFLAGS=-march=haswell \ - CFLAGS=-march=haswell \ - CXXFLAGS=-march=haswell + CARGO_NET_GIT_FETCH_WITH_CLI=true -# build-essential carries the packaging tools (including dpkg-shlibdeps); -# patchelf removes build-tree runpaths before dependency analysis. +# Only what docker/builder does not already carry: build-essential for the +# packaging tools (including dpkg-shlibdeps) and patchelf for runpaths. RUN apt-get update && apt-get install -y --no-install-recommends \ - binutils build-essential ca-certificates clang-19 cmake curl g++-15 gcc-15 git \ - libabsl-dev libarchive-dev libbenchmark-dev libboost-all-dev libbrotli-dev libcap-dev \ - libcgroup-dev libclang-19-dev libcli11-dev libcrypto++-dev libgmp-dev \ - libgmock-dev libgtest-dev libhugetlbfs-dev libmagicenum-dev libmimalloc-dev \ - libsecp256k1-dev libssl-dev \ - libstdc++-15-dev libtbb-dev liburing-dev libzstd-dev ninja-build patchelf pkg-config \ + build-essential patchelf \ && rm -rf /var/lib/apt/lists/* -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --profile minimal --default-toolchain 1.91.1 - # .dockerignore strips .git, so scripts/package-version cannot run here. RUN test -n "$PACKAGE_VERSION" From 23cb933c05454f2dc33735159a8cbe03f4132867 Mon Sep 17 00:00:00 2001 From: Mike Hume <1088457+mhumeSF@users.noreply.github.com> Date: Sun, 30 Aug 2026 23:28:10 -0400 Subject: [PATCH 4/4] build(deb): strip binaries by default The release profile sets debug = true, so each unstripped binary is 375 MB against 30 MB stripped. Set STRIP_SYMBOLS=0 to keep the symbols. --- scripts/build-deb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-deb b/scripts/build-deb index 57a7c68..b4d3573 100755 --- a/scripts/build-deb +++ b/scripts/build-deb @@ -3,7 +3,7 @@ set -eu root_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) output_dir=${DEB_OUTPUT_DIR:-"$root_dir/dist"} -strip_symbols=${STRIP_SYMBOLS:-0} +strip_symbols=${STRIP_SYMBOLS:-1} # Stage outside target/, which is a shared cache mount in container builds. stage_root=$(mktemp -d) trap 'rm -rf "$stage_root"' EXIT