From b899bb9587bd8baf1b564ca9b1ea01857eee752d Mon Sep 17 00:00:00 2001 From: Ryan Kuester Date: Tue, 10 Mar 2026 19:28:14 -0500 Subject: [PATCH 1/3] build: add build toolchain container image Add a Podman-based build toolchain image (build.Containerfile) with pinned Rust compiler, rustfmt, clippy, and just. The base image is pinned by digest for reproducibility. The justfile gains two new recipes: - build-image: builds the toolchain image, skipping if unchanged - in-container: runs any just recipe inside the toolchain image The image is tagged with a content hash of the Containerfile so build-image can detect staleness without rebuilding. Named volumes cache the cargo registry and git checkouts across runs. --- Cargo.toml | 1 + build.Containerfile | 21 +++++++++++++++++++++ justfile | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 build.Containerfile diff --git a/Cargo.toml b/Cargo.toml index 813481a7..6985a7d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = ["mujina-miner", "tools/mujina-dissect"] resolver = "2" [workspace.package] +# When bumping the compiler version, also update build.Containerfile. edition = "2024" license = "GPL-3.0-or-later" authors = ["Ryan Kuester "] diff --git a/build.Containerfile b/build.Containerfile new file mode 100644 index 00000000..62e0f5dc --- /dev/null +++ b/build.Containerfile @@ -0,0 +1,21 @@ +# Build toolchain image for running checks in a reproducible environment. +# +# The Rust compiler and just are pinned to exact versions. The base +# image is pinned by digest. Apt packages come from the Debian +# bookworm repos bundled in the base image; their exact versions can +# drift across builds but are stable system libraries that don't +# affect build output. + +FROM docker.io/library/rust:1.94-bookworm@sha256:b2fe2c0f26e0e1759752b6b2eb93b119d30a80b91304f5b18069b31ea73eaee8 + +# These are pinned to the exact toolchain release by the base image digest. +RUN rustup component add rustfmt clippy + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libudev-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +RUN cargo install just --version 1.40.0 + +WORKDIR /workspace diff --git a/justfile b/justfile index 63ce5e01..bd7ca5a3 100644 --- a/justfile +++ b/justfile @@ -21,6 +21,26 @@ test: run: cargo run --bin mujina-minerd +BUILD_IMAGE := "mujina-build" +BUILD_TAG := `sha256sum build.Containerfile | cut -c1-12` + +# Build the build toolchain image (skips if unchanged) +[group('container')] +build-image: + podman image exists {{BUILD_IMAGE}}:{{BUILD_TAG}} || \ + podman build -t {{BUILD_IMAGE}}:{{BUILD_TAG}} -f build.Containerfile . + +# Run a just recipe inside the build toolchain image +[group('container')] +in-container *args: build-image + podman run --rm \ + -v "$(pwd)":/workspace:Z \ + -v mujina-cargo-registry:/usr/local/cargo/registry \ + -v mujina-cargo-git:/usr/local/cargo/git \ + -w /workspace \ + {{BUILD_IMAGE}}:{{BUILD_TAG}} \ + just {{args}} + [group('container')] container-build tag=`git rev-parse --abbrev-ref HEAD`: podman build -t mujina-minerd:{{tag}} -f Containerfile . From d4a88c2ef8a9cba4d285e0c127b70979aad5b2ed Mon Sep 17 00:00:00 2001 From: Ryan Kuester Date: Tue, 10 Mar 2026 19:29:57 -0500 Subject: [PATCH 2/3] ci: add GitHub Actions workflow Run `just ci` on pushes to main and pull requests. The justfile's ci recipe delegates to `in-container "checks"`, keeping the workflow minimal and the justfile as the single source of truth for what CI does. The build toolchain image is cached as a tar archive via actions/cache, keyed on the Containerfile content hash. On cache hit, podman load restores the image and build-image skips the rebuild. --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++++++++++++++ justfile | 4 ++++ 2 files changed, 47 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..28090ce0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + checks: + name: checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + + # Cache the build toolchain image as a portable tar archive. + # Podman's overlay storage can't be cached directly (tar fails + # on overlay whiteout files), so we round-trip through podman + # save/load. The cache key is the hash of build.Containerfile. + # The image is tagged with a short hash of the Containerfile so + # that build-image in the justfile recognizes it and skips the + # build. + - uses: actions/cache@v4 + id: image-cache + with: + path: /tmp/mujina-build.tar + key: build-image-${{ hashFiles('build.Containerfile') }} + + - name: Load cached build image + if: steps.image-cache.outputs.cache-hit == 'true' + run: podman load -i /tmp/mujina-build.tar + + - run: just ci + + - name: Save build image for cache + if: steps.image-cache.outputs.cache-hit != 'true' + run: | + TAG=$(sha256sum build.Containerfile | cut -c1-12) + podman save -o /tmp/mujina-build.tar "mujina-build:$TAG" diff --git a/justfile b/justfile index bd7ca5a3..f30387cf 100644 --- a/justfile +++ b/justfile @@ -41,6 +41,10 @@ in-container *args: build-image {{BUILD_IMAGE}}:{{BUILD_TAG}} \ just {{args}} +# The CI pipeline. This is what GitHub Actions runs. +[group('ci')] +ci: (in-container "checks") + [group('container')] container-build tag=`git rev-parse --abbrev-ref HEAD`: podman build -t mujina-minerd:{{tag}} -f Containerfile . From 794b3dd265607b8014a3f43fc60579b9e7293b63 Mon Sep 17 00:00:00 2001 From: Ryan Kuester Date: Tue, 10 Mar 2026 19:49:24 -0500 Subject: [PATCH 3/3] ci: cache compiled dependencies between runs Cache the target/ directory via actions/cache, keyed on the toolchain (Containerfile hash) and dependency lockfile. Project crate artifacts are pruned after each run so only compiled dependencies are stored. A restore-keys prefix allows partial cache hits when dependencies change. --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28090ce0..a9436f35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,8 +34,26 @@ jobs: if: steps.image-cache.outputs.cache-hit == 'true' run: podman load -i /tmp/mujina-build.tar + # Cache compiled dependencies to speed up builds. The key + # includes the toolchain (Containerfile) so a compiler bump + # invalidates stale artifacts. restore-keys lets a partial + # hit (new dependency added) still warm the cache. + - uses: actions/cache@v4 + with: + path: target + key: cargo-target-${{ hashFiles('build.Containerfile') }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + cargo-target-${{ hashFiles('build.Containerfile') }}- + - run: just ci + # Prune project crate artifacts before the cache saves, + # keeping only compiled dependencies. + - name: Prune project artifacts from target cache + run: | + find target -name 'mujina*' -delete 2>/dev/null || true + find target -name 'libmujina*' -delete 2>/dev/null || true + - name: Save build image for cache if: steps.image-cache.outputs.cache-hit != 'true' run: |