---
name: Continuous Integration
on:
  push:
    branches:
      - "master"
  pull_request:
    types: [opened, synchronize, edited]
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

env:
  # Makes the upload-artifact work more reliably at the cost
  # of a bit of compile time.
  RUST_PROFILE: release
  SLOW_MACHINE: 1
  CI_SERVER_URL: "http://35.239.136.52:3170"
  PYTEST_OPTS_BASE: "-vvv --junit-xml=report.xml --timeout=1800 --durations=10"
  TEST_LOG_IGNORE_ERRORS: "1"

jobs:
  prebuild:
    name: Pre-build checks
    runs-on: ubuntu-24.04
    timeout-minutes: 120
    if: |
      github.event.action != 'edited' ||
      contains(github.event.pull_request.body, 'Changelog')
    env:
      BOLTDIR: bolts
    strategy:
      fail-fast: true
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}
          fetch-depth: 0

      - name: Rebase
        # We can't rebase if we're on master already.
        if: github.ref != 'refs/heads/master'
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
          git fetch origin ${{ github.base_ref }}
          git rebase origin/${{ github.base_ref }}

      - name: Check changelog
        env:
          PR_DESCRIPTION: "${{ github.event.pull_request.body || '' }}"
          EVENT_NAME: "${{ github.event_name }}"
          BASE_REF: "${{ github.base_ref || 'master' }}"
        run: |
          echo "Event Name: $EVENT_NAME"
          echo "Base Ref: $BASE_REF"
          echo "PR DESCRIPTION: $PR_DESCRIPTION"
          if [ "$EVENT_NAME" = "pull_request" ]; then
            if [[ "$PR_DESCRIPTION" != *"Changelog-None"* && \
                  -z "$(git log origin/$BASE_REF..HEAD --oneline --grep='Changelog-')" && \
                  "$(git rev-parse --abbrev-ref HEAD)" != "$BASE_REF" ]]; then
              echo "::error::'Changelog' entry is missing in all commits, and 'Changelog-None' not specified in the PR description"
              exit 1
            else
              if [[ "$PR_DESCRIPTION" == *"Changelog-None"* ]]; then
                echo "Changelog found in PR description"
              else
                echo "Changelog found in Commit \"$(git log origin/$BASE_REF..HEAD --oneline --grep='Changelog-')\""
              fi
            fi
          else
            echo "Not a PR event."
          fi

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh
          # We're going to check BOLT quotes, so get the latest version
          git clone https://github.com/lightning/bolts.git ../${BOLTDIR}
      - name: Configure
        run: ./configure --enable-debugbuild --enable-rust
      - name: Check source
        env:
          VALGRIND: 0
          PYTEST_OPTS: ${{ env.PYTEST_OPTS_BASE }}
        run: |
          uv run make check-source BASE_REF="origin/${{ github.base_ref }}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-prebuild
          path: report.xml
          if-no-files-found: ignore
      - name: Check Generated Files have been updated
        run: uv run make check-gen-updated
      - name: Check docs
        run: uv run make check-doc

  compile:
    name: Compile CLN ${{ matrix.cfg }}
    runs-on: ubuntu-24.04
    timeout-minutes: 30
    needs:
      - prebuild
    strategy:
      fail-fast: true
      matrix:
        include:
          - CFG: compile-gcc
            VALGRIND: 1
            COMPILER: gcc
          - CFG: compile-gcc-O3
            VALGRIND: 1
            COMPILER: gcc
            COPTFLAGS_VAR: COPTFLAGS="-O3 -Werror"
          # While we're at it let's try to compile with clang
          - CFG: compile-clang
            VALGRIND: 1
            COMPILER: clang
          - CFG: compile-clang-sanitizers
            COMPILER: clang
            ASAN: 1
            UBSAN: 1
            VALGRIND: 0
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Build
        env:
          COMPILER: ${{ matrix.COMPILER }}
          ASAN: ${{ matrix.ASAN }}
          UBSAN: ${{ matrix.UBSAN }}
          VALGRIND: ${{ matrix.VALGRIND }}
          COMPAT: 1
          CFG: ${{ matrix.CFG }}
        run: |
          set -e
          ./configure --enable-debugbuild CC="$COMPILER" ${{ matrix.COPTFLAGS_VAR }}

          uv run make -j $(nproc) testpack.tar.bz2

          # Rename now so we don't clash
          mv testpack.tar.bz2 cln-${CFG}.tar.bz2
      - name: Check rust packages
        run: cargo test --all
      - uses: actions/upload-artifact@v4
        with:
          name: cln-${{ matrix.CFG }}.tar.bz2
          path: cln-${{ matrix.CFG }}.tar.bz2

  check-units:
    # The unit test checks are not in the critical path (not dependent
    # on the integration tests), so run them with `valgrind`
    name: Run unit tests
    runs-on: ubuntu-24.04
    timeout-minutes: 60
    env:
      BOLTDIR: bolts
    needs:
      - compile
    strategy:
      fail-fast: true
      matrix:
        include:
          - CFG: compile-gcc
            VALGRIND: 1
          - CFG: compile-clang-sanitizers
            VALGRIND: 0
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh
          sudo apt-get update -qq
          # We're going to check BOLT quotes, so get the latest version
          git clone https://github.com/lightning/bolts.git ../${BOLTDIR}

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-${{ matrix.CFG }}.tar.bz2

      - name: Check
        run: |
          tar -xaf cln-${{ matrix.CFG }}.tar.bz2
          uv run eatmydata make -j $(nproc) check-units installcheck VALGRIND=${{ matrix.VALGRIND }}

  check-fuzz:
    name: Run fuzz regression tests
    runs-on: ubuntu-24.04
    needs:
      - prebuild
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Build
        run: |
          ./configure --enable-debugbuild --enable-fuzzing --enable-address-sanitizer --enable-ub-sanitizer --disable-valgrind CC=clang
          uv run make -j $(nproc) check-fuzz

  check-downgrade:
    name: Check we can downgrade the node
    runs-on: ubuntu-24.04
    needs:
      - compile
    strategy:
      fail-fast: false
      matrix:
        include:
          - CFG: compile-gcc
            TEST_DB_PROVIDER: sqlite3
            TEST_NETWORK: regtest
            VALGRIND: 1
          - CFG: compile-gcc
            TEST_DB_PROVIDER: postgres
            TEST_NETWORK: regtest
          - CFG: compile-gcc
            TEST_DB_PROVIDER: sqlite3
            TEST_NETWORK: liquid-regtest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Install bitcoind
        env:
          TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
        run: .github/scripts/install-bitcoind.sh

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-${{ matrix.CFG }}.tar.bz2

      - name: Unpack pre-built CLN
        env:
          CFG: ${{ matrix.CFG }}
        run: |
          tar -xaf cln-${CFG}.tar.bz2

      - name: Fetch and unpack previous CLN
        run: |
          mkdir /tmp/old-cln
          cd /tmp/old-cln
          wget https://github.com/ElementsProject/lightning/releases/download/v25.12/clightning-v25.12-ubuntu-24.04-amd64.tar.xz
          tar -xaf clightning-v25.12-ubuntu-24.04-amd64.tar.xz

      - name: Switch network
        if: ${{ matrix.TEST_NETWORK == 'liquid-regtest' }}
        run: |
          # Loading the network from config.vars rather than the envvar is a terrible idea...
          sed -i 's/TEST_NETWORK=regtest/TEST_NETWORK=liquid-regtest/g' config.vars
          cat config.vars

      - name: Test
        env:
          SLOW_MACHINE: 1
          PYTEST_PAR: 4
          TEST_DEBUG: 1
          TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
          TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
          LIGHTNINGD_POSTGRES_NO_VACUUM: 1
          VALGRIND: ${{ matrix.VALGRIND }}
          PREV_LIGHTNINGD: /tmp/old-cln/usr/bin/lightningd
          PYTEST_OPTS: ${{ env.PYTEST_OPTS_BASE }}
        run: |
          env
          cat config.vars
          sg wireshark "uv run eatmydata pytest tests/test_downgrade.py -n ${PYTEST_PAR} ${PYTEST_OPTS}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-check-downgrade-${{ matrix.TEST_DB_PROVIDER }}-${{ matrix.TEST_NETWORK }}
          path: report.xml
          if-no-files-found: ignore

  integration:
    name: Test CLN ${{ matrix.name }}
    runs-on: ubuntu-24.04
    timeout-minutes: 120
    env:
      RUST_PROFILE: release # Has to match the one in the compile step
      PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
    needs:
      - compile
    strategy:
      fail-fast: false
      matrix:
        include:
          - NAME: gcc
            CFG: compile-gcc
            TEST_DB_PROVIDER: sqlite3
            COMPILER: gcc
            TEST_NETWORK: regtest
          # While we're at it let's try to compile with clang
          - NAME: clang
            CFG: compile-clang
            TEST_DB_PROVIDER: sqlite3
            COMPILER: clang
            TEST_NETWORK: regtest
          # And of course we want to test postgres too
          - NAME: postgres
            CFG: compile-gcc
            COMPILER: gcc
            TEST_DB_PROVIDER: postgres
            TEST_NETWORK: regtest
          # And don't forget about elements (like cdecker did when
          # reworking the CI...)
          - NAME: liquid
            CFG: compile-gcc
            COMPILER: gcc
            TEST_NETWORK: liquid-regtest
            TEST_DB_PROVIDER: sqlite3
          # And dual funding!
          - NAME: dual-fund
            CFG: compile-gcc
            TEST_DB_PROVIDER: sqlite3
            COMPILER: gcc
            TEST_NETWORK: regtest
            EXPERIMENTAL_DUAL_FUND: 1
          # And splicing!
          - NAME: splicing
            CFG: compile-gcc
            TEST_DB_PROVIDER: sqlite3
            COMPILER: gcc
            TEST_NETWORK: regtest
            EXPERIMENTAL_SPLICING: 1
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Install bitcoind
        env:
          TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
        run: .github/scripts/install-bitcoind.sh

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-${{ matrix.CFG }}.tar.bz2

      - name: Unpack pre-built CLN
        env:
          CFG: ${{ matrix.CFG }}
        run: |
          tar -xaf cln-${CFG}.tar.bz2

      - name: Switch network
        if: ${{ matrix.TEST_NETWORK == 'liquid-regtest' }}
        run: |
          # Loading the network from config.vars rather than the envvar is a terrible idea...
          sed -i 's/TEST_NETWORK=regtest/TEST_NETWORK=liquid-regtest/g' config.vars
          cat config.vars

      - name: Test
        env:
          COMPILER: ${{ matrix.COMPILER }}
          EXPERIMENTAL_DUAL_FUND: ${{ matrix.EXPERIMENTAL_DUAL_FUND }}
          EXPERIMENTAL_SPLICING: ${{ matrix.EXPERIMENTAL_SPLICING }}
          COMPAT: 1
          CFG: ${{ matrix.CFG }}
          SLOW_MACHINE: 1
          PYTEST_PAR: 4
          TEST_DEBUG: 1
          TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
          TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
          LIGHTNINGD_POSTGRES_NO_VACUUM: 1
        run: |
          env
          cat config.vars
          VALGRIND=0 sg wireshark "uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-integration-${{ matrix.name }}
          path: report.xml
          if-no-files-found: ignore

  integration-valgrind:
    name: Valgrind Test CLN ${{ matrix.name }}
    runs-on: ubuntu-24.04
    timeout-minutes: 120
    env:
      RUST_PROFILE: release # Has to match the one in the compile step
      CFG: compile-gcc
      PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10 --test-group-random-seed=42
    needs:
      - compile
    strategy:
      fail-fast: false
      matrix:
        include:
          - NAME: Valgrind (01/10)
            GROUP: 1
            PYTEST_OPTS: --test-group=1  --test-group-count=10
          - NAME: Valgrind (02/10)
            GROUP: 2
            PYTEST_OPTS: --test-group=2  --test-group-count=10
          - NAME: Valgrind (03/10)
            GROUP: 3
            PYTEST_OPTS: --test-group=3  --test-group-count=10
          - NAME: Valgrind (04/10)
            GROUP: 4
            PYTEST_OPTS: --test-group=4  --test-group-count=10
          - NAME: Valgrind (05/10)
            GROUP: 5
            PYTEST_OPTS: --test-group=5  --test-group-count=10
          - NAME: Valgrind (06/10)
            GROUP: 6
            PYTEST_OPTS: --test-group=6  --test-group-count=10
          - NAME: Valgrind (07/10)
            GROUP: 7
            PYTEST_OPTS: --test-group=7  --test-group-count=10
          - NAME: Valgrind (08/10)
            GROUP: 8
            PYTEST_OPTS: --test-group=8  --test-group-count=10
          - NAME: Valgrind (09/10)
            GROUP: 9
            PYTEST_OPTS: --test-group=9  --test-group-count=10
          - NAME: Valgrind (10/10)
            GROUP: 10
            PYTEST_OPTS: --test-group=10 --test-group-count=10
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          sudo apt-get update -qq
          sudo apt-get install -yyq valgrind
          bash -x .github/scripts/setup.sh

      - name: Install bitcoind
        run: .github/scripts/install-bitcoind.sh

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-compile-gcc.tar.bz2

      - name: Unpack build
        run: tar -xvjf cln-compile-gcc.tar.bz2

      - name: Test
        env:
          SLOW_MACHINE: 1
          TEST_DEBUG: 1
          PYTEST_PAR: 2
        run: |
          VALGRIND=1 sg wireshark "uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}  ${{ matrix.PYTEST_OPTS }}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-integration-valgrind-${{ matrix.GROUP }}
          path: report.xml
          if-no-files-found: ignore

  integration-sanitizers:
    name: Sanitizers Test CLN
    runs-on: ubuntu-24.04
    timeout-minutes: 120
    env:
      RUST_PROFILE: release
      SLOW_MACHINE: 1
      TEST_DEBUG: 1
      PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10 --test-group-random-seed=42 
    needs:
      - compile
    strategy:
      fail-fast: false
      matrix:
        include:
          - NAME: ASan/UBSan (01/12)
            GROUP: 1
            PYTEST_OPTS: --test-group=1  --test-group-count=12
          - NAME: ASan/UBSan (02/12)
            GROUP: 2
            PYTEST_OPTS: --test-group=2  --test-group-count=12
          - NAME: ASan/UBSan (03/12)
            GROUP: 3
            PYTEST_OPTS: --test-group=3  --test-group-count=12
          - NAME: ASan/UBSan (04/12)
            GROUP: 4
            PYTEST_OPTS: --test-group=4  --test-group-count=12
          - NAME: ASan/UBSan (05/12)
            GROUP: 5
            PYTEST_OPTS: --test-group=5  --test-group-count=12
          - NAME: ASan/UBSan (06/12)
            GROUP: 6
            PYTEST_OPTS: --test-group=6  --test-group-count=12
          - NAME: ASan/UBSan (07/12)
            GROUP: 7
            PYTEST_OPTS: --test-group=7  --test-group-count=12
          - NAME: ASan/UBSan (08/12)
            GROUP: 8
            PYTEST_OPTS: --test-group=8  --test-group-count=12
          - NAME: ASan/UBSan (09/12)
            GROUP: 9
            PYTEST_OPTS: --test-group=9  --test-group-count=12
          - NAME: ASan/UBSan (10/12)
            GROUP: 10
            PYTEST_OPTS: --test-group=10 --test-group-count=12
          - NAME: ASan/UBSan (11/12)
            GROUP: 11
            PYTEST_OPTS: --test-group=11 --test-group-count=12
          - NAME: ASan/UBSan (12/12)
            GROUP: 12
            PYTEST_OPTS: --test-group=12 --test-group-count=12
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Install bitcoind
        run: .github/scripts/install-bitcoind.sh

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-compile-clang-sanitizers.tar.bz2

      - name: Unpack build
        run: tar -xvjf cln-compile-clang-sanitizers.tar.bz2

      - name: Test
        env:
          PYTEST_PAR: 2
        run: |
          sg wireshark "uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}  ${{ matrix.PYTEST_OPTS }}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-integration-sanitizers-${{ matrix.GROUP }}
          path: report.xml
          if-no-files-found: ignore

  update-docs-examples:
    name: Update examples in doc schemas (disabled temporarily!)
    if: false
    runs-on: ubuntu-24.04
    timeout-minutes: 30
    strategy:
      fail-fast: false
    env:
      VALGRIND: 0
      GENERATE_EXAMPLES: 1
      PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
      TEST_NETWORK: regtest
    needs:
      - compile
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh
      - name: Install bitcoind
        env:
          TEST_NETWORK: regtest
        run: .github/scripts/install-bitcoind.sh
      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-compile-gcc.tar.bz2
      - name: Unpack pre-built CLN
        run: |
          tar -xaf cln-compile-gcc.tar.bz2
      - name: Test
        run: |
          uv run eatmydata make -j $(nproc) check-doc-examples
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-update-docs-examples
          path: report.xml
          if-no-files-found: ignore

  min-btc-support:
    name: Test minimum supported BTC v${{ matrix.MIN_BTC_VERSION }} with ${{ matrix.NAME }}
    runs-on: ubuntu-24.04
    timeout-minutes: 120
    env:
      RUST_PROFILE: release # Has to match the one in the compile step
      PYTEST_OPTS: -vvv --junit-xml=report.xml --timeout=1800 --durations=10
    needs:
      - compile
    strategy:
      fail-fast: false
      matrix:
        include:
          - NAME: clang
            CFG: compile-clang
            TEST_DB_PROVIDER: sqlite3
            COMPILER: clang
            TEST_NETWORK: regtest
            MIN_BTC_VERSION: "25.0"
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.10
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Install dependencies
        run: |
          bash -x .github/scripts/setup.sh

      - name: Download Bitcoin Core
        run: wget "https://bitcoincore.org/bin/bitcoin-core-${{ matrix.MIN_BTC_VERSION }}/bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz"

      - name: Extract Bitcoin Core
        run: tar -xf "bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz"

      - name: Move Bitcoin Core Binaries
        run: sudo mv bitcoin-${{ matrix.MIN_BTC_VERSION }}/bin/* /usr/local/bin/

      - name: Clean Up Downloaded Bitcoin
        run: rm -rf "bitcoin-${{ matrix.MIN_BTC_VERSION }}-x86_64-linux-gnu.tar.gz" "bitcoin-${{ matrix.MIN_BTC_VERSION }}"

      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: cln-${{ matrix.CFG }}.tar.bz2

      - name: Unpack pre-built CLN
        env:
          CFG: ${{ matrix.CFG }}
        run: |
          tar -xaf cln-${CFG}.tar.bz2

      - name: Test
        env:
          COMPILER: ${{ matrix.COMPILER }}
          COMPAT: 1
          CFG: ${{ matrix.CFG }}
          SLOW_MACHINE: 1
          PYTEST_PAR: 4
          TEST_DEBUG: 1
          TEST_DB_PROVIDER: ${{ matrix.TEST_DB_PROVIDER }}
          TEST_NETWORK: ${{ matrix.TEST_NETWORK }}
          LIGHTNINGD_POSTGRES_NO_VACUUM: 1
        run: |
          env
          cat config.vars
          VALGRIND=0 sg wireshark "uv run eatmydata pytest tests/ -n ${PYTEST_PAR} ${PYTEST_OPTS}"
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-min-btc-support-${{ matrix.NAME }}
          path: report.xml
          if-no-files-found: ignore


  gather:
    # A dummy task that depends on the full matrix of tests, and
    # signals successful completion. Used for the PR status to pass
    # before merging.  Needs to run even if they failed!
    name: CI completion
    runs-on: ubuntu-24.04
    needs:
      - integration
      - check-units
      - integration-valgrind
      - integration-sanitizers
      - min-btc-support
      - check-downgrade
    if: ${{ always() }}
    steps:
      - name: Complete
        env:
          JOB_NAMES: "INTEGRATION CHECK_UNITS VALGRIND SANITIZERS BTC"
          INTEGRATION: ${{ needs.integration.result }}
          CHECK_UNITS: ${{ needs['check-units'].result }}
          VALGRIND: ${{ needs['integration-valgrind'].result }}
          SANITIZERS: ${{ needs['integration-sanitizers'].result }}
          DOCS: ${{ needs['update-docs-examples'].result }}
          BTC: ${{ needs['min-btc-support'].result }}
          CHECK_DOWNGRADE: ${{ needs['check-downgrade'].result }}
        run: |
          failed=""
          for name in $JOB_NAMES; do
            result="${!name}"
            echo "$name: $result"
            if [[ "$result" != "success" ]]; then
              failed="yes"
            fi
          done
          if [[ "$failed" == "yes" ]]; then
            echo "One or more required jobs failed"
            exit 1
          fi
