fix(ci): run docker-dependent tests in the CI image via docker run

This commit is contained in:
Daniil Polyakov 2026-07-15 22:19:15 +03:00
parent 6dce78dd8b
commit e081205d95
4 changed files with 148 additions and 59 deletions

View File

@ -1,10 +0,0 @@
name: Install risc0
description: Installs risc0 in the environment
runs:
using: "composite"
steps:
- name: Install risc0
run: |
curl -L https://risczero.com/install | bash
/home/runner/.risc0/bin/rzup install
shell: bash

View File

@ -0,0 +1,54 @@
name: Run in CI image
description: >
Runs a command inside the CI image on a plain runner, for jobs that need the
host's Docker daemon. `container:` cannot work for those: it puts the
workspace on a path the host daemon cannot resolve, and it forbids
`--network host`.
inputs:
image:
description: CI image reference, from the ci-image workflow's output.
required: true
run:
description: Command to run inside the image.
required: true
env:
description: Newline-separated NAME=VALUE pairs to pass into the container.
required: false
default: ""
runs:
using: "composite"
steps:
- name: Run in CI image
shell: bash
env:
INPUT_IMAGE: ${{ inputs.image }}
INPUT_RUN: ${{ inputs.run }}
INPUT_ENV: ${{ inputs.env }}
run: |
set -euo pipefail
env_args=()
while IFS= read -r pair; do
[[ -z "$pair" ]] && continue
env_args+=(--env "$pair")
done <<< "$INPUT_ENV"
# Cargo's registry is the only part of CARGO_HOME worth sharing with the
# host: mounting all of it would shadow the tools baked into the image.
mkdir -p "$HOME/.cargo/registry"
# Mounting the workspace on its own path is what makes this work. The
# daemon resolves compose bind mounts against the host, and the test
# binaries bake CARGO_MANIFEST_DIR in at compile time, so the path has
# to mean the same thing inside and outside the container.
docker run --rm \
--network host \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$PWD:$PWD" \
--volume "$HOME/.cargo/registry:/usr/local/cargo/registry" \
--workdir "$PWD" \
"${env_args[@]}" \
"$INPUT_IMAGE" \
bash -o pipefail -c "$INPUT_RUN"

View File

@ -47,6 +47,13 @@ RUN curl -L https://risczero.com/install | bash \
&& rm -rf /root/.risc0 \
&& rzup install
# Copying docker. Static Go binaries, so the alpine-built ones run fine here.
COPY --from=docker:29-cli /usr/local/bin/docker /usr/local/bin/docker
COPY --from=docker:29-cli /usr/local/libexec/docker/cli-plugins/docker-compose \
/usr/local/libexec/docker/cli-plugins/docker-compose
COPY --from=docker:29-cli /usr/local/libexec/docker/cli-plugins/docker-buildx \
/usr/local/libexec/docker/cli-plugins/docker-buildx
# Prebuilt binaries; compiling these from source would dominate the image build.
RUN curl -L --proto '=https' --tlsv1.2 -sSf \
https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash \
@ -70,4 +77,7 @@ RUN cargo --version \
&& taplo --version \
&& cargo machete --version \
&& cargo deny --version \
&& just --version
&& just --version \
&& docker --version \
&& docker compose version \
&& docker buildx version

View File

