# This workflow performs unit tests across different operating systems and Rust versions. It includes steps for: # - Testing on Ubuntu and macOS with stable and beta Rust toolchains. # - Installing Zebra from the lockfile without cache on Ubuntu. # - Verifying that Cargo.lock is up-to-date with Cargo.toml changes. # - Running cargo-deny checks for dependencies. # - Checking for unused dependencies in the code. name: Multi-OS Unit Tests # Ensures that only one workflow task will run at a time. Previous builds, if # already in process, will get cancelled. Only the latest commit will be allowed # to run, cancelling any workflows in between concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true on: workflow_dispatch: pull_request: paths: # code and tests - '**/*.rs' # hard-coded checkpoints and proptest regressions - '**/*.txt' # test data snapshots - '**/*.snap' # dependencies - '**/Cargo.toml' - '**/Cargo.lock' - '**/deny.toml' # configuration files - '.cargo/config.toml' - '**/clippy.toml' # workflow definitions - '.github/workflows/ci-unit-tests-os.yml' # we build Rust caches on main, # so they can be shared by all branches: # https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache push: branches: - main paths: # production code and test code - '**/*.rs' # hard-coded checkpoints # TODO: skip proptest regressions? - '**/*.txt' # test data snapshots - '**/*.snap' # dependencies - '**/Cargo.toml' - '**/Cargo.lock' - '**/deny.toml' # configuration files - '.cargo/config.toml' - '**/clippy.toml' # workflow definitions - '.github/workflows/ci-unit-tests-os.yml' env: CARGO_INCREMENTAL: ${{ vars.CARGO_INCREMENTAL }} RUST_LOG: ${{ vars.RUST_LOG }} RUST_BACKTRACE: ${{ vars.RUST_BACKTRACE }} RUST_LIB_BACKTRACE: ${{ vars.RUST_LIB_BACKTRACE }} COLORBT_SHOW_HIDDEN: ${{ vars.COLORBT_SHOW_HIDDEN }} jobs: ######################################## ### Build and test Zebra on all OSes ### ######################################## test: name: Test ${{ matrix.rust }} on ${{ matrix.os }}${{ matrix.features }} # The large timeout is to accommodate: # - macOS and Windows builds (typically 50-90 minutes), and timeout-minutes: 120 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # TODO: Windows was removed for now, see https://github.com/ZcashFoundation/zebra/issues/3801 os: [ubuntu-latest, macos-latest] rust: [stable, beta] # TODO: When vars.EXPERIMENTAL_FEATURES has features in it, add it here. # Or work out a way to trim the space from the variable: GitHub doesn't allow empty variables. # Or use `default` for the empty feature set and EXPERIMENTAL_FEATURES, and update the branch protection rules. #features: ${{ fromJSON(format('["", "{0}"]', vars.EXPERIMENTAL_FEATURES)) }} features: [""] exclude: # We're excluding macOS beta for the following reasons: # - the concurrent macOS runner limit is much lower than the Linux limit # - macOS is slower than Linux, and shouldn't have a build or test difference with Linux # - macOS is a second-tier Zebra support platform - os: macos-latest rust: beta steps: - uses: actions/checkout@v4.1.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.4.0 - name: Install last version of Protoc uses: arduino/setup-protoc@v3.0.0 with: # TODO: increase to latest version after https://github.com/arduino/setup-protoc/issues/33 is fixed version: '23.x' repo-token: ${{ secrets.GITHUB_TOKEN }} # Setup Rust with ${{ matrix.rust }} toolchain and minimal profile - name: Setup Rust run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=${{ matrix.rust }} --profile=minimal - uses: Swatinem/rust-cache@v2.7.3 # TODO: change Rust cache target directory on Windows, # or remove this workaround once the build is more efficient (#3005). #with: # workspaces: ". -> C:\\zebra-target" with: # Split the experimental features cache from the regular cache, to avoid linker errors. # (These might be "disk full" errors, or they might be dependency resolution issues.) key: ${{ matrix.features }} - name: Change target output directory on Windows # Windows doesn't have enough space on the D: drive, so we redirect the build output to the # larger C: drive. # TODO: Remove this workaround once the build is more efficient (#3005). if: matrix.os == 'windows-latest' run: | mkdir "C:\\zebra-target" echo "CARGO_TARGET_DIR=C:\\zebra-target" | Out-File -FilePath "$env:GITHUB_ENV" -Encoding utf8 -Append - name: cargo fetch run: | cargo fetch - name: Install LLVM on Windows if: matrix.os == 'windows-latest' run: | choco install llvm -y echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append echo "LIBCLANG_PATH=C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Skip network tests on Ubuntu and Windows # Ubuntu runners don't have reliable network or DNS during test steps. # Windows runners have an unreliable network. shell: bash if: matrix.os != 'macos-latest' run: echo "ZEBRA_SKIP_NETWORK_TESTS=1" >> $GITHUB_ENV - name: Minimise proptest cases on macOS and Windows # We set cases to 1, because some tests already run 1 case by default. # We keep maximum shrink iterations at the default value, because it only happens on failure. # # Windows compilation and tests are slower than other platforms. # macOS runners do extra network tests, so they take longer. shell: bash if: matrix.os != 'ubuntu-latest' run: | echo "PROPTEST_CASES=1" >> $GITHUB_ENV echo "PROPTEST_MAX_SHRINK_ITERS=1024" >> $GITHUB_ENV # Run unit and basic acceptance tests, only showing command output if the test fails. # # If some tests hang, add "-- --nocapture" for just that test, or for all the tests. - name: Run tests${{ matrix.features }} run: | cargo test --features "${{ matrix.features }}" --release --verbose --workspace # Explicitly run any tests that are usually #[ignored] - name: Run zebrad large sync tests${{ matrix.features }} # Skip the entire step on Ubuntu and Windows, because the test would be skipped anyway due to ZEBRA_SKIP_NETWORK_TESTS if: matrix.os == 'macos-latest' run: | cargo test --features "${{ matrix.features }}" --release --verbose --package zebrad --test acceptance -- --nocapture --include-ignored sync_large_checkpoints_ # Install Zebra with lockfile dependencies, with no caching and default features install-from-lockfile-no-cache: name: Install zebrad from lockfile without cache on ubuntu-latest timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.1.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.4.0 # Setup Rust with stable toolchain and minimal profile - name: Setup Rust run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal - name: Install zebrad run: | cargo install --locked --path ./zebrad/ zebrad # Check that Cargo.lock includes any Cargo.toml changes. # This check makes sure the `cargo-deny` crate dependency checks are accurate. check-cargo-lock: name: Check Cargo.lock is up to date timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.1.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.4.0 - name: Install last version of Protoc uses: arduino/setup-protoc@v3.0.0 with: version: '23.x' repo-token: ${{ secrets.GITHUB_TOKEN }} # Setup Rust with stable toolchain and minimal profile - name: Setup Rust run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal - uses: Swatinem/rust-cache@v2.7.3 with: shared-key: "clippy-cargo-lock" - name: Check Cargo.lock is up to date run: | cargo check --locked --all-features --all-targets cargo-deny: name: Check deny.toml ${{ matrix.checks }} ${{ matrix.features }} runs-on: ubuntu-latest strategy: matrix: checks: - bans - sources # We don't need to check `--no-default-features` here, because (except in very rare cases): # - disabling features isn't going to add duplicate dependencies # - disabling features isn't going to add more crate sources features: ['', '--features default-release-binaries', '--all-features'] # Always run the --all-features job, to get accurate "skip tree root was not found" warnings fail-fast: false # Prevent sudden announcement of a new advisory from failing ci: continue-on-error: ${{ matrix.checks == 'advisories' }} steps: - uses: actions/checkout@v4.1.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.4.0 - name: Check ${{ matrix.checks }} with features ${{ matrix.features }} uses: EmbarkStudios/cargo-deny-action@v1 with: # --all-features spuriously activates openssl, but we want to ban that dependency in # all of zebrad's production features for security reasons. But the --all-features job is # the only job that gives accurate "skip tree root was not found" warnings. # In other jobs, we expect some of these warnings, due to disabled features. command: check ${{ matrix.checks }} ${{ matrix.features == '--all-features' && '--allow banned' || '--allow unmatched-skip-root' }} arguments: --workspace ${{ matrix.features }} unused-deps: name: Check for unused dependencies runs-on: ubuntu-latest steps: - name: Checkout git repository uses: actions/checkout@v4.1.2 with: persist-credentials: false - uses: r7kamura/rust-problem-matchers@v1.4.0 # Setup Rust with stable toolchain and minimal profile - name: Setup Rust run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain=stable --profile=minimal - name: Install cargo-machete uses: baptiste0928/cargo-install@v3.0.1 with: crate: cargo-machete - name: Check unused dependencies # Exclude macro and transitive dependencies by filtering them out of the output, # then if there are any more unused dependencies, fail the job. run: | echo "-- full cargo machete output, including ignored dependencies --" cargo machete --skip-target-dir || true echo "-- unused dependencies are below this line, full output is above --" if cargo machete --skip-target-dir 2>/dev/null | \ grep --extended-regexp -e '^\\t' | \ grep -v -e gumdrop -e humantime-serde -e tinyvec -e zebra-utils; then echo "New unused dependencies were found, please remove them!" exit 1 else echo "No unused dependencies found." fi failure-issue: name: Open or update issues for OS integration failures # When a new job is added to this workflow, add it to this list. needs: [ test, install-from-lockfile-no-cache, check-cargo-lock, cargo-deny, unused-deps ] # Only open tickets for failed or cancelled jobs that are not coming from PRs. # (PR statuses are already reported in the PR jobs list, and checked by Mergify.) if: (failure() && github.event.pull_request == null) || (cancelled() && github.event.pull_request == null) runs-on: ubuntu-latest steps: - uses: jayqi/failed-build-issue-action@v1 with: title-template: "{{refname}} branch CI failed: {{eventName}} in {{workflow}}" # New failures open an issue with this label. label-name: S-ci-fail-os-integration-auto-issue # If there is already an open issue with this label, any failures become comments on that issue. always-create-new-issue: false github-token: ${{ secrets.GITHUB_TOKEN }}