RISC-V no-panic (part 2) #2176
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Rust | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: rust-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # Not needed in CI, should make things a bit faster | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_TERM_COLOR: always | |
| CARGO_BUILD_WARNINGS: deny | |
| # Build smaller artifacts to avoid running out of space in CI and make it a bit faster. | |
| # TODO: `-Znext-solver=globally` is a requirement for `generic_const_args`, `-Zmin-recursion-limit=256` is in turn | |
| # needed for some crates due to `-Znext-solver=globally` | |
| RUSTFLAGS: -Znext-solver=globally -Zmin-recursion-limit=256 -C strip=symbols | |
| # Needed for things like file system access | |
| # TODO: `-Znext-solver=globally` is a requirement for `generic_const_args`, `-Zmin-recursion-limit=256` is in turn | |
| # needed for some crates due to `-Znext-solver=globally` | |
| MIRIFLAGS: -Znext-solver=globally -Zmin-recursion-limit=256 -Zmiri-disable-isolation | |
| RUST_BACKTRACE: full | |
| jobs: | |
| cargo-fmt: | |
| runs-on: ubuntu-26.04 | |
| # Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead | |
| needs: act4 | |
| steps: | |
| - &checkout-step | |
| name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0 | |
| - &cache-restore-step | |
| name: Restore cache | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0 | |
| with: | |
| path: &cache-path | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| ${{ env.RUST_GPU_CACHE }} | |
| key: &cache-key ${{ runner.os }}-${{ runner.arch }}-cargo-${{ hashFiles('**/Cargo.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-${{ runner.arch }}-cargo- | |
| - name: cargo fmt | |
| run: cargo fmt --all -- --check | |
| cargo-clippy: | |
| strategy: | |
| matrix: | |
| os: | |
| - ubuntu-26.04 | |
| - ubuntu-26.04-arm | |
| - macos-26 | |
| - windows-11-vs2026-arm | |
| - windows-2025 | |
| type: | |
| - together | |
| - individually | |
| - features | |
| target: | |
| - "" | |
| include: | |
| - os: ubuntu-26.04 | |
| type: together | |
| target: riscv64gc-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: features | |
| target: riscv64gc-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: individually | |
| target: riscv64gc-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: together | |
| target: riscv64a23-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: features | |
| target: riscv64a23-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: individually | |
| target: riscv64a23-unknown-linux-gnu | |
| fail-fast: false | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| # TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally` | |
| command: >- | |
| cargo -Zgitoxide -Zgit clippy | |
| ${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }} | |
| -Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]' | |
| # Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead | |
| needs: act4 | |
| steps: | |
| - &rust-gpu-cache | |
| name: Set rust-gpu cache path | |
| shell: bash | |
| run: | | |
| if [[ "$RUNNER_OS" == "macOS" ]]; then | |
| echo "RUST_GPU_CACHE=$HOME/Library/Caches/rust-gpu" >> "$GITHUB_ENV" | |
| elif [[ "$RUNNER_OS" == "Windows" ]]; then | |
| echo "RUST_GPU_CACHE=$LOCALAPPDATA/rust-gpu" >> "$GITHUB_ENV" | |
| else | |
| echo "RUST_GPU_CACHE=$HOME/.cache/rust-gpu" >> "$GITHUB_ENV" | |
| fi | |
| - &shared-test-steps | |
| parallel: | |
| - *checkout-step | |
| - *cache-restore-step | |
| - name: RISC-V support | |
| run: | | |
| echo "command=${command} -Zbuild-std" >> $GITHUB_ENV | |
| echo "RUSTFLAGS=${RUSTFLAGS} -C linker=riscv64-linux-gnu-gcc" >> $GITHUB_ENV | |
| # TODO: Temporary workaround for https://github.com/rust-lang/cc-rs/issues/1654 | |
| echo "CC_riscv64_unknown_none_abundance=riscv64-linux-gnu-gcc" >> $GITHUB_ENV | |
| echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV | |
| sudo apt-get update | |
| sudo apt-get install --no-install-recommends --yes g++-riscv64-linux-gnu | |
| if: contains(matrix.target, 'riscv64') | |
| - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 | |
| if: runner.os == 'Linux' && matrix.target == '' | |
| # Use Mold linker for shorter build times | |
| - name: Use Mold linker on Linux | |
| shell: bash | |
| run: | | |
| echo "RUSTFLAGS=${RUSTFLAGS} -C link-arg=-fuse-ld=mold" >> $GITHUB_ENV | |
| if: runner.os == 'Linux' && matrix.target == '' | |
| # Use LLD linker for shorter build times | |
| - name: Use LLD linker on Windows | |
| shell: bash | |
| run: | | |
| echo "RUSTFLAGS=${RUSTFLAGS} -C linker=rust-lld" >> $GITHUB_ENV | |
| if: runner.os == 'Windows' | |
| - name: cargo clippy | |
| run: | | |
| ${{ env.command }} --locked --all-targets | |
| if: matrix.type == 'together' | |
| - name: cargo clippy (each crate individually) | |
| shell: bash | |
| run: | | |
| cargo metadata --no-deps --format-version 1 \ | |
| | jq -r '.packages[].name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package \"${crate}\"" >> "${{ runner.temp }}/queue" | |
| done | |
| if: matrix.type == 'individually' | |
| - name: cargo clippy (various features) | |
| shell: bash | |
| run: | | |
| metadata=$(cargo metadata --no-deps --format-version 1) | |
| for feature in alloc parallel scale-codec serde; do | |
| printf '%s' "$metadata" \ | |
| | jq -r --arg feature "$feature" '.packages[] | select( | |
| (.features | has($feature)) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/(execution|farmer|node|shared)/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`${feature}\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package \"${crate}\" --features \"${crate}/${feature}\"" >> "${{ runner.temp }}/queue" | |
| done | |
| done | |
| # Ensure `clippy` is happy with `guest` feature | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select( | |
| (.features | has("guest")) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue" | |
| echo "Checking \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --all-targets --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue" | |
| if: matrix.type == 'features' | |
| - parallel: | |
| - shell: bash | |
| env: | |
| WORKER_ID: "1" | |
| run: ¶llel_worker | | |
| set -e | |
| mkdir -p "${{ runner.temp }}/claims" | |
| export CARGO_TARGET_DIR="target_${WORKER_ID}" | |
| while IFS= read -r message && IFS= read -r command; do | |
| hash=$(printf '%s\n%s\n' "$message" "$command" | sha256sum | cut -d' ' -f1) | |
| if mkdir "${{ runner.temp }}/claims/${hash}" 2>/dev/null; then | |
| echo "$message" | |
| eval "$command" | |
| fi | |
| done < "${{ runner.temp }}/queue" | |
| if: matrix.type == 'individually' || matrix.type == 'features' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "2" | |
| run: *parallel_worker | |
| if: matrix.type == 'individually' || matrix.type == 'features' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "3" | |
| run: *parallel_worker | |
| if: matrix.type == 'individually' || matrix.type == 'features' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "4" | |
| run: *parallel_worker | |
| if: matrix.type == 'individually' || matrix.type == 'features' | |
| - name: Save cache | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0 | |
| with: | |
| path: *cache-path | |
| key: *cache-key | |
| # Only save on `main` branch and only from one canonical matrix variant per OS | |
| if: github.ref == 'refs/heads/main' && matrix.type == 'together' && matrix.target == '' | |
| cargo-test: | |
| strategy: | |
| matrix: | |
| os: | |
| - ubuntu-26.04 | |
| - ubuntu-26.04-arm | |
| - macos-26 | |
| - windows-11-vs2026-arm | |
| - windows-2025 | |
| miri: | |
| - true | |
| - false | |
| target: | |
| - "" | |
| type: | |
| # `together` variant is running in a separate job, see `cargo-test-slow` | |
| # - together | |
| - features | |
| # Only a few select targets are explicitly included with `guest-feature` below | |
| # - guest-feature | |
| # Test a few RISC-V targets (both Linux and one used for contracts) using Miri | |
| include: | |
| - os: ubuntu-26.04 | |
| miri: true | |
| type: guest-feature | |
| target: "" | |
| - os: ubuntu-26.04 | |
| miri: false | |
| type: guest-feature | |
| target: "" | |
| - os: ubuntu-26.04 | |
| miri: true | |
| type: features | |
| target: riscv64a23-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| miri: true | |
| type: guest-feature | |
| target: riscv64a23-unknown-linux-gnu | |
| # TODO: Tests will need custom allocator to work under this target | |
| # - os: ubuntu-26.04 | |
| # miri: true | |
| # type: guest-feature | |
| # target: crates/contracts/core/ab-contracts-tooling/src/riscv64-unknown-none-abundance.json | |
| fail-fast: false | |
| runs-on: ${{ matrix.os }} | |
| # Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead | |
| needs: act4 | |
| env: | |
| # NOTE: Running tests under Miri with nextest is SLOWER than without it: | |
| # https://github.com/nextest-rs/nextest/issues/27#issuecomment-4187292411 | |
| # TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally` | |
| command: >- | |
| ${{ matrix.miri == true && 'miri test --bins --lib --tests' || 'nextest run --no-tests pass' }} | |
| ${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }} | |
| -Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]' | |
| steps: &test-steps | |
| - name: Install cargo-nextest | |
| uses: taiki-e/install-action@a6b2e2dcd845ddd7f509ce4f3ed3d922b80cc5d9 # 2.84.0 | |
| with: | |
| tool: cargo-nextest | |
| - *rust-gpu-cache | |
| - *shared-test-steps | |
| - name: Install Vulkan runtime (Linux) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install --no-install-recommends --yes mesa-vulkan-drivers | |
| if: matrix.miri == false && runner.os == 'Linux' && matrix.type == 'together' | |
| - name: Install Vulkan runtime (Windows) | |
| run: | | |
| & { | |
| # See https://vulkan.lunarg.com/sdk/home for updates | |
| $sdkVersion = "1.4.350.0" | |
| if ("${{ runner.arch }}" -eq "ARM64") { | |
| $arch = "ARM64" | |
| $urlPath = "warm" | |
| $expectedHash = "1845c336ca17180ca4e4f6f52542ff9998aa39a76bc6ed75961eef67aad1a811" | |
| } else { | |
| $arch = "X64" | |
| $urlPath = "windows" | |
| $expectedHash = "23ce69f32cef3e2799617e2b1776cd0c71030d23a91f8375821cc40d76b185b9" | |
| } | |
| $url = "https://sdk.lunarg.com/sdk/download/$sdkVersion/$urlPath/VulkanRT-$arch-$sdkVersion-Components.zip" | |
| Invoke-WebRequest -Uri $url -OutFile VulkanRT.zip | |
| $actualHash = (Get-FileHash VulkanRT.zip -Algorithm SHA256).Hash.ToLower() | |
| if ($actualHash -ne $expectedHash.ToLower()) { | |
| throw "SHA256 mismatch: expected $expectedHash, got $actualHash" | |
| } | |
| 7z e VulkanRT.zip -o"${{ runner.temp }}\vulkan" -r "vulkan-1.*" | |
| Remove-Item VulkanRT.zip | |
| $vulkanPath = "${{ runner.temp }}\vulkan" | |
| "$vulkanPath" >> $env:GITHUB_PATH | |
| } | |
| & { | |
| # See https://github.com/mmozeiko/build-mesa/releases for updates | |
| $version = "26.1.4" | |
| if ("${{ runner.arch }}" -eq "ARM64") { | |
| $arch = "arm64" | |
| $archJson = "aarch64" | |
| $expectedHash = "b77f5ea72e442c5424b85eb55199bf9bf9404809e1d832ed88183b36cf3a440e" | |
| } else { | |
| $arch = "x64" | |
| $archJson = "x86_64" | |
| $expectedHash = "18f2c6a3eb2f36b669d68b7a888647aeaf0c007a61487f8cf606df4365126e85" | |
| } | |
| $url = "https://github.com/mmozeiko/build-mesa/releases/download/$version/mesa-lavapipe-$arch-$version.7z" | |
| Invoke-WebRequest -Uri $url -OutFile mesa-lavapipe.7z | |
| $actualHash = (Get-FileHash mesa-lavapipe.7z -Algorithm SHA256).Hash.ToLower() | |
| if ($actualHash -ne $expectedHash.ToLower()) { | |
| throw "SHA256 mismatch: expected $expectedHash, got $actualHash" | |
| } | |
| 7z x mesa-lavapipe.7z -o"${{ runner.temp }}\mesa" | |
| Remove-Item mesa-lavapipe.7z | |
| $mesaPath = "${{ runner.temp }}\mesa" | |
| "$mesaPath" >> $env:GITHUB_PATH | |
| $icdPath = "$mesaPath\lvp_icd.$archJson.json" | |
| New-Item -Path "HKLM:\SOFTWARE\Khronos\Vulkan\Drivers" -Force| Out-Null | |
| Set-ItemProperty -Path "HKLM:\SOFTWARE\Khronos\Vulkan\Drivers" -Name $icdPath -Value 0 -Type DWord | |
| } | |
| if: matrix.miri == false && runner.os == 'Windows' && matrix.type == 'together' | |
| - name: cargo ${{ env.command }} (together) | |
| run: | | |
| cargo -Zgitoxide -Zgit ${{ env.command }} --locked | |
| if: matrix.miri == false && matrix.type == 'together' | |
| # The whole app is only needed on Windows since it doesn't support shebang | |
| - name: Install Miri capture wrapper | |
| shell: bash | |
| run: | | |
| miri_bin="$(rustup which miri)" | |
| miri_dir="$(dirname "$miri_bin")" | |
| mv "$miri_bin" "$miri_dir/miri.real" | |
| mkdir -p "${{ runner.temp }}/miri_invocations" | |
| cat > "${{ runner.temp }}/miri_wrapper.rs" << 'RUST_EOF' | |
| use std::fmt::Write as FmtWrite; | |
| use std::io::Write; | |
| use std::{env, fs, iter, process}; | |
| fn main() { | |
| let exe = env::current_exe().unwrap(); | |
| let miri_real = exe.parent().unwrap().join("miri.real"); | |
| if env::var_os("MIRI_BE_RUSTC").is_some() { | |
| let status = process::Command::new(&miri_real) | |
| .args(env::args_os().skip(1)) | |
| .status() | |
| .expect("failed to exec miri.real"); | |
| process::exit(status.code().unwrap_or(1)); | |
| } | |
| if let Ok(inv_path) = env::var("MIRI_REPLAY") { | |
| let data = fs::read_to_string(&inv_path).unwrap(); | |
| let mut lines = data.lines(); | |
| let cwd = lines.next().unwrap(); | |
| let n_args = lines.next().unwrap().parse().unwrap(); | |
| let args = iter::repeat_with(|| lines.next().unwrap()) | |
| .take(n_args) | |
| .collect::<Vec<_>>(); | |
| let n_envs = lines.next().unwrap().parse().unwrap(); | |
| let envs = iter::repeat_with(|| lines.next().unwrap().split_once('=').unwrap()) | |
| .take(n_envs) | |
| .collect::<Vec<_>>(); | |
| let status = process::Command::new(&miri_real) | |
| .args(&args) | |
| .env_clear() | |
| .envs(envs) | |
| .current_dir(cwd) | |
| .status() | |
| .expect("failed to exec miri.real in replay"); | |
| process::exit(status.code().unwrap_or(1)); | |
| } | |
| let args = env::args().skip(1).collect::<Vec<_>>(); | |
| let crate_name = args | |
| .windows(2) | |
| .find_map(|w| (w[0] == "--crate-name").then(|| w[1].as_str())) | |
| .unwrap_or("unknown"); | |
| let inv_dir = env::var("MIRI_INVOCATIONS_DIR").expect("MIRI_INVOCATIONS_DIR not set"); | |
| let queue_path = env::var("MIRI_QUEUE_PATH").expect("MIRI_QUEUE_PATH not set"); | |
| let inv_path = format!("{inv_dir}/{crate_name}-{}", process::id()); | |
| let cwd = env::current_dir().unwrap(); | |
| let envs = env::vars().collect::<Vec<_>>(); | |
| let mut data = String::new(); | |
| writeln!(data, "{}", cwd.display()).unwrap(); | |
| writeln!(data, "{}", args.len()).unwrap(); | |
| for arg in &args { | |
| writeln!(data, "{arg}").unwrap(); | |
| } | |
| writeln!(data, "{}", envs.len()).unwrap(); | |
| for (k, v) in &envs { | |
| writeln!(data, "{k}={v}").unwrap(); | |
| } | |
| fs::write(&inv_path, &data).unwrap(); | |
| let mut queue = fs::OpenOptions::new() | |
| .create(true) | |
| .append(true) | |
| .open(&queue_path) | |
| .unwrap(); | |
| let name = std::path::Path::new(&inv_path) | |
| .file_name() | |
| .unwrap() | |
| .to_string_lossy(); | |
| writeln!(queue, "Miri: running {name}").unwrap(); | |
| writeln!(queue, "MIRI_REPLAY=\"{inv_path}\" \"{}\"", exe.display()).unwrap(); | |
| } | |
| RUST_EOF | |
| rustc "${{ runner.temp }}/miri_wrapper.rs" -o "$miri_bin" | |
| if: matrix.miri == true && matrix.type == 'together' | |
| - name: cargo ${{ env.command }} --capture (together) | |
| shell: bash | |
| env: | |
| MIRI_INVOCATIONS_DIR: ${{ runner.temp }}/miri_invocations | |
| MIRI_QUEUE_PATH: ${{ runner.temp }}/queue | |
| run: | | |
| cargo -Zgitoxide -Zgit ${{ env.command }} --locked | |
| if: matrix.miri == true && matrix.type == 'together' | |
| - name: cargo test (various features) | |
| shell: bash | |
| run: | | |
| metadata=$(cargo metadata --no-deps --format-version 1) | |
| for feature in alloc parallel scale-codec serde; do | |
| printf '%s' "$metadata" \ | |
| | jq -r --arg feature "$feature" '.packages[] | select( | |
| (.features | has($feature)) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/(execution|farmer|node|shared)/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Testing \`${feature}\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/${feature}\"" >> "${{ runner.temp }}/queue" | |
| done | |
| done | |
| echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue" | |
| echo "cargo -Zgitoxide -Zgit $command --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue" | |
| echo "Testing \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue" | |
| echo "cargo -Zgitoxide -Zgit $command --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue" | |
| if: matrix.type == 'features' | |
| - name: cargo ${{ env.command }} (guest feature) | |
| shell: bash | |
| run: | | |
| metadata=$(cargo metadata --no-deps --format-version 1) | |
| # Ensure tests pass with `guest` feature | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Testing \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select( | |
| (.features | has("guest")) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Testing \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "cargo -Zgitoxide -Zgit $command --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| if: matrix.type == 'guest-feature' && runner.os == 'Linux' | |
| - parallel: | |
| - shell: bash | |
| env: | |
| WORKER_ID: "1" | |
| run: *parallel_worker | |
| if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "2" | |
| run: *parallel_worker | |
| if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "3" | |
| run: *parallel_worker | |
| if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature' | |
| - shell: bash | |
| env: | |
| WORKER_ID: "4" | |
| run: *parallel_worker | |
| if: matrix.miri == true || matrix.type == 'features' || matrix.type == 'guest-feature' | |
| # This is a hack to start slower jobs before the rest, see `cargo-test` job for full definition | |
| cargo-test-slow: | |
| strategy: | |
| matrix: | |
| os: | |
| - ubuntu-26.04 | |
| - ubuntu-26.04-arm | |
| - macos-26 | |
| - windows-11-vs2026-arm | |
| - windows-2025 | |
| miri: | |
| - true | |
| - false | |
| target: | |
| - "" | |
| type: | |
| - together | |
| # - features | |
| # - guest-feature | |
| include: | |
| - os: ubuntu-26.04 | |
| miri: true | |
| type: together | |
| target: riscv64a23-unknown-linux-gnu | |
| fail-fast: false | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| # NOTE: Running tests under Miri with nextest is SLOWER than without it: | |
| # https://github.com/nextest-rs/nextest/issues/27#issuecomment-4187292411 | |
| # TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally` | |
| command: >- | |
| ${{ matrix.miri == true && 'miri test --bins --lib --tests' || 'nextest run --no-tests pass' }} | |
| ${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }} | |
| -Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]' | |
| steps: *test-steps | |
| no-panic: | |
| strategy: | |
| matrix: | |
| os: | |
| - ubuntu-26.04 | |
| - ubuntu-26.04-arm | |
| - macos-26 | |
| - windows-11-vs2026-arm | |
| - windows-2025 | |
| type: | |
| - default | |
| - features | |
| # Only one target is explicitly included with `guest-feature` below | |
| # - guest-feature | |
| target: | |
| - "" | |
| include: | |
| - os: ubuntu-26.04 | |
| type: default | |
| target: riscv64a23-unknown-linux-gnu | |
| - os: ubuntu-26.04 | |
| type: features | |
| target: riscv64a23-unknown-linux-gnu | |
| # TODO: Tests will need custom allocator to work under this target | |
| # - os: ubuntu-26.04 | |
| # type: guest-feature | |
| # target: crates/contracts/core/ab-contracts-tooling/src/riscv64-unknown-none-abundance.json | |
| fail-fast: false | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| # TODO: `-Ztarget-applies-to-host` and the rest of arguments here are for `-Znext-solver=globally` | |
| command: >- | |
| cargo -Zgitoxide -Zgit build | |
| ${{ matrix.target != '' && format('-Zjson-target-spec --target {0}', matrix.target) || '' }} | |
| -Ztarget-applies-to-host -Zhost-config --config 'host.rustflags=["-Znext-solver=globally"]' | |
| steps: | |
| - *rust-gpu-cache | |
| - *shared-test-steps | |
| # Increase the inlining threshold to make sure the compiler can see that some functions do not panic. | |
| # Native CPU and LTO to allow the compiler to apply more optimizations and prove lack of panics in more cases. | |
| - name: Set up RUSTFLAGS | |
| shell: bash | |
| run: | | |
| echo "RUSTFLAGS=${RUSTFLAGS} -Cllvm-args=--inline-threshold=10000 -C embed-bitcode -C lto -Z dylib-lto ${{ matrix.target == '' && '-C target-cpu=native' || '' }}" >> $GITHUB_ENV | |
| - name: Ensure no panics in annotated code | |
| shell: bash | |
| run: | | |
| # TODO: This doesn't seem to work for now: https://users.rust-lang.org/t/compiler-optimizations-are-different-for-crate-itself-vs-dependency/130187?u=nazar-pc | |
| # So we end up building individual crates instead, which is unfortunate | |
| # ${{ env.command }} --release --all-targets --features no-panic | |
| cargo metadata --no-deps --format-version 1 \ | |
| | jq -r '.packages[] | select(.features | has("no-panic")) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package \"${crate}\"" >> "${{ runner.temp }}/queue" | |
| done | |
| if: matrix.type == 'default' | |
| - name: Ensure no panics in annotated code (various features) | |
| shell: bash | |
| run: | | |
| cargo metadata --no-deps --format-version 1 \ | |
| | jq -r '.packages[] | select( | |
| (.features | has("no-panic")) and | |
| (.features | has("alloc")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`alloc\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/alloc\"" >> "${{ runner.temp }}/queue" | |
| done | |
| echo "Checking \`executor\` in \`ab-contracts-common\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package ab-contracts-common --features ab-contracts-common/executor" >> "${{ runner.temp }}/queue" | |
| echo "Checking \`payload-builder\` in \`ab-system-contract-simple-wallet-base\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package ab-system-contract-simple-wallet-base --features ab-system-contract-simple-wallet-base/payload-builder" >> "${{ runner.temp }}/queue" | |
| if: matrix.type == 'features' | |
| - name: Ensure no panics in annotated code (guest feature) | |
| shell: bash | |
| run: | | |
| metadata=$(cargo metadata --no-deps --format-version 1) | |
| # Not all example/system contracts have the `no-panic` feature yet | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select( | |
| (.features | has("no-panic")) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| # Not all core contracts have both `no-panic` and `guest` features yet | |
| printf '%s' "$metadata" \ | |
| | jq -r '.packages[] | select( | |
| (.features | has("no-panic")) and | |
| (.features | has("guest")) and | |
| (.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/core/")) | |
| ) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`guest\` in \`${crate}\`" >> "${{ runner.temp }}/queue" | |
| echo "$command --release --all-targets --features no-panic --package \"${crate}\" --features \"${crate}/guest\"" >> "${{ runner.temp }}/queue" | |
| done | |
| if: matrix.type == 'guest-feature' && runner.os == 'Linux' | |
| - ¶llel-workers | |
| parallel: | |
| - shell: bash | |
| env: | |
| WORKER_ID: "1" | |
| run: *parallel_worker | |
| - shell: bash | |
| env: | |
| WORKER_ID: "2" | |
| run: *parallel_worker | |
| - shell: bash | |
| env: | |
| WORKER_ID: "3" | |
| run: *parallel_worker | |
| - shell: bash | |
| env: | |
| WORKER_ID: "4" | |
| run: *parallel_worker | |
| contracts: | |
| runs-on: ubuntu-26.04 | |
| # Gives the slow cargo test jobs a head start, forcing others to start after this relatively quick job instead | |
| needs: act4 | |
| steps: | |
| - *checkout-step | |
| - *cache-restore-step | |
| # TODO: Run clippy and `no-panic` on custom target for contracts too | |
| - name: Ensure contracts compile for a custom target | |
| shell: bash | |
| run: | | |
| cargo metadata --no-deps --format-version 1 \ | |
| | jq -r '.packages[] | select(.manifest_path | gsub("\\\\"; "/") | test("/crates/contracts/(example|system)/")) | .name' \ | |
| | tr -d '\r' \ | |
| | while IFS= read -r crate; do | |
| echo "Checking \`${crate}\` (release profile)" >> "${{ runner.temp }}/queue" | |
| echo "cargo run --bin cargo-ab-contract -- build --package \"${crate}\"" >> "${{ runner.temp }}/queue" | |
| echo "Checking \`${crate}\` (contract profile)" >> "${{ runner.temp }}/queue" | |
| echo "cargo run --bin cargo-ab-contract -- build --package \"${crate}\" --profile contract" >> "${{ runner.temp }}/queue" | |
| done | |
| - *parallel-workers | |
| act4: | |
| env: | |
| ACT4_REPO_REVISION: df4516ebd254eccbefdb5e395ac04f158390e178 | |
| strategy: | |
| matrix: | |
| os: | |
| - ubuntu-26.04 | |
| - ubuntu-26.04-arm | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - *checkout-step | |
| # Compute the hashes of inputs to avoid cloning and building ELFs when nothing has changed, see: | |
| # https://github.com/riscv/riscv-arch-test/issues/1654 | |
| - name: Compute sources hash | |
| id: sources-hash | |
| working-directory: crates/execution/ab-riscv-act4-runner | |
| run: | | |
| RES_HASH=$(find res -type f -print0 \ | |
| | sort -z \ | |
| | xargs -0 sha256sum \ | |
| | sha256sum \ | |
| | cut -d' ' -f1) | |
| COMBINED=$(printf '%s\n%s\n' "${ACT4_REPO_REVISION}" "${RES_HASH}" \ | |
| | sha256sum \ | |
| | cut -d' ' -f1) | |
| echo "value=${COMBINED}" >> $GITHUB_OUTPUT | |
| - name: Restore cache | |
| id: restore-cache | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0 | |
| with: | |
| path: &act4-work-cache-path | | |
| crates/execution/ab-riscv-act4-runner/work/*/elfs | |
| key: &act4-work-cache-key act4-work-${{ steps.sources-hash.outputs.value }} | |
| # TODO: Workaround for https://github.com/rust-lang/rustup/issues/988 | |
| - name: Download toolchain | |
| run: rustc --version | |
| - name: Clone ACT4 repo | |
| run: | | |
| git clone --depth 1 --revision="${ACT4_REPO_REVISION}" \ | |
| https://github.com/riscv/riscv-arch-test \ | |
| riscv-arch-test | |
| if: steps.restore-cache.outputs.cache-hit != 'true' | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # 4.2.0 | |
| if: steps.restore-cache.outputs.cache-hit != 'true' | |
| # TODO: Use official image once it is available: https://github.com/riscv/riscv-arch-test/pull/1161 | |
| - name: Build act4-build image | |
| id: build | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # 7.3.0 | |
| with: | |
| context: riscv-arch-test | |
| file: crates/execution/ab-riscv-act4-runner/res/Dockerfile | |
| cache-from: type=gha,scope=act4-build-${{ runner.arch }} | |
| # Only save on `main` branch | |
| cache-to: ${{ github.ref == 'refs/heads/main' && format('type=gha,mode=min,scope=act4-build-{0}', runner.arch) || '' }} | |
| load: true | |
| if: steps.restore-cache.outputs.cache-hit != 'true' | |
| - name: Build ELFs | |
| working-directory: crates/execution/ab-riscv-act4-runner | |
| run: | | |
| mkdir -p work | |
| docker run --rm \ | |
| -u $(id -u):$(id -g) \ | |
| -v ./work:/act4/work \ | |
| -v ./res/abundance:/act4/config/abundance:ro \ | |
| -e CONFIG_FILES="config/abundance/abundance-rv32i-max/test_config.yaml config/abundance/abundance-rv64i-max/test_config.yaml" \ | |
| -e FAST=True \ | |
| ${{ steps.build.outputs.imageid }} \ | |
| make | |
| if: steps.restore-cache.outputs.cache-hit != 'true' | |
| - parallel: | |
| - name: ACT4 tests RV32 | |
| working-directory: crates/execution/ab-riscv-act4-runner | |
| env: | |
| CARGO_TARGET_DIR: target_rv32 | |
| run: | | |
| cargo run --release -- rv32 work/abundance-rv32i-max/elfs | |
| - name: ACT4 tests RV64 (native) | |
| working-directory: crates/execution/ab-riscv-act4-runner | |
| env: | |
| CARGO_TARGET_DIR: target_rv64_native | |
| # Native CPU will use AES intrinsics for Zknd/Zkne implementation | |
| run: | | |
| RUSTFLAGS="$RUSTFLAGS -C target-cpu=native" cargo run --release -- rv64 work/abundance-rv64i-max/elfs | |
| # Force-disable AES intrinsics to test software implementation | |
| - name: ACT4 tests RV64 (-aes) | |
| working-directory: crates/execution/ab-riscv-act4-runner | |
| env: | |
| CARGO_TARGET_DIR: target_rv64_aes_disabled | |
| run: | | |
| RUSTFLAGS="$RUSTFLAGS -C target-feature=-aes" cargo run --release -- rv64 work/abundance-rv64i-max/elfs | |
| - name: Save cache | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # 6.1.0 | |
| with: | |
| path: *act4-work-cache-path | |
| key: *act4-work-cache-key | |
| # Only save on `main` branch and only from one OS since the contents will be identical for all OSes | |
| if: steps.restore-cache.outputs.cache-hit != 'true' && github.ref == 'refs/heads/main' && matrix.os == 'ubuntu-26.04' | |
| rust-all: | |
| # Hack for buggy GitHub Actions behavior with skipped checks: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks | |
| if: ${{ always() }} | |
| runs-on: ubuntu-slim | |
| needs: | |
| - cargo-clippy | |
| - cargo-fmt | |
| - cargo-test | |
| - cargo-test-slow | |
| - contracts | |
| - no-panic | |
| - act4 | |
| steps: | |
| - name: Job status check | |
| env: | |
| RESULTS: ${{ join(needs.*.result, ' ') }} | |
| run: | | |
| for result in $RESULTS; do | |
| [[ "$result" == "success" ]] || exit 1 | |
| done |