name: "Lint+Test"
on:
  pull_request:
    types: [labeled, opened, synchronize, reopened, auto_merge_enabled]
  push:
    branches:
      - main
      - devnet
      - testnet
      - mainnet
      - aptos-node-v*
      - aptos-release-v*
  workflow_dispatch:

env:
  HAS_BUILDPULSE_SECRETS: ${{ secrets.BUILDPULSE_ACCESS_KEY_ID != '' && secrets.BUILDPULSE_SECRET_ACCESS_KEY != '' }}
  HAS_DATADOG_SECRETS: ${{ secrets.DD_API_KEY != '' }}
  CARGO_INCREMENTAL: "0"
  CARGO_TERM_COLOR: always

# cancel redundant builds
concurrency:
  # cancel redundant builds on PRs (only on PR, not on branches)
  group: ${{ github.workflow }}-${{ (github.event_name == 'pull_request' && github.ref) || github.sha }}
  cancel-in-progress: true

jobs:
  # This job determines which files were changed
  file_change_determinator:
    runs-on: ubuntu-latest
    outputs:
      only_docs_changed: ${{ steps.determine_file_changes.outputs.only_docs_changed }}
    steps:
      - uses: actions/checkout@v4
      - name: Run the file change determinator
        id: determine_file_changes
        uses: ./.github/actions/file-change-determinator

  # Run all general lints (i.e., non-rust and docs lints). This is a PR required job.
  general-lints:
    needs: file_change_determinator
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        with:
          fetch-depth: 0 # get all the history because python-lint-tests requires it.
      - name: Run general lints
        uses: ./.github/actions/general-lints
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - run: echo "Skipping general lints! Unrelated changes detected."
        if: needs.file_change_determinator.outputs.only_docs_changed == 'true'

  # Run the crypto hasher domain separation checks
  rust-cryptohasher-domain-separation-check:
    needs: file_change_determinator
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    if: contains(github.event.pull_request.labels.*.name, 'CICD:non-required-tests')
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v4
      - uses: aptos-labs/aptos-core/.github/actions/rust-setup@main
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - run: python3 scripts/check-cryptohasher-symbols.py

  # Run all rust lints. This is a PR required job.
  rust-lints:
    needs: file_change_determinator
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    steps:
      - uses: actions/checkout@v4
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        with:
          fetch-depth: 0 # get all the history because cargo xtest --change-since origin/main requires it.
      - name: Run rust lints
        uses: ./.github/actions/rust-lints
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - run: echo "Skipping rust lints! Unrelated changes detected."
        if: needs.file_change_determinator.outputs.only_docs_changed == 'true'

  # Run cargo deny. This is a PR required job.
  rust-cargo-deny:
    needs: file_change_determinator
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
      - uses: EmbarkStudios/cargo-deny-action@v1
        with:
          command: check licenses
      - run: echo "Skipping cargo deny! Unrelated changes detected."
        if: needs.file_change_determinator.outputs.only_docs_changed == 'true'

  # Run all rust smoke tests. This is a PR required job.
  rust-smoke-tests:
    needs: file_change_determinator
    if: | # Only run on each PR once an appropriate event occurs
      (
        github.event_name == 'workflow_dispatch' ||
        github.event_name == 'push' ||
        contains(github.event.pull_request.labels.*.name, 'CICD:run-e2e-tests') ||
        github.event.pull_request.auto_merge != null) ||
        contains(github.event.pull_request.body, '#e2e'
      )
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    steps:
      - uses: actions/checkout@v4
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
      - name: Run rust smoke tests
        uses: ./.github/actions/rust-smoke-tests
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - run: echo "Skipping rust smoke tests! Unrelated changes detected."
        if: needs.file_change_determinator.outputs.only_docs_changed == 'true'

  # Run only the targeted rust unit tests. This is a PR required job.
  rust-targeted-unit-tests:
    if: | # Don't run on release branches. Instead, all unit tests will be triggered.
      (
        !contains(github.event.pull_request.base.ref, '-release-')
      )
    needs: file_change_determinator
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          fetch-depth: 0 # Fetch all git history for accurate target determination
      - name: Run targeted rust unit tests
        uses: ./.github/actions/rust-targeted-unit-tests
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}

  # Run all rust unit tests. This is not a PR required job.
  rust-unit-tests:
    if: | # Only run when an appropriate event occurs
      (
        github.event_name == 'workflow_dispatch' ||
        github.event_name == 'push' ||
        contains(github.event.pull_request.labels.*.name, 'CICD:run-all-unit-tests') ||
        contains(github.event.pull_request.base.ref, '-release-')
      )
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    steps:
      - uses: actions/checkout@v4
      - name: Run rust unit tests
        uses: ./.github/actions/rust-unit-tests
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
          BUILDKITE_ANALYTICS_TOKEN: ${{ secrets.BUILDKITE_ANALYTICS_TOKEN }}

  # Run the cached packages build. This is a PR required job.
  rust-build-cached-packages:
    needs: file_change_determinator
    if: | # Only run on each PR once an appropriate event occurs
      (
        github.event_name == 'workflow_dispatch' ||
        github.event_name == 'push' ||
        contains(github.event.pull_request.labels.*.name, 'CICD:run-e2e-tests') ||
        github.event.pull_request.auto_merge != null
      )
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    steps:
      - uses: actions/checkout@v4
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
      - uses: aptos-labs/aptos-core/.github/actions/rust-setup@main
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - name: Run aptos cached packages build test
        if: needs.file_change_determinator.outputs.only_docs_changed != 'true'
        run: scripts/cargo_build_aptos_cached_packages.sh --check
      - run: echo "Skipping cached packages test! Unrelated changes detected."
        if: needs.file_change_determinator.outputs.only_docs_changed == 'true'

  # Run the consensus only unit tests
  rust-consensus-only-unit-test:
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    if: contains(github.event.pull_request.labels.*.name, 'CICD:build-consensus-only-image')
    steps:
      - uses: actions/checkout@v4
      - uses: aptos-labs/aptos-core/.github/actions/rust-setup@main
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - uses: taiki-e/install-action@v1.5.6
        with:
          tool: nextest
      - run: cargo nextest run --locked --workspace --exclude smoke-test --exclude aptos-testcases --exclude aptos-api --exclude aptos-executor-benchmark --exclude aptos-backup-cli --retries 3 --no-fail-fast -F consensus-only-perf-test
        env:
          RUST_MIN_STACK: 4297152

  # Run the consensus only smoke test
  rust-consensus-only-smoke-test:
    runs-on: runs-on,cpu=64,family=c7,hdd=500,image=aptos-ubuntu-x64,run-id=${{ github.run_id }}
    if: contains(github.event.pull_request.labels.*.name, 'CICD:build-consensus-only-image')
    steps:
      - uses: actions/checkout@v4
      - uses: aptos-labs/aptos-core/.github/actions/rust-setup@main
        with:
          GIT_CREDENTIALS: ${{ secrets.GIT_CREDENTIALS }}
      - uses: taiki-e/install-action@v1.5.6
        with:
          tool: nextest
      # prebuild aptos-node binary, so that tests don't start before node is built.
      # also prebuild aptos-node binary as a separate step to avoid feature unification issues
      - run: cargo build --locked --package=aptos-node -F consensus-only-perf-test --release && LOCAL_SWARM_NODE_RELEASE=1 CONSENSUS_ONLY_PERF_TEST=1 cargo nextest run --release --package smoke-test -E "test(test_consensus_only_with_txn_emitter)" --run-ignored all

      # We always try to create the artifact, but it only creates on flaky or failed smoke tests -- when the directories are empty.
      - name: Upload smoke test logs for failed and flaky tests
        uses: actions/upload-artifact@v3
        if: ${{ failure() || success() }}
        with:
          name: failed-consensus-only-smoke-test-logs
          # Retain all smoke test data except for the db (which may be large).
          path: |
            /tmp/.tmp*
            !/tmp/.tmp*/**/db/
          retention-days: 14