@ -150,20 +150,24 @@ jobs:
RUST_LOG: "info"
run: cargo nextest run --workspace --exclude integration_tests --exclude test_fixtures --all-features
# Not a `container:` job: the tests drive the host's Docker daemon through
# testcontainers, so they run in the CI image via `run-in-ci-image` instead.
test-fixtures-tests:
needs: ci-image
runs-on: ubuntu-latest
container:
image: ${{ needs.ci-image.outputs.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
@ -171,19 +175,20 @@ jobs:
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Run test_fixtures tests
env:
RISC0_DEV_MODE: "1"
RUST_LOG: "info"
run: cargo nextest run -p test_fixtures
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
env: |
RISC0_DEV_MODE=1
RUST_LOG=info
run: cargo nextest run -p test_fixtures
# Not a `container:` job: the test binaries bake CARGO_MANIFEST_DIR in at
# compile time, so the archive has to be built on the same path the matrix
# jobs later run it from.
integration-tests-prebuild:
needs: ci-image
runs-on: ubuntu-latest
container:
image: ${{ needs.ci-image.outputs.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
outputs:
targets: ${{ steps.discover-targets.outputs.targets }}
steps:
@ -191,6 +196,13 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
@ -198,9 +210,11 @@ jobs:
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Build integration test archive
env:
RISC0_DEV_MODE: "1"
run: cargo nextest archive -p integration_tests --archive-file integration-tests.tar.zst --no-pager
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
env: RISC0_DEV_MODE=1
run: cargo nextest archive -p integration_tests --archive-file integration-tests.tar.zst --no-pager
- name: Upload integration test archive
uses: actions/upload-artifact@v4
@ -208,15 +222,22 @@ jobs:
name: integration-tests-archive
path: integration-tests.tar.zst
- name: List integration test binaries
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
run: |
cargo nextest list \
--archive-file integration-tests.tar.zst \
--list-type binaries-only \
--message-format json \
--no-pager > integration-tests-binaries.json
# $GITHUB_OUTPUT is outside the mounted workspace, so the container cannot
# write to it. jq is on the runner, so do this pass here.
- name: Discover integration test targets from archive
id: discover-targets
run: |
cargo nextest list \
--archive-file integration-tests.tar.zst \
--list-type binaries-only \
--message-format json \
--no-pager > integration-tests-binaries.json
targets_json="$(jq -c '[."rust-binaries" | to_entries[] | select(.value.kind == "test" and .value."binary-name" != "tps") | .value."binary-name"] | sort | unique' integration-tests-binaries.json)"
if [[ "$targets_json" == "[]" ]]; then
@ -230,11 +251,6 @@ jobs:
integration-tests:
needs: [ci-image, test-fixtures-tests, integration-tests-prebuild]
runs-on: ubuntu-latest
container:
image: ${{ needs.ci-image.outputs.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
timeout-minutes: 90
strategy:
fail-fast: false
@ -246,31 +262,43 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Download integration test archive
uses: actions/download-artifact@v4
with:
name: integration-tests-archive
- name: Run tests
env:
RISC0_DEV_MODE: "1"
RUST_LOG: "info"
run: cargo nextest run --archive-file integration-tests.tar.zst -E "binary(${{ matrix.target }})"
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
env: |
RISC0_DEV_MODE=1
RUST_LOG=info
run: cargo nextest run --archive-file integration-tests.tar.zst -E "binary(${{ matrix.target }})"
valid-proof-test:
needs: ci-image
runs-on: ubuntu-latest
container:
image: ${{ needs.ci-image.outputs.image }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
timeout-minutes: 90
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
with:
@ -278,14 +306,16 @@ jobs:
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Test valid proof
env:
RUST_LOG: "info"
run: cargo test -p integration_tests -- --exact private::private_transfer_to_owned_account
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
env: RUST_LOG=info
run: cargo test -p integration_tests -- --exact private::private_transfer_to_owned_account
# Not containerised: `just build-artifacts` shells out to `cargo risczero
# build`, which runs the guest build in Docker and bind-mounts the workspace.
# From inside a container job the workspace path wouldn't match the host's.
# `just build-artifacts` drives the host's Docker daemon via `cargo risczero
# build`, so it goes through the image rather than `container:`.
artifacts:
needs: ci-image
runs-on: ubuntu-latest
timeout-minutes: 60
@ -295,7 +325,12 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
- uses: ./.github/actions/install-risc0
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Restore Rust cache
uses: Swatinem/rust-cache@v2
@ -303,11 +338,11 @@ jobs:
shared-key: ci-rust-cache
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- name: Install just
run: cargo install --locked just
- name: Build artifacts
run: just build-artifacts
uses: ./.github/actions/run-in-ci-image
with:
image: ${{ needs.ci-image.outputs.image }}
run: just build-artifacts
- name: Check if artifacts match repository
run: |