Skip to content

Fix/v2 cpi context borrow check #8534

Fix/v2 cpi context borrow check

Fix/v2 cpi context borrow check #8534

Workflow file for this run

name: Tests
on:
push:
branches:
- anchor-next
paths-ignore:
- "docs/**"
pull_request:
paths-ignore:
- "docs/**"
workflow_dispatch:
env:
SOLANA_VERSION: "3.1.10"
SBF_TOOLS_VERSION: "v1.52"
jobs:
test-v2:
name: v2 Tests
runs-on: ubuntu-latest
timeout-minutes: 60
env:
CARGO_INCREMENTAL: "0"
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
# tests-v2 runs `cargo build-sbf` on its member programs; the rest of
# the v2 stack doesn't need the Solana toolchain but installing it
# once for the whole job is cheaper than splitting the matrix.
- uses: ./.github/actions/setup-solana/
# Cache dependency downloads, but do not cache target directories here:
# coverage and debugger runs leave multi-GB artifacts behind, and saving
# or restoring those archives costs more than it buys in this job.
- uses: actions/cache@v4
name: Cache Cargo home
with:
path: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ runner.os }}-v2-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-home-${{ runner.os }}-v2-
# Install platform-tools up front so the concurrent `cargo build-sbf`
# invocations fired by parallel test threads don't race on the
# one-time extract-then-rename of the platform-tools directory (surfaces
# in CI as `Unable to rename: No such file or directory`).
# SBF_TOOLS_VERSION must match `tests-v2/src/lib.rs::build_program`.
#
# Cache the platform-tools directory separately from the Solana tool
# suite cache. The Solana cache is keyed by CLI version and may already
# be a hit before this directory exists, so it will not save platform
# tools that `cargo build-sbf --install-only` downloads later in the job.
- uses: actions/cache@v4
name: Cache SBF platform tools
with:
path: ~/.cache/solana/${{ env.SBF_TOOLS_VERSION }}/platform-tools
key: solana-platform-tools-${{ runner.os }}-${{ env.SBF_TOOLS_VERSION }}
- run: cargo build-sbf --tools-version "$SBF_TOOLS_VERSION" --install-only
# Tools for `make coverage-v2`:
# - lcov for merging sbf.lcov + host.lcov and generating the report.
# - cargo-llvm-cov for host-side instrumentation coverage.
#
# The three direct `cargo test -p anchor-{lang,spl}-v2` / `-p tests-v2`
# invocations that used to live here (including `anchor-lang-v2`'s
# `--features testing` flag for the Miri-witnesses scaffold) are
# subsumed by the coverage-v2 steps below — the Makefile's host
# coverage targets run the same packages under instrumentation.
- name: Install lcov
run: sudo apt-get install -y --no-install-recommends lcov
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
# Run the v2 test suite with coverage enabled, split across the same SBF
# and host-side coverage boundaries used by `make coverage-v2`. Each
# step still fails on test failure while keeping the slowest package
# boundary visible in the Actions UI.
- name: v2 SBF runtime coverage
run: make coverage-v2-sbf
- name: Prune v2 SBF coverage scratch
run: make coverage-v2-sbf-prune
- name: v2 host coverage setup
run: make coverage-v2-host-clean
- name: v2 host coverage for anchor-lang-v2
run: make coverage-v2-host-lang
- name: v2 host coverage for anchor-spl-v2
run: make coverage-v2-host-spl
- name: v2 host coverage for tests-v2
env:
CARGO_BUILD_JOBS: "1"
run: make coverage-v2-host-tests
- name: v2 host coverage for idl-build fixtures
run: make coverage-v2-host-idl-build
- name: Generate v2 coverage report
run: |
make coverage-v2-host-report
make coverage-v2-report
# Upload to Codecov. The action handles comment posting on PRs via
# the Codecov GitHub App — including fork PRs, where the token
# flows through their bot instead of our workflow's `GITHUB_TOKEN`.
#
# `token` is optional on public repos (Codecov supports tokenless
# uploads for open-source projects) but passing the repo-scoped
# secret when available speeds upload discovery and is required on
# anchor-next pushes. On fork PRs the secret is empty — the action
# falls through to the tokenless flow automatically.
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/coverage/combined.lcov
flags: v2
fail_ci_if_error: false
# Miri (Tree Borrows) — UB + aliasing + provenance checks on the
# same integration tests above. Catches things the default cargo
# test run can't: `AccountView` copy-aliasing, `Slab::header_ptr`
# write provenance, `AccountCursor::next` strict-provenance walks.
# Caches the nightly toolchain and miri component to avoid a
# per-run rustup fetch (~200 MB).
- uses: actions/cache@v4
name: Cache rustup nightly + miri
with:
path: |
~/.rustup/toolchains/
~/.rustup/update-hashes/
~/.rustup/settings.toml
key: rustup-miri-${{ runner.os }}-v2-${{ hashFiles('rust-toolchain*', '**/rust-toolchain*') }}
restore-keys: |
rustup-miri-${{ runner.os }}-v2-
- name: Install nightly + miri
run: |
rustup toolchain install nightly --component miri --profile minimal
cargo +nightly miri setup
- name: Miri (Tree Borrows) for anchor-lang-v2 + anchor-spl-v2
env:
MIRIFLAGS: '-Zmiri-tree-borrows'
run: cargo +nightly miri test -p anchor-lang-v2 -p anchor-spl-v2 --tests --features anchor-lang-v2/testing
# Kani harnesses live in-tree under `#[cfg(kani)]` and are runnable
# locally via `cargo kani -p anchor-lang-v2` / `-p anchor-spl-v2`,
# but are not wired into CI: kani-verifier 0.67.0 has an
# upstream-deterministic `.unwrap()` panic on CBMC `ERROR` status
# lines (kani#4519, fixed on main in #4540, not yet released).
# Re-enable once kani-verifier ≥ 0.68.0 ships on crates.io.
# CLI: unit-test + build sanity only. No anchor-binary install,
# no `anchor build`/`anchor test` flows, no tests/* integration.
- name: Run CLI Tests
run: cargo test -p anchor-cli
# Install the `anchor` bin to `~/.cargo/bin` (already on PATH via
# rustup). Shared `./target/` via `--target-dir` means the build
# reuses artifacts from prior `cargo test -p anchor-cli` instead
# of starting from scratch in a temp tree; `--debug` skips release
# opt (smoke test — runtime speed doesn't matter).
- name: Install CLI
run: cargo install --path cli --locked --debug --force --target-dir ./target
# `anchor debugger` smoke test. These bench crates have no
# Anchor.toml, so the CLI falls into loose mode and runs the full
# pipeline: `cargo build-sbf` → `cargo test --features profile` →
# TUI. We wrap in `script` so crossterm can enter raw mode on a
# pty (GHA stdin/stdout aren't ttys) and `timeout` kills the TUI
# once it's in its event loop.
#
# Failure handling: each `prog` is run in its own subshell so
# `cd` is scoped (no `popd` bookkeeping) and so `|| rc=$?`
# captures the inner pipeline's exit status directly. Build and
# TUI phases are kept distinct so a build break is reported as
# such instead of bleeding into a confusing TUI failure.
#
# Exit-code semantics for the TUI phase:
# 124 → timeout-killed (TUI was still alive — success)
# 0 → clean exit (rare in CI; legal)
# else → real failure; dump the pty log
- name: anchor debugger TUI launches for bench anchor-v2 programs
run: |
set -euo pipefail
fail=0
for prog in bench/programs/*/anchor-v2; do
echo "::group::anchor debugger in $prog"
log=$(mktemp)
# Build phase — surface build breaks as a distinct error.
if ! ( cd "$prog" && anchor coverage ); then
echo "::error::`anchor coverage` failed in $prog"
fail=1
rm -f "$log"
echo "::endgroup::"
continue
fi
# TUI phase — `|| rc=$?` captures the subshell exit cleanly
# without toggling `set +e`/`set -e` around it.
rc=0
( cd "$prog" && timeout --kill-after=30 30 \
script -qec 'anchor debugger --skip-build' "$log" ) || rc=$?
if [ "$rc" -ne 0 ] && [ "$rc" -ne 124 ]; then
echo "::error::anchor debugger failed in $prog (exit $rc)"
cat "$log"
fail=1
fi
rm -f "$log"
echo "::endgroup::"
done
exit "$fail"