diff --git a/.github/workflows/build-publish-partnerchains-dev.yml b/.github/workflows/build-publish-partnerchains-dev.yml index d2a352848..fa485038b 100644 --- a/.github/workflows/build-publish-partnerchains-dev.yml +++ b/.github/workflows/build-publish-partnerchains-dev.yml @@ -43,4 +43,5 @@ jobs: echo "[net]" >> "$HOME"/.cargo/config echo "git-fetch-with-cli = true" >> "$HOME"/.cargo/config - . ./.envrc && earthly --ci --push +partnerchains-dev + # No --ci/--strict: +partnerchains-dev -> +node-image -> +build -> +cache-probe (LOCALLY). + . ./.envrc && earthly --push +partnerchains-dev diff --git a/.github/workflows/continuous-integration-checks.yml b/.github/workflows/continuous-integration-checks.yml index 392c6fe29..1a2571034 100644 --- a/.github/workflows/continuous-integration-checks.yml +++ b/.github/workflows/continuous-integration-checks.yml @@ -57,7 +57,11 @@ jobs: - name: Run build if: steps.guard.outputs.hit != 'true' run: | - . ./.envrc && earthly --ci +check + # No --ci/--strict: +check -> +check-rust-prepare -> +cache-probe uses + # LOCALLY (host registry probe), which strict mode forbids. The probe is + # a read-only existence check that falls back to cooking on miss, so + # build reproducibility is unaffected. + . ./.envrc && earthly +check - uses: ./.github/actions/tree-cache-guard/save if: steps.guard.outputs.hit != 'true' @@ -102,7 +106,8 @@ jobs: - name: Run feature unification check if: steps.guard.outputs.hit != 'true' run: | - . ./.envrc && earthly --ci +check-feature-unification + # No --ci/--strict: reaches +cache-probe (LOCALLY) via +check-rust-prepare. + . ./.envrc && earthly +check-feature-unification - uses: ./.github/actions/tree-cache-guard/save if: steps.guard.outputs.hit != 'true' diff --git a/.github/workflows/continuous-integration-test.yml b/.github/workflows/continuous-integration-test.yml index 08b1360cf..d0cc44867 100644 --- a/.github/workflows/continuous-integration-test.yml +++ b/.github/workflows/continuous-integration-test.yml @@ -94,7 +94,7 @@ jobs: --secret GITHUB_TOKEN="$GITHUB_TOKEN" \ --secret DOCKERHUB_USER="$DOCKERHUB_USER" \ --secret DOCKERHUB_TOKEN="$DOCKERHUB_TOKEN" \ - --strict $PUSH_FLAG \ + $PUSH_FLAG \ --remote-cache=ghcr.io/midnightntwrk/midnight-node:cache-test \ +test --CACHE_KEY="$CACHE_KEY" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7b07e852c..d1f09482f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -487,56 +487,6 @@ jobs: with: key: ${{ steps.guard.outputs.key }} - # Test pallet-midnight fixture tests - these do NOT require toolkit-js - test-pallet-fixtures: - permissions: - contents: read - actions: write - name: Test Pallet Fixtures - runs-on: ubuntu-latest - env: - FORCE_COLOR: 1 - steps: - - name: Checkout node repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 - with: - submodules: true - - - id: guard - uses: ./.github/actions/tree-cache-guard - - - uses: EarthBuild/actions-setup@cae2d9ab68894d8402751fe42e07c7cca0272f7f - if: steps.guard.outputs.hit != 'true' - with: - version: v0.8.16 - github-token: ${{ github.token }} - use-cache: false - - - name: Free disk space - if: steps.guard.outputs.hit != 'true' && runner.environment != 'self-hosted' - run: scripts/free-disk-space.sh - - - name: Run pallet fixture tests - if: steps.guard.outputs.hit != 'true' - env: - # See +test's `Run build` step for why EARTHLY_GIT_BRANCH is unsafe - # under actions/checkout; reach for the actual head/ref name instead. - GITHUB_REF_RAW: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }} - run: | - CACHE_KEY=$(printf '%s' "$GITHUB_REF_RAW" | tr -c 'A-Za-z0-9._-' '-') - if [ -z "$CACHE_KEY" ]; then - echo "::error::could not derive CACHE_KEY from refs (head_ref=$GITHUB_REF_RAW)" - exit 1 - fi - echo "Using CACHE_KEY=$CACHE_KEY" - . ./.envrc && earthly -P --strict \ - +test-pallet-fixtures --CACHE_KEY="$CACHE_KEY" - - - uses: ./.github/actions/tree-cache-guard/save - if: steps.guard.outputs.hit != 'true' - with: - key: ${{ steps.guard.outputs.key }} - # Gate job for toolkit E2E tests - if this fails, toolkit E2E tests are skipped test-toolkit: permissions: diff --git a/.github/workflows/nightly-build-check.yml b/.github/workflows/nightly-build-check.yml index da88ddf34..56a118ec4 100644 --- a/.github/workflows/nightly-build-check.yml +++ b/.github/workflows/nightly-build-check.yml @@ -42,4 +42,5 @@ jobs: echo "[net]" >> "$HOME"/.cargo/config echo "git-fetch-with-cli = true" >> "$HOME"/.cargo/config - . ./.envrc && earthly -P --ci --platform=linux/amd64 +node-image + # No --ci/--strict: +node-image -> +build -> +build-prepare -> +cache-probe (LOCALLY). + . ./.envrc && earthly -P --platform=linux/amd64 +node-image diff --git a/.github/workflows/seed-deps-cache.yml b/.github/workflows/seed-deps-cache.yml new file mode 100644 index 000000000..69f34bc28 --- /dev/null +++ b/.github/workflows/seed-deps-cache.yml @@ -0,0 +1,100 @@ +name: Seed deps cache + +# One-shot workflow_dispatch to force-cook and push the cargo-chef deps image +# to ghcr.io/midnightntwrk/midnight-node-deps. Run this once after the +# cook-build target or Cargo.toml changes so subsequent CI pulls the image +# from the registry instead of cooking locally. +# +# Why --no-cache: earthly silently skips SAVE IMAGE --push when it considers +# the target cached (even if buildkit layers re-ran). --no-cache forces +# earthly's own target-level cache bypass, ensuring the push fires. + +on: + workflow_dispatch: + +# no top-level permissions +permissions: + contents: read + +jobs: + seed-arm64: + name: Cook and push deps (arm64) + runs-on: ubuntu-latest-8-core-arm64 + timeout-minutes: 120 + permissions: + contents: read + env: + FORCE_COLOR: 1 + steps: + - uses: EarthBuild/actions-setup@cae2d9ab68894d8402751fe42e07c7cca0272f7f + with: + version: v0.8.16 + github-token: ${{ github.token }} + use-cache: false + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 + with: + submodules: true + + - name: Login to GHCR + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0 + with: + registry: ghcr.io + username: MidnightCI + password: ${{ secrets.MIDNIGHTCI_PACKAGES_WRITE }} + + - name: Cook and push deps + env: + EARTHLY_REMOTE_CACHE: "" + ACTIONS_CACHE_URL: "" + ACTIONS_RUNTIME_URL: "" + run: | + mkdir -p "$HOME/.cargo" + printf '[net]\ngit-fetch-with-cli = true\n' >> "$HOME/.cargo/config" + . ./.envrc + earthly --no-cache --push +seed-deps --ALLOW_CACHE_PUSH=true + + seed-amd64: + name: Cook and push deps (amd64) + runs-on: [self-hosted, "tier:large"] + timeout-minutes: 180 + permissions: + contents: read + env: + FORCE_COLOR: 1 + steps: + - uses: EarthBuild/actions-setup@cae2d9ab68894d8402751fe42e07c7cca0272f7f + with: + version: v0.8.16 + github-token: ${{ github.token }} + use-cache: false + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2 + with: + submodules: true + + - name: Isolate docker config (self-hosted shared HOME) + run: | + mkdir -p "$RUNNER_TEMP/.docker" + echo "DOCKER_CONFIG=$RUNNER_TEMP/.docker" >> "$GITHUB_ENV" + + - name: Login to GHCR + uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0 + with: + registry: ghcr.io + username: MidnightCI + password: ${{ secrets.MIDNIGHTCI_PACKAGES_WRITE }} + + - name: Cook and push deps + env: + TMPDIR: ${{ runner.temp }} + EARTHLY_REMOTE_CACHE: "" + ACTIONS_CACHE_URL: "" + ACTIONS_RUNTIME_URL: "" + run: | + mkdir -p "$HOME/.cargo" + printf '[net]\ngit-fetch-with-cli = true\n' >> "$HOME/.cargo/config" + . ./.envrc + earthly --no-cache --push +seed-deps --ALLOW_CACHE_PUSH=true diff --git a/Cargo.toml b/Cargo.toml index dfe459d54..a747603e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -367,6 +367,18 @@ sp-weights = { default-features = false, git = "https://github.com/paritytech/po [profile.release] panic = "unwind" +# file:line in backtraces for our own crates (node, toolkit, pallets, tests), +# without the size/cost of full (=2) debug info. Single source of truth: identical +# across every release consumer, so they share the cooked-dependency cache image. +debug = "line-tables-only" + +# ...but NOT for dependencies. The substrate/polkadot-sdk dep graph dominates debug +# size (~1 GB of .debug_str type-names alone); we rarely need file:line inside it. +# Stripping debug from deps keeps the node binary and the cooked-deps cache image +# lean while preserving line numbers in the frames we actually debug. Final binaries +# additionally get their debug sections zlib-compressed post-link (Earthfile +build). +[profile.release.package."*"] +debug = 0 [profile.production] inherits = "release" diff --git a/Earthfile b/Earthfile index 92ef2007f..12bcd2c29 100644 --- a/Earthfile +++ b/Earthfile @@ -14,6 +14,14 @@ VERSION 0.8 # the builtin resolves to the literal string `HEAD` across every PR. ARG --global CACHE_KEY=local +# Content-addressed cache of cooked Rust dependencies (cargo-chef). The deps image +# is named by hash(recipe.json · arch · rust-version · flavor); a cache miss cooks +# locally (+cook-), a hit is pulled. Pushing to the shared registry is OFF +# by default so untrusted (PR) builds can't poison the cache — only trusted CI runs +# on `main` flip ALLOW_CACHE_PUSH=true. See +cook-build / +build-prepare. +ARG --global DEPS_CACHE_REPO=ghcr.io/midnightntwrk/midnight-node-deps +ARG --global ALLOW_CACHE_PUSH=false + # ================ Local Targets START ================ # If you add a new one here, prefix it with "local-" # Add the target name to the doc string so it shows up @@ -140,7 +148,7 @@ subxt: # build-node-only builds only the midnight-node binary build-node-only: FROM +build-prepare - COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \ + COPY --dir Cargo.lock Cargo.toml docs .sqlx \ ledger node pallets primitives metadata res runtime util tests relay partner-chains . ARG NATIVEARCH @@ -148,7 +156,8 @@ build-node-only: RUN cargo auditable build -p midnight-node --locked --release RUN mkdir -p /artifacts-$NATIVEARCH \ - && mv /target/release/midnight-node /artifacts-$NATIVEARCH + && mv /target/release/midnight-node /artifacts-$NATIVEARCH \ + && objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/midnight-node" SAVE ARTIFACT /artifacts-$NATIVEARCH @@ -740,6 +749,24 @@ prep-no-copy: # FROM --platform=$NATIVEPLATFORM +node-ci-image-single-platform FROM midnightntwrk/midnight-node-ci:${RUST_VERSION}-${COMPACTC_VERSION}-$NATIVEARCH + # Inherited by every target built FROM +prep-no-copy (prep, build-prepare, + # check-rust-prepare, test, …) so all cargo invocations share these settings: + # - verbose output (equivalent to -v, and propagates into nested cargo builds + # driven by nextest/auditable/chef that a per-line -v would miss) + # - RUSTC_BOOTSTRAP=1 unlocks unstable cargo features on the stable toolchain + # - checksum-freshness: decide out-of-date by file hash, not mtime (unstable; + # requires the bootstrap above). Earthly's COPY (without --keep-ts) pins every + # file to a fixed constant mtime (2020-04-16T12:00:00Z) for reproducible layers, + # which defeats cargo's default mtime freshness check: re-copied content with the + # same constant mtime would be seen as unchanged. Hashing the bytes avoids that. + ENV CARGO_TERM_VERBOSE=true + ENV RUSTC_BOOTSTRAP=1 + ENV CARGO_UNSTABLE_CHECKSUM_FRESHNESS=true + # Off everywhere: incremental artifacts are large and embed host-specific + # absolute paths, which bloat and poison the cross-machine cooked-deps cache. + # (Release is already non-incremental; this also covers the dev/check flavor.) + ENV CARGO_INCREMENTAL=0 + # ca-certificates and curl-minimal already present in the CI base image RUN cargo --version @@ -749,7 +776,7 @@ prep-no-copy: prep: FROM +prep-no-copy - COPY --keep-ts --dir \ + COPY --dir \ Cargo.lock Cargo.toml .cargo .config .sqlx deny.toml docs \ ledger LICENSE node pallets primitives README.md res runtime \ metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION . @@ -825,21 +852,41 @@ planner: RUN cargo chef prepare --recipe-path recipe.json SAVE ARTIFACT recipe.json /recipe.json -check-rust-prepare: - # NOTE: This just uses recipe.json - no src files! +# cook-check pre-compiles deps for the check (clippy) flavor. Cooks the superset — +# clippy, all-targets (dev-deps), and the rb+try-runtime features check-rust lints — +# so check-rust fully reuses it and check-feature-unification rides on top, recompiling +# only its --no-dev-deps/default-features delta. +cook-check: FROM +prep-no-copy - # COPY +planner/recipe.json /recipe.json - CACHE --sharing shared --id cargo-git /usr/local/cargo/git - CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry + COPY +planner/recipe.json /recipe.json + RUN cargo chef cook --clippy --workspace --all-targets --features runtime-benchmarks,try-runtime --recipe-path /recipe.json + ARG DEPS_TAG + IF [ "$ALLOW_CACHE_PUSH" = "true" ] + SAVE IMAGE --push $DEPS_CACHE_REPO:$DEPS_TAG + END - # Build dependencies - this is the caching Docker layer! - # RUN SKIP_WASM_BUILD=1 cargo chef cook --clippy --workspace --all-targets --features runtime-benchmarks --recipe-path /recipe.json +check-rust-prepare: + # Resolve the check dependency cache (pull or cook), mirroring +build-prepare. + FROM +prep-no-copy + COPY +planner/recipe.json /recipe.json + COPY rust-toolchain.toml /rust-toolchain.toml + ARG NATIVEARCH + ARG RUST_VERSION=$(grep '^channel' /rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/') + ARG RECIPE_HASH=$(sha256sum /recipe.json | cut -c1-16) + ARG DEPS_TAG=${RUST_VERSION}-${NATIVEARCH}-check-${RECIPE_HASH} + COPY (+cache-probe/hit.txt --DEPS_TAG=$DEPS_TAG) /deps-hit.txt + ARG DEPS_HIT=$(cat /deps-hit.txt) + IF [ "$DEPS_HIT" = "true" ] + FROM $DEPS_CACHE_REPO:$DEPS_TAG + ELSE + FROM +cook-check --DEPS_TAG=$DEPS_TAG + END check-rust: FROM +check-rust-prepare CACHE --sharing shared --id cargo-git /usr/local/cargo/git CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry - COPY --keep-ts --dir \ + COPY --dir \ Cargo.lock Cargo.toml .config .sqlx deny.toml docs \ ledger LICENSE node pallets primitives README.md res runtime \ metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION . @@ -860,7 +907,7 @@ check-feature-unification: FROM +check-rust-prepare CACHE --sharing shared --id cargo-git /usr/local/cargo/git CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry - COPY --keep-ts --dir \ + COPY --dir \ Cargo.lock Cargo.toml .config .sqlx deny.toml docs \ ledger LICENSE node pallets primitives README.md res runtime \ metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION . @@ -895,24 +942,31 @@ check: # Core tests - excludes Midnight Node Toolkit (requires Node Toolkit (JS) npm packages from midnight-js) test: ARG NATIVEARCH - FROM +prep + # Consume the release deps cache (cooked once, shared with +build). No CACHE /target + # mount here: it would shadow the cooked /target baked into the build image. + FROM +build-prepare CACHE --sharing shared --id cargo-git /usr/local/cargo/git CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry - # See top-of-file CACHE_KEY ARG for why this is scoped. - CACHE --id target-${CACHE_KEY} /target + COPY --dir Cargo.lock Cargo.toml .cargo .config .sqlx deny.toml docs \ + ledger LICENSE node pallets primitives README.md res runtime \ + metadata rustfmt.toml util tests relay partner-chains COMPACTC_VERSION . # Test RUN mkdir /test-artifacts - # Note: debug and opt-level=1 OOM the linker (>24GB) due to large test binaries - ENV RUSTFLAGS="-C target-cpu=native -C opt-level=2 -C debuginfo=1" + # No RUSTFLAGS override: release profile (opt-3) inherited — keeps test binaries + # small enough to link (debug/opt-1 OOM'd the linker at >24GB), and debuginfo is + # line-tables via [profile.release] in Cargo.toml. target-cpu=native is gone (it + # made artifacts non-portable across the machines now sharing this image). COPY .envrc ./bin/.envrc COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static # Run all tests EXCEPT: # - Midnight Node Toolkit (depends on Node Toolkit (JS) npm packages from midnight-js) - # - pallet-midnight fixture tests (depend on .mn files that need regenerating with Midnight Node Toolkit) # - partner-chains-cardano-offchain are: 1) flaky, 2) long running, 3) test in partner-chains repo, 4) cover functionality used to e2e test partner-chains (non-production) + # The pallet-midnight fixture tests (test_get_contract_state / test_send_mn_transaction + # / test_validation_works) run here too — they share this exact release build, so a + # separate job just paid for a second cold compile of the same artifacts. # DOCKERHUB_USER/TOKEN default to empty so local builds and fork PRs (where secrets # aren't exposed) still work — at the cost of unauthenticated pull rate limits. WITH DOCKER @@ -922,8 +976,7 @@ test: fi && \ MIDNIGHT_LEDGER_EXPERIMENTAL=1 cargo nextest r --profile ci --release --workspace --locked \ --exclude midnight-node-toolkit \ - --exclude partner-chains-cardano-offchain \ - -E 'not (test(/^tests::test_get_contract_state$/) | test(/^tests::test_send_mn_transaction$/) | test(/^tests::test_validation_works$/))' + --exclude partner-chains-cardano-offchain END # RUN MIDNIGHT_LEDGER_EXPERIMENTAL=1 cargo llvm-cov nextest --profile ci --release --workspace --locked \ @@ -935,34 +988,6 @@ test: # AS /target is a temp cache, copy the results to /test-artifacts, otherwise earthly won't find them later # SAVE ARTIFACT --if-exists ./test-artifacts-$NATIVEARCH AS LOCAL ./test-artifacts -# Pallet fixture tests - runs pallet-midnight tests that depend on regenerated .mn fixtures -# These tests do NOT require toolkit-js -test-pallet-fixtures: - ARG NATIVEARCH - FROM +prep - CACHE --sharing shared --id cargo-git /usr/local/cargo/git - CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry - # See top-of-file CACHE_KEY ARG for why this is scoped. - CACHE --id target-${CACHE_KEY} /target - - # These tests use a mock runtime (MockBlock), not the real WASM runtime. - # Debug mode skips LLVM optimization passes, compiling faster than release on free CI runners. - ENV SKIP_WASM_BUILD=1 - ENV RUSTFLAGS="-C debuginfo=1" - COPY .envrc ./bin/.envrc - COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree - ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static - - # Run pallet-midnight fixture tests in debug mode (compiles much faster) - WITH DOCKER - RUN MIDNIGHT_LEDGER_EXPERIMENTAL=1 cargo nextest r --profile ci --locked \ - -E 'test(/^tests::test_get_contract_state$/) | test(/^tests::test_send_mn_transaction$/) | test(/^tests::test_validation_works$/)' - END - # RUN cargo llvm-cov report --html --release --output-dir /test-artifacts-pallet-fixtures-$NATIVEARCH/html - # RUN cargo llvm-cov report --lcov --release --output-path /test-artifacts-pallet-fixtures-$NATIVEARCH/tests.lcov - - # SAVE ARTIFACT ./test-artifacts-pallet-fixtures-$NATIVEARCH AS LOCAL ./test-artifacts-pallet-fixtures - # Midnight Node Toolkit tests - requires Node Toolkit (JS) which depends on midnight-js npm packages build-test-toolkit: ARG NATIVEARCH @@ -993,7 +1018,9 @@ build-test-toolkit: # Test RUN mkdir /test-artifacts-toolkit # Compile the tests to go as fast as possible on this machine: - ENV RUSTFLAGS="-C target-cpu=native -C debuginfo=1" + # target-cpu=native removed: non-portable codegen under a fixed fingerprint is + # unsound once build artifacts are shared across machines. + ENV RUSTFLAGS="-C debuginfo=1" COPY .envrc ./bin/.envrc COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static @@ -1048,23 +1075,80 @@ test-toolkit: END SAVE ARTIFACT /artifacts AS LOCAL ./test-artifacts-toolkit +# cache-probe (LOCALLY): does the named deps image already exist in the registry? +# Runs on the host so it uses host docker + registry creds. Emits hit.txt +# ("true"/"false"). Generic over flavor — the caller passes the full DEPS_TAG. +# Not logged in / no creds → inspect fails → "false" → the caller cooks locally. +cache-probe: + LOCALLY + ARG DEPS_TAG + RUN docker manifest inspect "$DEPS_CACHE_REPO:$DEPS_TAG" >/dev/null 2>&1 \ + && echo true > "/tmp/deps-hit-$DEPS_TAG" \ + || echo false > "/tmp/deps-hit-$DEPS_TAG" + SAVE ARTIFACT "/tmp/deps-hit-$DEPS_TAG" hit.txt + +# cook-build pre-compiles every third-party dependency for the release (build) +# flavor into a content-addressed image. The codegen env here is baked into the +# image, so both the pull and the cook path expose identical CC/CXX to the +# downstream workspace build. Consumed by +build-prepare on a cache miss. +cook-build: + FROM +prep-no-copy + ENV CC=clang + ENV CXX=clang++ + ENV CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true + COPY +planner/recipe.json /recipe.json + # Deps inherit [profile.release] (opt-3, line-tables) from chef's reconstructed + # skeleton — matching what +build compiles the workspace crates with, so cargo + # reuses them instead of rebuilding. + RUN cargo chef cook --release --recipe-path /recipe.json + ARG DEPS_TAG + IF [ "$ALLOW_CACHE_PUSH" = "true" ] + SAVE IMAGE --push $DEPS_CACHE_REPO:$DEPS_TAG + END + +# seed-deps computes DEPS_TAG and force-pushes the build-flavor cook image. +# Used by the seed-deps-cache workflow_dispatch; not part of normal CI. +# earthly --no-cache --push +seed-deps --ALLOW_CACHE_PUSH=true +seed-deps: + FROM +prep-no-copy + COPY +planner/recipe.json /recipe.json + COPY rust-toolchain.toml /rust-toolchain.toml + ARG NATIVEARCH + ARG RUST_VERSION=$(grep '^channel' /rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/') + ARG RECIPE_HASH=$(sha256sum /recipe.json | cut -c1-16) + ARG DEPS_TAG=${RUST_VERSION}-${NATIVEARCH}-build-${RECIPE_HASH} + BUILD +cook-build --DEPS_TAG=$DEPS_TAG + build-prepare: - # NOTE: This just uses recipe.json - no src files! + # Resolve the release dependency cache: pull the pre-cooked image if it exists, + # else cook it locally (+cook-build). The cook is the expensive layer; this + # shares it across machines/CI runs. recipe.json + rust-toolchain.toml are used + # only to compute the tag — discarded when we re-FROM the chosen base below. FROM +prep-no-copy - # TODO: re-enable when chef is improved. - # COPY +planner/recipe.json /recipe.json - # CACHE --sharing shared --id cargo-git /usr/local/cargo/git - # CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry + COPY +planner/recipe.json /recipe.json + COPY rust-toolchain.toml /rust-toolchain.toml + ARG NATIVEARCH + ARG RUST_VERSION=$(grep '^channel' /rust-toolchain.toml | sed 's/.*"\(.*\)".*/\1/') + ARG RECIPE_HASH=$(sha256sum /recipe.json | cut -c1-16) + ARG DEPS_TAG=${RUST_VERSION}-${NATIVEARCH}-build-${RECIPE_HASH} + COPY (+cache-probe/hit.txt --DEPS_TAG=$DEPS_TAG) /deps-hit.txt + ARG DEPS_HIT=$(cat /deps-hit.txt) + IF [ "$DEPS_HIT" = "true" ] + FROM $DEPS_CACHE_REPO:$DEPS_TAG + ELSE + # BUILD (not just FROM) so +cook-build's `SAVE IMAGE --push` actually fires. + # Earthly only emits SAVE IMAGE for a target reached via BUILD / as an output, + # never for one used solely as a FROM base — so without this the cooked deps + # image was never published. Pushes only under `earthly --push` (i.e. +images), + # so PR check/test jobs still cook-without-publishing. + BUILD +cook-build --DEPS_TAG=$DEPS_TAG + FROM +cook-build --DEPS_TAG=$DEPS_TAG + END + # Per-commit, affects only the node crate's build script — kept out of the cook + # so it doesn't bust the deps cache every commit. ARG EARTHLY_GIT_SHORT_HASH ENV SUBSTRATE_CLI_GIT_COMMIT_HASH=$EARTHLY_GIT_SHORT_HASH - ENV CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true - ENV CC=clang - ENV CXX=clang++ - - # Build dependencies - this is the caching Docker layer! - # TODO: re-enable when chef is improved. - # RUN SKIP_WASM_BUILD=1 cargo chef cook --release --workspace --all-targets --recipe-path /recipe.json # build creates production ready binaries build: @@ -1072,7 +1156,7 @@ build: # CACHE --sharing shared --id cargo-git /usr/local/cargo/git # CACHE --sharing shared --id cargo-reg /usr/local/cargo/registry # CACHE /target - COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \ + COPY --dir Cargo.lock Cargo.toml docs .sqlx \ ledger node pallets primitives metadata res runtime util tests relay partner-chains COMPACTC_VERSION . ARG NATIVEARCH @@ -1096,11 +1180,18 @@ build: && mv /target/release/aiken-deployer /artifacts-$NATIVEARCH \ && cp /target/release/wbuild/midnight-node-runtime/*.wasm /artifacts-$NATIVEARCH/midnight-node-runtime/ + # zlib-compress the (our-code-only) line-tables debug in the shipped ELF binaries: + # ~1/3 the size, file:line backtraces preserved (verified — runtime panic traces + # resolve from SHF_COMPRESSED sections). Post-link file op, no build-cache impact. + RUN for b in midnight-node midnight-node-toolkit aiken-deployer; do \ + objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/$b"; \ + done + SAVE ARTIFACT /artifacts-$NATIVEARCH AS LOCAL artifacts build-benchmarks: FROM +build-prepare - COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \ + COPY --dir Cargo.lock Cargo.toml docs .sqlx \ ledger node pallets primitives metadata relay res runtime util tests partner-chains . ARG NATIVEARCH @@ -1110,7 +1201,8 @@ build-benchmarks: cargo auditable build --workspace --locked --release --features runtime-benchmarks RUN mkdir -p /artifacts-$NATIVEARCH \ - && mv /target/release/midnight-node /artifacts-$NATIVEARCH/midnight-node-benchmarks + && mv /target/release/midnight-node /artifacts-$NATIVEARCH/midnight-node-benchmarks \ + && objcopy --compress-debug-sections=zlib "/artifacts-$NATIVEARCH/midnight-node-benchmarks" SAVE ARTIFACT /artifacts-$NATIVEARCH AS LOCAL artifacts-benchmarks @@ -1512,7 +1604,7 @@ testnet-sync-e2e: # local-env-e2e executes any tests that depend on a running local-env local-env-e2e: FROM +prep - COPY --keep-ts --dir Cargo.lock Cargo.toml docs .sqlx \ + COPY --dir Cargo.lock Cargo.toml docs .sqlx \ ledger node pallets primitives metadata res runtime util tests relay partner-chains local-environment scripts . COPY static/contracts/simple-merkle-tree /test-static/simple-merkle-tree ENV MIDNIGHT_LEDGER_TEST_STATIC_DIR=/test-static diff --git a/changes/node/changed/line-table-backtraces-and-dependency-cache.md b/changes/node/changed/line-table-backtraces-and-dependency-cache.md new file mode 100644 index 000000000..6708d417d --- /dev/null +++ b/changes/node/changed/line-table-backtraces-and-dependency-cache.md @@ -0,0 +1,28 @@ +#node #toolkit +# Line-number panic backtraces, and a cargo-chef dependency cache + +The `midnight-node` and `midnight-node-toolkit` release binaries now carry +`line-tables-only` debug info, so panic backtraces resolve to `file:line` +for our own crates (`RUST_BACKTRACE=1` still required at runtime). +Dependencies are built with no debug info +(`[profile.release.package."*"] debug = 0`) and the binaries' debug +sections are zlib-compressed after linking, keeping the cost modest: +`midnight-node` is ~331 MB, versus ~181 MB with no debug and ~1.9 GB if +debug were left on across the whole dependency graph. Backtrace frames +inside dependencies show addresses rather than line numbers — the +deliberate trade for size. + +The Earthfile re-enables cargo-chef dependency caching as a +content-addressed image, named by a hash of the chef recipe, target +architecture, Rust version, and build flavor. A cache miss cooks +dependencies locally; a hit is pulled. Pushing to the shared registry is +gated behind `ALLOW_CACHE_PUSH`, which defaults to off so untrusted PR +builds cannot poison the cache — only trusted CI on `main` pushes. The +`check` (clippy) and `build` (release) flavors get separate images +because their cargo flags differ; `test`, `benchmarks`, and the +pallet-fixture tests share the `build` image. The previously test-only +`target-cpu=native` flag was removed: non-portable codegen under a fixed +fingerprint is unsound once artifacts are shared across machines. + +PR: https://github.com/midnightntwrk/midnight-node/pull/1645 +Issue: