name: General on: push: branches: - main - dev paths-ignore: - "**.md" - "!.github/workflows/*.yml" pull_request: paths-ignore: - "**.md" - "!.github/workflows/*.yml" permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: CARGO_INCREMENTAL: "0" CARGO_PROFILE_TEST_DEBUG: "0" NEXTEST_VERSION: "0.9.140" jobs: fmt-rs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install nightly toolchain for rustfmt run: rustup install nightly-2026-04-14 --profile minimal --component rustfmt - name: Check Rust files are formatted run: cargo +nightly-2026-04-14 fmt --check fmt-toml: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install taplo-cli uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: taplo-cli@0.10.0 fallback: none - name: Check TOML files are formatted run: taplo fmt --check machete: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install active toolchain run: rustup install --profile minimal - name: Install cargo-machete uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: cargo-machete@0.9.2 fallback: none - name: Check for unused dependencies run: cargo machete deny: runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install cargo-deny uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: cargo-deny@0.20.2 fallback: none - name: Check licenses and advisories run: cargo deny check lint: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/install-system-deps - name: Install active toolchain run: rustup install --profile minimal --component clippy - name: Restore Rust cache uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: shared-key: ci-rust-cache cache-bin: false save-if: false - name: Lint workspace env: RISC0_SKIP_BUILD: "1" run: cargo clippy --workspace --all-targets --all-features -- -D warnings - name: Lint programs env: RISC0_SKIP_BUILD: "1" run: cargo clippy -p "*program" -- -D warnings - name: Check rustdoc links env: RISC0_SKIP_BUILD: "1" RUSTDOCFLAGS: -D warnings run: | # Guest packages generate colliding output names, so document them # separately below with isolated target directories. cargo doc \ --workspace \ --all-features \ --no-deps \ --exclude test_program_guests \ --exclude test_methods_guests for package in test_program_guests test_methods_guests; do cargo doc \ -p "$package" \ --all-features \ --no-deps \ --target-dir "$RUNNER_TEMP/rustdoc-$package" done unit-tests: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/install-system-deps - name: Install active toolchain run: rustup install --profile minimal - uses: ./.github/actions/install-risc0 - name: Restore Rust cache uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: shared-key: ci-rust-cache cache-bin: false save-if: false - name: Install nextest uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: nextest@${{ env.NEXTEST_VERSION }} fallback: none - name: Run tests env: RISC0_DEV_MODE: "1" RUST_LOG: info run: cargo nextest run --workspace --exclude integration_tests --exclude test_fixtures --all-features test-fixtures-tests: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/install-system-deps - name: Install active toolchain run: rustup install --profile minimal - uses: ./.github/actions/install-risc0 with: profile: runtime save-cache: "true" - name: Restore Rust cache uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: shared-key: ci-rust-cache cache-bin: false save-if: false - name: Install nextest uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: nextest@${{ env.NEXTEST_VERSION }} fallback: none - name: Run test_fixtures tests env: RISC0_DEV_MODE: "1" RUST_LOG: info run: cargo nextest run -p test_fixtures integration-tests-prepare: runs-on: ubuntu-24.04 timeout-minutes: 60 outputs: integration-matrix: ${{ steps.publish-matrix.outputs.integration-matrix }} auth-transfer-artifact-id: ${{ steps.publish-matrix.outputs.auth-transfer-artifact-id }} steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/install-system-deps - name: Install active toolchain run: rustup install --profile minimal - uses: ./.github/actions/install-risc0 with: save-cache: "true" - name: Restore Rust cache uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: shared-key: ci-rust-cache cache-bin: false save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }} - name: Install nextest uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: nextest@${{ env.NEXTEST_VERSION }} fallback: none - name: Discover integration test targets run: | target_dir="$RUNNER_TEMP/integration-test-targets" mkdir -p "$target_dir" cargo metadata \ --no-deps \ --format-version 1 > "$target_dir/metadata.json" jq -r ' [.packages[] | select(.name == "integration_tests") | .targets[] | select(.kind | index("test")) | select(.name != "tps") | .name] | sort | unique[] ' "$target_dir/metadata.json" > "$target_dir/discovered.targets" if [[ ! -s "$target_dir/discovered.targets" ]]; then echo "No non-tps integration test targets were discovered." >&2 exit 1 fi target_count="$(wc -l < "$target_dir/discovered.targets")" if (( target_count > 64 )); then echo "More than 64 integration targets were discovered." >&2 exit 1 fi { echo "### Integration target archives" echo echo "| Target | Archive size |" echo "| --- | ---: |" } >> "$GITHUB_STEP_SUMMARY" - name: Install Node.js for workflow artifact client uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 - name: Install workflow artifact client run: | npm ci \ --prefix .github/scripts/artifact-client \ --ignore-scripts \ --omit=dev \ --no-audit \ --no-fund - name: Build and upload integration test targets uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: ARTIFACT_CLIENT_SCRIPT: ${{ github.workspace }}/.github/scripts/artifact-client/upload.mjs INTEGRATION_ARTIFACT_DIR: ${{ runner.temp }}/integration-test-targets INTEGRATION_ARTIFACT_MANIFEST: ${{ runner.temp }}/integration-test-targets/uploaded.jsonl INTEGRATION_TARGETS_FILE: ${{ runner.temp }}/integration-test-targets/discovered.targets RISC0_DEV_MODE: "1" with: script: | const { pathToFileURL } = await import("node:url") const artifactClient = await import( pathToFileURL(process.env.ARTIFACT_CLIENT_SCRIPT) ) await artifactClient.buildAndUploadIntegrationTargets({ core, exec }) - name: Publish integration test matrix id: publish-matrix run: | target_dir="$RUNNER_TEMP/integration-test-targets" jq -r .target "$target_dir/uploaded.jsonl" \ | sort > "$target_dir/uploaded.targets" if ! diff -u \ "$target_dir/discovered.targets" \ "$target_dir/uploaded.targets"; then echo "Every discovered integration target must be uploaded exactly once." >&2 exit 1 fi integration_matrix="$(jq -s -c '{include: .}' \ "$target_dir/uploaded.jsonl")" auth_transfer_artifact_id="$(jq -r \ 'select(.target == "auth_transfer") | .artifact_id' \ "$target_dir/uploaded.jsonl")" if [[ ! "$auth_transfer_artifact_id" =~ ^[0-9]+$ ]]; then echo "The auth_transfer artifact ID is missing or invalid." >&2 exit 1 fi echo "integration-matrix=$integration_matrix" >> "$GITHUB_OUTPUT" echo "auth-transfer-artifact-id=$auth_transfer_artifact_id" \ >> "$GITHUB_OUTPUT" - name: Release artifact client and target metadata disk if: always() run: | rm -rf .github/scripts/artifact-client/node_modules rm -rf "$RUNNER_TEMP/integration-test-targets" - name: Stage RISC Zero runtime run: | mkdir -p "$RUNNER_TEMP/risc0-runtime" install -m 0755 "$(command -v r0vm)" "$RUNNER_TEMP/risc0-runtime/r0vm" - name: Upload RISC Zero runtime uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: risc0-runtime-3.0.5 path: ${{ runner.temp }}/risc0-runtime/r0vm compression-level: 6 retention-days: 1 if-no-files-found: error overwrite: true - name: Release staged RISC Zero runtime disk if: always() run: rm -f "$RUNNER_TEMP/risc0-runtime/r0vm" integration-tests: needs: - test-fixtures-tests - integration-tests-prepare runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: fail-fast: false max-parallel: 30 matrix: ${{ fromJSON(needs.integration-tests-prepare.outputs.integration-matrix) }} name: integration-tests (${{ matrix.target }}) steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/restore-risc0-runtime - name: Install nextest uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: nextest@${{ env.NEXTEST_VERSION }} fallback: none - name: Download integration test archive uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: artifact-ids: ${{ matrix.artifact_id }} path: ${{ runner.temp }}/integration-test-target skip-decompress: true - name: Run integration test target env: ARCHIVE_NAME: ${{ matrix.archive }} RISC0_DEV_MODE: "1" RUST_LOG: info TARGET: ${{ matrix.target }} run: | if [[ ! "$TARGET" =~ ^[a-z0-9_]+$ ]]; then echo "Invalid integration test target: $TARGET" >&2 exit 2 fi expected_archive="integration-test-$TARGET.tar.zst" if [[ "$ARCHIVE_NAME" != "$expected_archive" ]]; then echo "Archive $ARCHIVE_NAME does not match target $TARGET." >&2 exit 2 fi archive="$RUNNER_TEMP/integration-test-target/$ARCHIVE_NAME" extract_dir="$RUNNER_TEMP/nextest-target" echo "::group::Archive and disk usage before extraction" du -h "$archive" df -h "$RUNNER_TEMP" echo "::endgroup::" mkdir -p "$extract_dir" cargo-nextest nextest list \ --archive-file "$archive" \ --extract-to "$extract_dir" \ --workspace-remap "$GITHUB_WORKSPACE" \ --list-type binaries-only \ --message-format json \ --no-pager > /dev/null rm -f "$archive" echo "::group::Disk usage after extraction" du -sh "$extract_dir" df -h "$RUNNER_TEMP" echo "::endgroup::" available_kib="$(df --output=avail -k "$RUNNER_TEMP" | tail -n 1 | tr -d ' ')" minimum_free_kib=$((6 * 1024 * 1024)) if (( available_kib < minimum_free_kib )); then echo "Less than 6 GiB remains after target extraction." >&2 df -h "$RUNNER_TEMP" >&2 exit 1 fi cargo-nextest nextest run \ --cargo-metadata "$extract_dir/target/nextest/cargo-metadata.json" \ --binaries-metadata "$extract_dir/target/nextest/binaries-metadata.json" \ --target-dir-remap "$extract_dir/target" \ --workspace-remap "$GITHUB_WORKSPACE" \ --show-progress none \ --success-output immediate \ --status-level all \ --final-status-level all \ --no-output-indent \ -E "binary(=$TARGET)" valid-proof-test: needs: integration-tests-prepare runs-on: ubuntu-24.04 timeout-minutes: 90 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: ./.github/actions/restore-risc0-runtime - name: Install nextest uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: nextest@${{ env.NEXTEST_VERSION }} fallback: none - name: Download auth_transfer integration test archive uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: artifact-ids: ${{ needs.integration-tests-prepare.outputs.auth-transfer-artifact-id }} path: ${{ runner.temp }}/integration-test-target skip-decompress: true - name: Test valid proof env: RISC0_INFO: "1" RUST_LOG: info,risc0_zkvm::host::client::prove::external=debug,risc0_zkvm::host::server::prove=debug,risc0_zkvm::host::recursion::prove=debug run: | archive="$RUNNER_TEMP/integration-test-target/integration-test-auth_transfer.tar.zst" extract_dir="$RUNNER_TEMP/nextest-target" mkdir -p "$extract_dir" cargo-nextest nextest list \ --archive-file "$archive" \ --extract-to "$extract_dir" \ --workspace-remap "$GITHUB_WORKSPACE" \ --list-type binaries-only \ --message-format json \ --no-pager > /dev/null rm -f "$archive" echo "Starting non-development RISC Zero succinct proof." cargo-nextest nextest run \ --cargo-metadata "$extract_dir/target/nextest/cargo-metadata.json" \ --binaries-metadata "$extract_dir/target/nextest/binaries-metadata.json" \ --target-dir-remap "$extract_dir/target" \ --workspace-remap "$GITHUB_WORKSPACE" \ --no-capture \ --show-progress none \ -E 'binary(=auth_transfer) & test(=private::private_transfer_to_owned_account)' artifacts: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install active toolchain run: rustup install --profile minimal - uses: ./.github/actions/install-risc0 with: profile: build - name: Restore Rust cache uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: shared-key: ci-rust-cache cache-bin: false save-if: false - name: Install just uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2.83.2 with: tool: just@1.56.0 fallback: none - name: Build artifacts run: just build-artifacts - name: Check if artifacts match repository run: | if ! git diff --exit-code artifacts/; then echo "❌ Artifacts in the repository are out of date!" echo "Please run 'just build-artifacts' and commit the changes." exit 1 fi echo "✅ Artifacts are up to date"