name: test-suite

on:
  push:
    branches:
      - stable
      - staging
      - trying
      - 'pr/*'
  pull_request:
  merge_group:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  # Deny warnings in CI
  # Disable debug info (see https://github.com/sigp/lighthouse/issues/4005)
  RUSTFLAGS: "-D warnings -C debuginfo=0"
  # Prevent Github API rate limiting.
  LIGHTHOUSE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  # Enable self-hosted runners for the sigp repo only.
  SELF_HOSTED_RUNNERS: ${{ github.repository == 'sigp/lighthouse' }}
  # Self-hosted runners need to reference a different host for `./watch` tests.
  WATCH_HOST: ${{ github.repository == 'sigp/lighthouse' && 'host.docker.internal' || 'localhost' }}
  # Disable incremental compilation
  CARGO_INCREMENTAL: 0
  # Enable portable to prevent issues with caching `blst` for the wrong CPU type
  TEST_FEATURES: portable
jobs:
  check-labels:
    runs-on: ubuntu-latest
    name: Check for 'skip-ci' label
    outputs:
      skip_ci: ${{ steps.set-output.outputs.SKIP_CI }}
    steps:
     - name: check for skip-ci label
       id: set-output
       env:
         LABELS: ${{ toJson(github.event.pull_request.labels) }}
       run: |
         SKIP_CI="false"
         if [ -z "${LABELS}" ]  || [ "${LABELS}" = "null" ]; then
           LABELS="none";
         else
           LABELS=$(echo ${LABELS} | jq -r '.[].name')
         fi
         for label in ${LABELS}; do
           if [ "$label" = "skip-ci" ]; then
             SKIP_CI="true"
             break
           fi
         done
         echo "skip_ci=$SKIP_CI" >> $GITHUB_OUTPUT

  target-branch-check:
    name: target-branch-check
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
    steps:
        - name: Check that the pull request is not targeting the stable branch
          run: test ${{ github.base_ref }} != "stable"
  release-tests-ubuntu:
    name: release-tests-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    # Use self-hosted runners only on the sigp repo.
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest'  }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
      env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    - name: Install Foundry (anvil)
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: foundry-rs/foundry-toolchain@v1
      with:
        version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
    - name: Run tests in release
      run: make nextest-release
    - name: Show cache stats
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: sccache --show-stats
  release-tests-windows:
    name: release-tests-windows
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "windows", "CI"]') || 'windows-2019'  }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
      env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    - name: Install Foundry (anvil)
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: foundry-rs/foundry-toolchain@v1
      with:
        version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
    - name: Install make
      if: env.SELF_HOSTED_RUNNERS == 'false'
      run: choco install -y make
    - name: Set LIBCLANG_PATH
      run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
    - name: Run tests in release
      run: make nextest-release
    - name: Show cache stats
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: sccache --show-stats
  beacon-chain-tests:
    name: beacon-chain-tests
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    # Use self-hosted runners only on the sigp repo.
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest'  }}
    env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
    - name: Run beacon_chain tests for all known forks
      run: make test-beacon-chain
    - name: Show cache stats
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: sccache --show-stats
  op-pool-tests:
    name: op-pool-tests
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
    - name: Run operation_pool tests for all known forks
      run: make test-op-pool
  network-tests:
    name: network-tests
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
        channel: stable
        cache-target: release
        bins: cargo-nextest
    - name: Run network tests for all known forks
      run: make test-network
  slasher-tests:
    name: slasher-tests
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
    - name: Run slasher tests for all supported backends
      run: make test-slasher
  debug-tests-ubuntu:
    name: debug-tests-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    # Use self-hosted runners only on the sigp repo.
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "large"]') || 'ubuntu-latest'  }}
    env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          bins: cargo-nextest
    - name: Install Foundry (anvil)
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: foundry-rs/foundry-toolchain@v1
      with:
        version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d
    - name: Run tests in debug
      run: make nextest-debug
    - name: Show cache stats
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: sccache --show-stats
  state-transition-vectors-ubuntu:
    name: state-transition-vectors-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
    - name: Run state_transition_vectors in release.
      run: make run-state-transition-tests
  ef-tests-ubuntu:
    name: ef-tests-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    # Use self-hosted runners only on the sigp repo.
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest'  }}
    env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          bins: cargo-nextest
    - name: Run consensus-spec-tests with blst and fake_crypto
      run: make nextest-ef
    - name: Show cache stats
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: sccache --show-stats
  basic-simulator-ubuntu:
    name: basic-simulator-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
    - name: Run a basic beacon chain sim that starts from Bellatrix
      run: cargo run --release --bin simulator basic-sim
  fallback-simulator-ubuntu:
    name: fallback-simulator-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
    - name: Run a beacon chain sim which tests VC fallback behaviour
      run: cargo run --release --bin simulator fallback-sim
  execution-engine-integration-ubuntu:
    name: execution-engine-integration-ubuntu
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ${{ github.repository == 'sigp/lighthouse' && fromJson('["self-hosted", "linux", "CI", "small"]') || 'ubuntu-latest'  }}
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      if: env.SELF_HOSTED_RUNNERS == 'false'
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          cache: false
      env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    - name: Add go compiler to $PATH
      if: env.SELF_HOSTED_RUNNERS == 'true'
      run: echo "/usr/local/go/bin" >> $GITHUB_PATH
    - name: Run exec engine integration tests in release
      run: make test-exec-engine
  check-code:
    name: check-code
    runs-on: ubuntu-latest
    env:
      CARGO_INCREMENTAL: 1
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: stable
          cache-target: release
          components: rustfmt,clippy
          bins: cargo-audit
    - name: Check formatting with cargo fmt
      run: make cargo-fmt
    - name: Lint code for quality and style with Clippy
      run: make lint
    - name: Certify Cargo.lock freshness
      run: git diff --exit-code Cargo.lock
    - name: Typecheck benchmark code without running it
      run: make check-benches
    - name: Validate state_processing feature arbitrary-fuzz
      run:  make arbitrary-fuzz
    - name: Run cargo audit
      run: make audit-CI
    - name: Run cargo vendor to make sure dependencies can be vendored for packaging, reproducibility and archival purpose
      run:  CARGO_HOME=$(readlink -f $HOME) make vendor
    - name: Markdown-linter
      run: make mdlint
  check-msrv:
    name: check-msrv
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Install Rust at Minimum Supported Rust Version (MSRV)
      run: |
        metadata=$(cargo metadata --no-deps --format-version 1)
        msrv=$(echo $metadata | jq -r '.packages | map(select(.name == "lighthouse")) | .[0].rust_version')
        rustup override set $msrv
    - name: Run cargo check
      run: cargo check --workspace
  cargo-udeps:
    name: cargo-udeps
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of nightly Rust
      uses: moonrepo/setup-rust@v1
      with:
          channel: nightly
          bins: cargo-udeps
          cache: false
      env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    - name: Create Cargo config dir
      run: mkdir -p .cargo
    - name: Install custom Cargo config
      run: cp -f .github/custom/config.toml .cargo/config.toml
    - name: Run cargo udeps to identify unused crates in the dependency graph
      run: make udeps
    env:
      # Allow warnings on Nightly
      RUSTFLAGS: ""
  compile-with-beta-compiler:
    name: compile-with-beta-compiler
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Install dependencies
      run: sudo apt update && sudo apt install -y git gcc g++ make cmake pkg-config llvm-dev libclang-dev clang
    - name: Use Rust beta
      run: rustup override set beta
    - name: Run make
      run: make
  cli-check:
    name: cli-check
    needs: [check-labels]
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Get latest version of stable Rust
      uses: moonrepo/setup-rust@v1
      with:
        channel: stable
        cache-target: release
    - name: Run Makefile to trigger the bash script
      run: make cli
  # This job succeeds ONLY IF all others succeed. It is used by the merge queue to determine whether
  # a PR is safe to merge. New jobs should be added here.
  test-suite-success:
    name: test-suite-success
    if: needs.check-labels.outputs.skip_ci != 'true'
    runs-on: ubuntu-latest
    needs: [
      'check-labels',
      'target-branch-check',
      'release-tests-ubuntu',
      'release-tests-windows',
      'beacon-chain-tests',
      'op-pool-tests',
      'network-tests',
      'slasher-tests',
      'debug-tests-ubuntu',
      'state-transition-vectors-ubuntu',
      'ef-tests-ubuntu',
      'basic-simulator-ubuntu',
      'fallback-simulator-ubuntu',
      'execution-engine-integration-ubuntu',
      'check-code',
      'check-msrv',
      'cargo-udeps',
      'compile-with-beta-compiler',
      'cli-check',
    ]
    steps:
      - uses: actions/checkout@v4
      - name: Check that success job is dependent on all others
        run: ./scripts/ci/check-success-job.sh ./.github/workflows/test-suite.yml test-suite-success
