name: CI

on:
  workflow_dispatch:
  pull_request:
  push:
    branches:
      - main

jobs:

  test:
    name: Test (+${{ matrix.rust }}) on ${{ matrix.os }}
    # The large timeout is to accommodate:
    # - Windows builds (75 minutes, typically 30-50 minutes)
    # - parameter downloads (40 minutes, but only when the cache expires)
    timeout-minutes: 115
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macOS-latest]
        rust: [stable]
    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full

    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ matrix.rust }}
          profile: minimal
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: cargo fetch
        uses: actions-rs/cargo@v1.0.3
        with:
          command: 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

      - 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

      # Modified from:
      # https://github.com/zcash/librustzcash/blob/c48bb4def2e122289843ddb3cb2984c325c03ca0/.github/workflows/ci.yml#L20-L33
      - name: Fetch path to Zcash parameters
        working-directory: ./zebra-consensus
        shell: bash
        run: echo "ZCASH_PARAMS=$(cargo run --example get-params-path)" >> $GITHUB_ENV
      - name: Cache Zcash parameters
        id: cache-params
        uses: actions/cache@v2
        with:
          path: ${{ env.ZCASH_PARAMS }}
          key: ${{ runner.os }}-sprout-and-sapling-params
      - name: Fetch Zcash parameters
        if: steps.cache-params.outputs.cache-hit != 'true'
        working-directory: ./zebra-consensus
        run: cargo run --example download-params

      - name: Show env vars
        run: |
            echo "Test env vars:"
            echo "ZEBRA_SKIP_NETWORK_TESTS=${{ env.ZEBRA_SKIP_NETWORK_TESTS }}"
            echo "ZCASH_PARAMS=${{ env.ZCASH_PARAMS }}"
            echo "PROPTEST_CASES=${{ env.PROPTEST_CASES }}"
            echo "PROPTEST_MAX_SHRINK_ITERS=${{ env.PROPTEST_MAX_SHRINK_ITERS }}"
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      - name: Run tests
        uses: actions-rs/cargo@v1.0.3
        with:
          command: test
          args: --verbose --all

      # Explicitly run any tests that are usually #[ignored]

      - name: Run zebrad large sync tests
        # 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'
        uses: actions-rs/cargo@v1.0.3
        with:
          command: test
          # Note: this only runs the zebrad acceptance tests, because re-running all the test binaries is slow on Windows
          args: --verbose --package zebrad --test acceptance sync_large_checkpoints_ -- --ignored

  test-fake-activation-heights:
    name: Build (+${{ matrix.rust }}) zebra-state with fake activation heights on ubuntu-latest
    timeout-minutes: 60
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust: [stable]
    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full

    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ matrix.rust }}
          profile: minimal
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: cargo fetch
        uses: actions-rs/cargo@v1.0.3
        with:
          command: fetch

      - name: Show env vars
        run: |
            echo "Test env vars:"
            echo "ZEBRA_SKIP_NETWORK_TESTS=${{ env.ZEBRA_SKIP_NETWORK_TESTS }}"
            echo "ZCASH_PARAMS=${{ env.ZCASH_PARAMS }}"
            echo "PROPTEST_CASES=${{ env.PROPTEST_CASES }}"
            echo "PROPTEST_MAX_SHRINK_ITERS=${{ env.PROPTEST_MAX_SHRINK_ITERS }}"
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      # This test changes zebra-chain's activation heights,
      # which can recompile all the Zebra crates,
      # so we want its build products to be cached separately.
      #
      # Also, we don't want to accidentally use the fake heights in other tests.
      - name: Run tests with fake activation heights
        uses: actions-rs/cargo@v1.0.3
        env:
          TEST_FAKE_ACTIVATION_HEIGHTS: ""
        with:
          command: test
          # Note: this only runs the zebra-state crate tests,
          # because re-running all the test binaries can be slow
          args: --verbose --package zebra-state --lib -- with_fake_activation_heights

  build-chain-no-features:
    name: Build (+${{ matrix.rust }}) zebra-chain w/o features on ubuntu-latest
    timeout-minutes: 60
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust: [stable, beta]
    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full


    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: ${{ matrix.rust }}
          profile: minimal
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: cargo fetch
        uses: actions-rs/cargo@v1.0.3
        with:
          command: fetch

      - name: Show env vars
        run: |
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      - name: Run build without features enabled
        working-directory: ./zebra-chain
        run: cargo build --verbose --no-default-features

  build:
    name: Build (+stable) on ubuntu-latest
    timeout-minutes: 60
    runs-on: ubuntu-latest

    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full

    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          profile: minimal
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: cargo fetch
        uses: actions-rs/cargo@v1.0.3
        with:
          command: fetch

      - name: Show env vars
        run: |
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      - name: Build
        uses: actions-rs/cargo@v1.0.3
        with:
          command: build
          args: --verbose --release

  clippy-deps:
    name: Clippy (stable)
    timeout-minutes: 30
    runs-on: ubuntu-latest
    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full

    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          components: clippy
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: Show env vars
        run: |
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      - name: Run clippy
        uses: actions-rs/clippy-check@v1.0.7
        with:
          # GitHub displays the clippy job and its results as separate entries
          name: Clippy (stable) Results
          token: ${{ secrets.GITHUB_TOKEN }}
          args: --all-features --all-targets -- -D warnings

      # This check makes sure the crate dependency check is accurate
      - name: Check Cargo.lock is up to date
        uses: actions-rs/cargo@v1.0.3
        with:
          command: check
          args: --locked --all-features --all-targets

      # Edit zebra/deny.toml to allow duplicate dependencies
      - name: Check for dependent crates with different versions
        uses: EmbarkStudios/cargo-deny-action@v1.2.9
        with:
          command: check bans
          args: --all-features --workspace

      - name: Check crate sources
        uses: EmbarkStudios/cargo-deny-action@v1.2.9
        with:
          command: check sources
          args: --all-features --workspace

  fmt:
    name: Rustfmt
    timeout-minutes: 30
    runs-on: ubuntu-latest
    env:
      CARGO_INCREMENTAL: 0
      RUST_BACKTRACE: full

    steps:
      - uses: actions/checkout@v2.4.0
        with:
          persist-credentials: false

      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable
          components: rustfmt
          override: true

      - uses: Swatinem/rust-cache@v1

      - name: Show env vars
        run: |
            echo "Common env vars:"
            echo "RUST_BACKTRACE=${{ env.RUST_BACKTRACE }}"
            echo "Build env vars:"
            echo "CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }}"
            echo "CARGO_TARGET_DIR=${{ env.CARGO_TARGET_DIR }}"

      - name: Check rustfmt
        uses: actions-rs/cargo@v1.0.3
        with:
          command: fmt
          args: --all -- --check
