Skip to content

Commit 03a1099

Browse files
committed
fix: smaller images
Signed-off-by: Giles Cope <gilescope@gmail.com>
1 parent 50e51a3 commit 03a1099

3 files changed

Lines changed: 182 additions & 41 deletions

File tree

Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,18 @@ sp-weights = { default-features = false, git = "https://github.com/paritytech/po
367367

368368
[profile.release]
369369
panic = "unwind"
370+
# file:line in backtraces for our own crates (node, toolkit, pallets, tests),
371+
# without the size/cost of full (=2) debug info. Single source of truth: identical
372+
# across every release consumer, so they share the cooked-dependency cache image.
373+
debug = "line-tables-only"
374+
375+
# ...but NOT for dependencies. The substrate/polkadot-sdk dep graph dominates debug
376+
# size (~1 GB of .debug_str type-names alone); we rarely need file:line inside it.
377+
# Stripping debug from deps keeps the node binary and the cooked-deps cache image
378+
# lean while preserving line numbers in the frames we actually debug. Final binaries
379+
# additionally get their debug sections zlib-compressed post-link (Earthfile +build).
380+
[profile.release.package."*"]
381+
debug = 0
370382

371383
[profile.production]
372384
inherits = "release"

Earthfile

Lines changed: 142 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ VERSION 0.8
1414
# the builtin resolves to the literal string `HEAD` across every PR.
1515
ARG --global CACHE_KEY=local
1616

17+
# Content-addressed cache of cooked Rust dependencies (cargo-chef). The deps image
18+
# is named by hash(recipe.json · arch · rust-version · flavor); a cache miss cooks
19+
# locally (+cook-<flavor>), a hit is pulled. Pushing to the shared registry is OFF
20+
# by default so untrusted (PR) builds can't poison the cache — only trusted CI runs
21+
# on `main` flip ALLOW_CACHE_PUSH=true. See +cook-build / +build-prepare.
22+
ARG --global DEPS_CACHE_REPO=ghcr.io/midnight-ntwrk/midnight-node-deps
23+
ARG --global ALLOW_CACHE_PUSH=false
24+
1725
# ================ Local Targets START ================
1826
# If you add a new one here, prefix it with "local-"
1927
# Add the target name to the doc string so it shows up
@@ -140,15 +148,16 @@ subxt:
140148
# build-node-only builds only the midnight-node binary
141149
build-node-only:
142150
FROM +build-prepare
143-
COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \
151+
COPY --dir Cargo.lock Cargo.toml docs .sqlx \
144152
ledger node pallets primitives metadata res runtime util tests relay partner-chains .
145153

146154
ARG NATIVEARCH
147155

148156
RUN cargo auditable build -p midnight-node --locked --release
149157

150158
RUN mkdir -p /artifacts-$NATIVEARCH \
151-
&& mv /target/release/midnight-node /artifacts-$NATIVEARCH
159+
&& mv /target/release/midnight-node /artifacts-$NATIVEARCH \
160+
&& objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/midnight-node"
152161

153162
SAVE ARTIFACT /artifacts-$NATIVEARCH
154163

@@ -715,6 +724,24 @@ prep-no-copy:
715724
# FROM --platform=$NATIVEPLATFORM +node-ci-image-single-platform
716725
FROM midnightntwrk/midnight-node-ci:${RUST_VERSION}-${COMPACTC_VERSION}-$NATIVEARCH
717726

727+
# Inherited by every target built FROM +prep-no-copy (prep, build-prepare,
728+
# check-rust-prepare, test, …) so all cargo invocations share these settings:
729+
# - verbose output (equivalent to -v, and propagates into nested cargo builds
730+
# driven by nextest/auditable/chef that a per-line -v would miss)
731+
# - RUSTC_BOOTSTRAP=1 unlocks unstable cargo features on the stable toolchain
732+
# - checksum-freshness: decide out-of-date by file hash, not mtime (unstable;
733+
# requires the bootstrap above). Earthly's COPY (without --keep-ts) pins every
734+
# file to a fixed constant mtime (2020-04-16T12:00:00Z) for reproducible layers,
735+
# which defeats cargo's default mtime freshness check: re-copied content with the
736+
# same constant mtime would be seen as unchanged. Hashing the bytes avoids that.
737+
ENV CARGO_TERM_VERBOSE=true
738+
ENV RUSTC_BOOTSTRAP=1
739+
ENV CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true
740+
# Off everywhere: incremental artifacts are large and embed host-specific
741+
# absolute paths, which bloat and poison the cross-machine cooked-deps cache.
742+
# (Release is already non-incremental; this also covers the dev/check flavor.)
743+
ENV CARGO_INCREMENTAL=0
744+
718745
# ca-certificates and curl-minimal already present in the CI base image
719746

720747
RUN cargo --version
@@ -724,7 +751,7 @@ prep-no-copy:
724751

725752
prep:
726753
FROM +prep-no-copy
727-
COPY --keep-ts --dir \
754+
COPY --dir \
728755
Cargo.lock Cargo.toml .cargo .config .sqlx deny.toml docs \
729756
ledger LICENSE node pallets primitives README.md res runtime \
730757
metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION .
@@ -800,21 +827,41 @@ planner:
800827
RUN cargo chef prepare --recipe-path recipe.json
801828
SAVE ARTIFACT recipe.json /recipe.json
802829

803-
check-rust-prepare:
804-
# NOTE: This just uses recipe.json - no src files!
830+
# cook-check pre-compiles deps for the check (clippy) flavor. Cooks the superset —
831+
# clippy, all-targets (dev-deps), and the rb+try-runtime features check-rust lints —
832+
# so check-rust fully reuses it and check-feature-unification rides on top, recompiling
833+
# only its --no-dev-deps/default-features delta.
834+
cook-check:
805835
FROM +prep-no-copy
806-
# COPY +planner/recipe.json /recipe.json
807-
CACHE --sharing shared --id cargo-git /usr/local/cargo/git
808-
CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
836+
COPY +planner/recipe.json /recipe.json
837+
RUN cargo chef cook --clippy --workspace --all-targets --features runtime-benchmarks,try-runtime --recipe-path /recipe.json
838+
ARG DEPS_TAG
839+
IF [ "$ALLOW_CACHE_PUSH" = "true" ]
840+
SAVE IMAGE --push $DEPS_CACHE_REPO:$DEPS_TAG
841+
END
809842

810-
# Build dependencies - this is the caching Docker layer!
811-
# RUN SKIP_WASM_BUILD=1 cargo chef cook --clippy --workspace --all-targets --features runtime-benchmarks --recipe-path /recipe.json
843+
check-rust-prepare:
844+
# Resolve the check dependency cache (pull or cook), mirroring +build-prepare.
845+
FROM +prep-no-copy
846+
COPY +planner/recipe.json /recipe.json
847+
COPY rust-toolchain.toml /rust-toolchain.toml
848+
ARG NATIVEARCH
849+
ARG RUST_VERSION=$(grep '^channel' /rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/')
850+
ARG RECIPE_HASH=$(sha256sum /recipe.json | cut -c1-16)
851+
ARG DEPS_TAG=${RUST_VERSION}-${NATIVEARCH}-check-${RECIPE_HASH}
852+
COPY (+cache-probe/hit.txt --DEPS_TAG=$DEPS_TAG) /deps-hit.txt
853+
ARG DEPS_HIT=$(cat /deps-hit.txt)
854+
IF [ "$DEPS_HIT" = "true" ]
855+
FROM $DEPS_CACHE_REPO:$DEPS_TAG
856+
ELSE
857+
FROM +cook-check --DEPS_TAG=$DEPS_TAG
858+
END
812859

813860
check-rust:
814861
FROM +check-rust-prepare
815862
CACHE --sharing shared --id cargo-git /usr/local/cargo/git
816863
CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
817-
COPY --keep-ts --dir \
864+
COPY --dir \
818865
Cargo.lock Cargo.toml .config .sqlx deny.toml docs \
819866
ledger LICENSE node pallets primitives README.md res runtime \
820867
metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION .
@@ -835,7 +882,7 @@ check-feature-unification:
835882
FROM +check-rust-prepare
836883
CACHE --sharing shared --id cargo-git /usr/local/cargo/git
837884
CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
838-
COPY --keep-ts --dir \
885+
COPY --dir \
839886
Cargo.lock Cargo.toml .config .sqlx deny.toml docs \
840887
ledger LICENSE node pallets primitives README.md res runtime \
841888
metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION .
@@ -870,16 +917,21 @@ check:
870917
# Core tests - excludes Midnight Node Toolkit (requires Node Toolkit (JS) npm packages from midnight-js)
871918
test:
872919
ARG NATIVEARCH
873-
FROM +prep
920+
# Consume the release deps cache (cooked once, shared with +build). No CACHE /target
921+
# mount here: it would shadow the cooked /target baked into the build image.
922+
FROM +build-prepare
874923
CACHE --sharing shared --id cargo-git /usr/local/cargo/git
875924
CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
876-
# See top-of-file CACHE_KEY ARG for why this is scoped.
877-
CACHE --id target-${CACHE_KEY} /target
925+
COPY --dir Cargo.lock Cargo.toml .cargo .config .sqlx deny.toml docs \
926+
ledger LICENSE node pallets primitives README.md res runtime \
927+
metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION .
878928

879929
# Test
880930
RUN mkdir /test-artifacts
881-
# Note: debug and opt-level=1 OOM the linker (>24GB) due to large test binaries
882-
ENV RUSTFLAGS="-C target-cpu=native -C opt-level=2 -C debuginfo=1"
931+
# No RUSTFLAGS override: release profile (opt-3) inherited — keeps test binaries
932+
# small enough to link (debug/opt-1 OOM'd the linker at >24GB), and debuginfo is
933+
# line-tables via [profile.release] in Cargo.toml. target-cpu=native is gone (it
934+
# made artifacts non-portable across the machines now sharing this image).
883935
COPY .envrc ./bin/.envrc
884936
COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree
885937
ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static
@@ -914,23 +966,24 @@ test:
914966
# These tests do NOT require toolkit-js
915967
test-pallet-fixtures:
916968
ARG NATIVEARCH
917-
FROM +prep
969+
# Unified with +test onto the release deps cache. Previously a separate debug build
970+
# (for speed); now it rides the shared image, so release costs nothing extra here.
971+
FROM +build-prepare
918972
CACHE --sharing shared --id cargo-git /usr/local/cargo/git
919973
CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
920-
# See top-of-file CACHE_KEY ARG for why this is scoped.
921-
CACHE --id target-${CACHE_KEY} /target
974+
COPY --dir Cargo.lock Cargo.toml .cargo .config .sqlx deny.toml docs \
975+
ledger LICENSE node pallets primitives README.md res runtime \
976+
metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION .
922977

923978
# These tests use a mock runtime (MockBlock<Test>), not the real WASM runtime.
924-
# Debug mode skips LLVM optimization passes, compiling faster than release on free CI runners.
925979
ENV SKIP_WASM_BUILD=1
926-
ENV RUSTFLAGS="-C debuginfo=1"
927980
COPY .envrc ./bin/.envrc
928981
COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree
929982
ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static
930983

931-
# Run pallet-midnight fixture tests in debug mode (compiles much faster)
984+
# --release so the fingerprints match +test/+build and reuse the shared deps.
932985
WITH DOCKER
933-
RUN MIDNIGHT_LEDGER_EXPERIMENTAL=1 cargo nextest r --profile ci --locked \
986+
RUN MIDNIGHT_LEDGER_EXPERIMENTAL=1 cargo nextest r --profile ci --release --locked \
934987
-E 'test(/^tests::test_get_contract_state$/) | test(/^tests::test_send_mn_transaction$/) | test(/^tests::test_validation_works$/)'
935988
END
936989
# RUN cargo llvm-cov report --html --release --output-dir /test-artifacts-pallet-fixtures-$NATIVEARCH/html
@@ -968,7 +1021,9 @@ build-test-toolkit:
9681021
# Test
9691022
RUN mkdir /test-artifacts-toolkit
9701023
# Compile the tests to go as fast as possible on this machine:
971-
ENV RUSTFLAGS="-C target-cpu=native -C debuginfo=1"
1024+
# target-cpu=native removed: non-portable codegen under a fixed fingerprint is
1025+
# unsound once build artifacts are shared across machines.
1026+
ENV RUSTFLAGS="-C debuginfo=1"
9721027
COPY .envrc ./bin/.envrc
9731028
COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree
9741029
ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static
@@ -1023,31 +1078,69 @@ test-toolkit:
10231078
END
10241079
SAVE ARTIFACT /artifacts AS LOCAL ./test-artifacts-toolkit
10251080

1081+
# cache-probe (LOCALLY): does the named deps image already exist in the registry?
1082+
# Runs on the host so it uses host docker + registry creds. Emits hit.txt
1083+
# ("true"/"false"). Generic over flavor — the caller passes the full DEPS_TAG.
1084+
# Not logged in / no creds → inspect fails → "false" → the caller cooks locally.
1085+
cache-probe:
1086+
LOCALLY
1087+
ARG DEPS_TAG
1088+
RUN docker manifest inspect "$DEPS_CACHE_REPO:$DEPS_TAG" >/dev/null 2>&1 \
1089+
&& echo true > "/tmp/deps-hit-$DEPS_TAG" \
1090+
|| echo false > "/tmp/deps-hit-$DEPS_TAG"
1091+
SAVE ARTIFACT "/tmp/deps-hit-$DEPS_TAG" hit.txt
1092+
1093+
# cook-build pre-compiles every third-party dependency for the release (build)
1094+
# flavor into a content-addressed image. The codegen env here is baked into the
1095+
# image, so both the pull and the cook path expose identical CC/CXX to the
1096+
# downstream workspace build. Consumed by +build-prepare on a cache miss.
1097+
cook-build:
1098+
FROM +prep-no-copy
1099+
ENV CC=clang
1100+
ENV CXX=clang++
1101+
ENV CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true
1102+
COPY +planner/recipe.json /recipe.json
1103+
# Deps inherit [profile.release] (opt-3, line-tables) from chef's reconstructed
1104+
# skeleton — matching what +build compiles the workspace crates with, so cargo
1105+
# reuses them instead of rebuilding.
1106+
RUN cargo chef cook --release --recipe-path /recipe.json
1107+
ARG DEPS_TAG
1108+
IF [ "$ALLOW_CACHE_PUSH" = "true" ]
1109+
SAVE IMAGE --push $DEPS_CACHE_REPO:$DEPS_TAG
1110+
END
1111+
10261112
build-prepare:
1027-
# NOTE: This just uses recipe.json - no src files!
1113+
# Resolve the release dependency cache: pull the pre-cooked image if it exists,
1114+
# else cook it locally (+cook-build). The cook is the expensive layer; this
1115+
# shares it across machines/CI runs. recipe.json + rust-toolchain.toml are used
1116+
# only to compute the tag — discarded when we re-FROM the chosen base below.
10281117
FROM +prep-no-copy
1029-
# TODO: re-enable when chef is improved.
1030-
# COPY +planner/recipe.json /recipe.json
1031-
# CACHE --sharing shared --id cargo-git /usr/local/cargo/git
1032-
# CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
1118+
COPY +planner/recipe.json /recipe.json
1119+
COPY rust-toolchain.toml /rust-toolchain.toml
1120+
ARG NATIVEARCH
1121+
ARG RUST_VERSION=$(grep '^channel' /rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/')
1122+
ARG RECIPE_HASH=$(sha256sum /recipe.json | cut -c1-16)
1123+
ARG DEPS_TAG=${RUST_VERSION}-${NATIVEARCH}-build-${RECIPE_HASH}
1124+
COPY (+cache-probe/hit.txt --DEPS_TAG=$DEPS_TAG) /deps-hit.txt
1125+
ARG DEPS_HIT=$(cat /deps-hit.txt)
1126+
IF [ "$DEPS_HIT" = "true" ]
1127+
FROM $DEPS_CACHE_REPO:$DEPS_TAG
1128+
ELSE
1129+
FROM +cook-build --DEPS_TAG=$DEPS_TAG
1130+
END
10331131

1132+
# Per-commit, affects only the node crate's build script — kept out of the cook
1133+
# so it doesn't bust the deps cache every commit.
10341134
ARG EARTHLY_GIT_SHORT_HASH
10351135
ENV SUBSTRATE_CLI_GIT_COMMIT_HASH=$EARTHLY_GIT_SHORT_HASH
1036-
ENV CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true
1037-
ENV CC=clang
1038-
ENV CXX=clang++
1039-
1040-
# Build dependencies - this is the caching Docker layer!
1041-
# TODO: re-enable when chef is improved.
1042-
# RUN SKIP_WASM_BUILD=1 cargo chef cook --release --workspace --all-targets --recipe-path /recipe.json
10431136

10441137
# build creates production ready binaries
10451138
build:
10461139
FROM +build-prepare
10471140
# CACHE --sharing shared --id cargo-git /usr/local/cargo/git
10481141
# CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry
10491142
# CACHE /target
1050-
COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \
1143+
COPY --dir Cargo.lock Cargo.toml docs .sqlx \
10511144
ledger node pallets primitives metadata res runtime util tests relay partner-chains COMPACTC_VERSION .
10521145

10531146
ARG NATIVEARCH
@@ -1071,11 +1164,18 @@ build:
10711164
&& mv /target/release/aiken-deployer /artifacts-$NATIVEARCH \
10721165
&& cp /target/release/wbuild/midnight-node-runtime/*.wasm /artifacts-$NATIVEARCH/midnight-node-runtime/
10731166

1167+
# zlib-compress the (our-code-only) line-tables debug in the shipped ELF binaries:
1168+
# ~1/3 the size, file:line backtraces preserved (verified — runtime panic traces
1169+
# resolve from SHF_COMPRESSED sections). Post-link file op, no build-cache impact.
1170+
RUN for b in midnight-node midnight-node-toolkit aiken-deployer; do \
1171+
objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/$b"; \
1172+
done
1173+
10741174
SAVE ARTIFACT /artifacts-$NATIVEARCH AS LOCAL artifacts
10751175

10761176
build-benchmarks:
10771177
FROM +build-prepare
1078-
COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \
1178+
COPY --dir Cargo.lock Cargo.toml docs .sqlx \
10791179
ledger node pallets primitives metadata relay res runtime util tests partner-chains .
10801180

10811181
ARG NATIVEARCH
@@ -1085,7 +1185,8 @@ build-benchmarks:
10851185
cargo auditable build --workspace --locked --release --features runtime-benchmarks
10861186

10871187
RUN mkdir -p /artifacts-$NATIVEARCH \
1088-
&& mv /target/release/midnight-node /artifacts-$NATIVEARCH/midnight-node-benchmarks
1188+
&& mv /target/release/midnight-node /artifacts-$NATIVEARCH/midnight-node-benchmarks \
1189+
&& objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/midnight-node-benchmarks"
10891190

10901191
SAVE ARTIFACT /artifacts-$NATIVEARCH AS LOCAL artifacts-benchmarks
10911192

@@ -1494,7 +1595,7 @@ local-env-e2e:
14941595
# Defaults reproduce the legacy single-tenant ports.
14951596
ARG E2E_NODE_RPC_PORT=9933
14961597
ARG E2E_OGMIOS_PORT=1337
1497-
COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \
1598+
COPY --dir Cargo.lock Cargo.toml docs .sqlx \
14981599
ledger node pallets primitives metadata res runtime util tests relay partner-chains local-environment scripts .
14991600
COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree
15001601
ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#node #toolkit
2+
# Line-number panic backtraces, and a cargo-chef dependency cache
3+
4+
The `midnight-node` and `midnight-node-toolkit` release binaries now carry
5+
`line-tables-only` debug info, so panic backtraces resolve to `file:line`
6+
for our own crates (`RUST_BACKTRACE=1` still required at runtime).
7+
Dependencies are built with no debug info
8+
(`[profile.release.package."*"] debug = 0`) and the binaries' debug
9+
sections are zlib-compressed after linking, keeping the cost modest:
10+
`midnight-node` is ~331 MB, versus ~181 MB with no debug and ~1.9 GB if
11+
debug were left on across the whole dependency graph. Backtrace frames
12+
inside dependencies show addresses rather than line numbers — the
13+
deliberate trade for size.
14+
15+
The Earthfile re-enables cargo-chef dependency caching as a
16+
content-addressed image, named by a hash of the chef recipe, target
17+
architecture, Rust version, and build flavor. A cache miss cooks
18+
dependencies locally; a hit is pulled. Pushing to the shared registry is
19+
gated behind `ALLOW_CACHE_PUSH`, which defaults to off so untrusted PR
20+
builds cannot poison the cache — only trusted CI on `main` pushes. The
21+
`check` (clippy) and `build` (release) flavors get separate images
22+
because their cargo flags differ; `test`, `benchmarks`, and the
23+
pallet-fixture tests share the `build` image. The previously test-only
24+
`target-cpu=native` flag was removed: non-portable codegen under a fixed
25+
fingerprint is unsound once artifacts are shared across machines.
26+
27+
PR:
28+
Issue:

0 commit comments

Comments
 (0)