name: CI on: push: branches: - "master" pull_request: branches: - "*" merge_group: branches: - "master" permissions: # Required to manage and delete caches. actions: write # Default permission for checking out code. contents: read concurrency: # Cancel any previous workflows if they are from a PR or push. group: ${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true defaults: run: shell: bash env: BITCOIN_VERSION: "29" # TRANCHES defines the number of tranches used in the itests. TRANCHES: 16 # SMALL_TRANCHES defines the number of tranches used in the less stable itest # builds # # TODO(yy): remove this value and use TRANCHES. SMALL_TRANCHES: 8 # If you change this please also update GO_VERSION in Makefile (then run # `make lint` to see where else it needs to be updated as well). GO_VERSION: 1.25.3 jobs: static-checks: name: Static Checks runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v5 with: # Needed for some checks. fetch-depth: 0 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: Setup Go ${{ env.GO_VERSION }} uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' use-build-cache: 'no' ######################## # sample configuration check ######################## - name: Check default values in sample-lnd.conf file run: make sample-conf-check ######################## # Check code and RPC format ######################## - name: Check code format run: make fmt-check - name: Check go modules tidiness run: make tidy-module-check - name: Lint proto files run: make protolint ######################## # SQLC code gen check ######################## - name: Docker image cache uses: satackey/action-docker-layer-caching@v0.0.11 # Ignore the failure of a step and avoid terminating the job. continue-on-error: true - name: Check SQL models run: make sqlc-check ######################## # RPC and mobile compilation check ######################## - name: Check RPC format run: make rpc-check - name: Check JSON/WASM stub compilation run: make rpc-js-compile - name: Check mobile RPC bindings run: make mobile-rpc - name: Check mobile specific code run: go build --tags="mobile" ./mobile ######################## # check commits ######################## check-commits: if: github.event_name == 'pull_request' name: Check commits runs-on: ubuntu-latest steps: - name: git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: setup go ${{ env.GO_VERSION }} uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' # Use the same cache from unit test job to save time. key-prefix: unit-test - name: fetch and rebase on ${{ github.base_ref }} uses: ./.github/actions/rebase - name: check commits run: scripts/check-each-commit.sh upstream/${{ github.base_ref }} ######################## # lint code ######################## lint: name: Lint code runs-on: ubuntu-latest steps: - name: git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: setup go ${{ env.GO_VERSION }} uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' # Use the same cache from unit test job to save time. key-prefix: unit-test - name: lint run: GOGC=50 make lint ######################## # cross compilation ######################## cross-compile: name: Cross compilation runs-on: ubuntu-latest strategy: fail-fast: true matrix: # Please keep this list in sync with make/release_flags.mk! include: - name: i386 sys: freebsd-386 linux-386 windows-386 - name: amd64 sys: darwin-amd64 freebsd-amd64 linux-amd64 netbsd-amd64 openbsd-amd64 windows-amd64 - name: arm sys: darwin-arm64 freebsd-arm linux-armv6 linux-armv7 linux-arm64 windows-arm steps: - name: Git checkout uses: actions/checkout@v5 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: Setup go ${{ env.GO_VERSION }} uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: cross-compile use-build-cache: 'no' - name: Build release for all architectures run: make release sys="${{ matrix.sys }}" ######################## # run unit tests ######################## unit-test: name: Run unit tests runs-on: ubuntu-latest strategy: # Allow other tests in the matrix to continue if one fails. fail-fast: false matrix: unit_type: - unit-cover - unit tags="kvdb_etcd" - unit tags="kvdb_postgres" - unit tags="kvdb_sqlite" - unit tags="test_db_sqlite" - unit tags="test_db_postgres" - unit-race - unit-module steps: - name: Git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: Fetch and rebase on ${{ github.base_ref }} if: github.event_name == 'pull_request' uses: ./.github/actions/rebase - name: Git checkout fuzzing seeds uses: actions/checkout@v5 with: repository: lightninglabs/lnd-fuzz path: lnd-fuzz - name: Rsync fuzzing seeds run: rsync -a --ignore-existing lnd-fuzz/ ./ - name: Setup go ${{ env.GO_VERSION }} uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: unit-test - name: Install bitcoind run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION - name: Run ${{ matrix.unit_type }} run: make ${{ matrix.unit_type }} - name: Clean coverage run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt if: matrix.unit_type == 'unit-cover' - name: Send coverage uses: coverallsapp/github-action@v2 if: matrix.unit_type == 'unit-cover' continue-on-error: true with: file: coverage-norpc.txt flag-name: 'unit' format: 'golang' parallel: true ######################## # run integration tests with TRANCHES ######################## basic-integration-test: name: Run basic itests runs-on: ubuntu-latest strategy: # Allow other tests in the matrix to continue if one fails. fail-fast: false matrix: include: - name: btcd args: backend=btcd cover=1 - name: bitcoind args: backend=bitcoind cover=1 - name: bitcoind-notxindex args: backend="bitcoind notxindex" - name: neutrino args: backend=neutrino cover=1 steps: - name: Git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check for no-itest label id: check-label uses: ./.github/actions/check-label with: label: 'no-itest' skip-message: "Tests auto-passed due to 'no-itest' label" - name: Clean up runner space if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/cleanup-space - name: Fetch and rebase on ${{ github.base_ref }} if: github.event_name == 'pull_request' && steps.check-label.outputs.skip != 'true' uses: ./.github/actions/rebase - name: Setup go ${{ env.GO_VERSION }} if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: integration-test - name: Install bitcoind if: steps.check-label.outputs.skip != 'true' run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION - name: Run ${{ matrix.name }} if: steps.check-label.outputs.skip != 'true' run: make itest-parallel tranches=${{ env.TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }} - name: Clean coverage run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt if: ${{ contains(matrix.args, 'cover=1') && steps.check-label.outputs.skip != 'true' }} - name: Send coverage if: ${{ contains(matrix.args, 'cover=1') && steps.check-label.outputs.skip != 'true' }} continue-on-error: true uses: coverallsapp/github-action@v2 with: file: coverage-norpc.txt flag-name: 'itest-${{ matrix.name }}' format: 'golang' parallel: true - name: Zip log files on failure if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} timeout-minutes: 5 # timeout after 5 minute run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log itest/postgres.log - name: Upload log files on failure uses: actions/upload-artifact@v4 if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} with: name: logs-itest-${{ matrix.name }} path: logs-itest-${{ matrix.name }}.zip retention-days: 5 ######################## # run integration tests with SMALL_TRANCHES ######################## integration-test: name: Run itests runs-on: ubuntu-latest strategy: # Allow other tests in the matrix to continue if one fails. fail-fast: false matrix: include: - name: bitcoind-rpcpolling args: backend="bitcoind rpcpolling" - name: bitcoind-etcd args: backend=bitcoind dbbackend=etcd - name: bitcoind-sqlite args: backend=bitcoind dbbackend=sqlite - name: bitcoind-sqlite-nativesql args: backend=bitcoind dbbackend=sqlite nativesql=true - name: bitcoind-sqlite=nativesql-experiment args: backend=bitcoind dbbackend=sqlite nativesql=true tags=test_native_sql - name: bitcoind-postgres args: backend=bitcoind dbbackend=postgres - name: bitcoind-postgres-nativesql args: backend=bitcoind dbbackend=postgres nativesql=true - name: bitcoind-postgres-nativesql-experiment args: backend=bitcoind dbbackend=postgres nativesql=true tags=test_native_sql steps: - name: Git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check for no-itest label id: check-label uses: ./.github/actions/check-label with: label: 'no-itest' skip-message: "Tests auto-passed due to 'no-itest' label" - name: Clean up runner space if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/cleanup-space - name: Fetch and rebase on ${{ github.base_ref }} if: github.event_name == 'pull_request' && steps.check-label.outputs.skip != 'true' uses: ./.github/actions/rebase - name: Setup go ${{ env.GO_VERSION }} if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: integration-test - name: Install bitcoind if: steps.check-label.outputs.skip != 'true' run: ./scripts/install_bitcoind.sh $BITCOIN_VERSION - name: Run ${{ matrix.name }} if: steps.check-label.outputs.skip != 'true' run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} ${{ matrix.args }} shuffleseed=${{ github.run_id }}${{ strategy.job-index }} - name: Clean coverage run: grep -Ev '(\.pb\.go|\.pb\.json\.go|\.pb\.gw\.go)' coverage.txt > coverage-norpc.txt if: ${{ contains(matrix.args, 'cover=1') && steps.check-label.outputs.skip != 'true' }} - name: Send coverage if: ${{ contains(matrix.args, 'cover=1') && steps.check-label.outputs.skip != 'true' }} continue-on-error: true uses: coverallsapp/github-action@v2 with: file: coverage-norpc.txt flag-name: 'itest-${{ matrix.name }}' format: 'golang' parallel: true - name: Zip log files on failure if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} timeout-minutes: 5 # timeout after 5 minute run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log - name: Upload log files on failure uses: actions/upload-artifact@v4 if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} with: name: logs-itest-${{ matrix.name }} path: logs-itest-${{ matrix.name }}.zip retention-days: 5 ######################## # run windows integration test ######################## windows-integration-test: name: Run windows itest runs-on: windows-latest steps: - name: Git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check for no-itest label id: check-label uses: ./.github/actions/check-label with: label: 'no-itest' skip-message: "Tests auto-passed due to 'no-itest' label" - name: Fetch and rebase on ${{ github.base_ref }} if: github.event_name == 'pull_request' && steps.check-label.outputs.skip != 'true' uses: ./.github/actions/rebase - name: Setup go ${{ env.GO_VERSION }} if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: integration-test - name: Run itest if: steps.check-label.outputs.skip != 'true' run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} windows=1 shuffleseed=${{ github.run_id }} - name: Kill any remaining lnd processes if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} shell: powershell run: taskkill /IM lnd-itest.exe /T /F - name: Zip log files on failure if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} timeout-minutes: 5 # timeout after 5 minute run: 7z a logs-itest-windows.zip itest/**/*.log - name: Upload log files on failure uses: actions/upload-artifact@v4 if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} with: name: logs-itest-windows path: logs-itest-windows.zip retention-days: 5 ######################## # run macOS integration test ######################## macos-integration-test: name: Run macOS itest runs-on: macos-14 steps: - name: Git checkout uses: actions/checkout@v5 with: fetch-depth: 0 - name: Check for no-itest label id: check-label uses: ./.github/actions/check-label with: label: 'no-itest' skip-message: "Tests auto-passed due to 'no-itest' label" - name: Fetch and rebase on ${{ github.base_ref }} if: github.event_name == 'pull_request' && steps.check-label.outputs.skip != 'true' uses: ./.github/actions/rebase - name: Setup go ${{ env.GO_VERSION }} if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/setup-go with: go-version: '${{ env.GO_VERSION }}' key-prefix: integration-test - name: Run itest if: steps.check-label.outputs.skip != 'true' run: make itest-parallel tranches=${{ env.SMALL_TRANCHES }} shuffleseed=${{ github.run_id }} - name: Zip log files on failure if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} timeout-minutes: 5 # timeout after 5 minute run: 7z a logs-itest-macos.zip itest/**/*.log - name: Upload log files on failure uses: actions/upload-artifact@v4 if: ${{ failure() && steps.check-label.outputs.skip != 'true' }} with: name: logs-itest-macos path: logs-itest-macos.zip retention-days: 5 ######################## # check pinned dependencies ######################## dep-pin: name: Check pinned dependencies runs-on: ubuntu-latest strategy: # Allow other tests in the matrix to continue if one fails. fail-fast: false matrix: pinned_dep: - google.golang.org/grpc v1.59.0 - github.com/golang/protobuf v1.5.4 steps: - name: Git checkout uses: actions/checkout@v5 - name: Clean up runner space uses: ./.github/actions/cleanup-space - name: Ensure dependencies at correct version run: if ! grep -q "${{ matrix.pinned_dep }}" go.mod; then echo dependency ${{ matrix.pinned_dep }} should not be altered ; exit 1 ; fi ######################## # check PR updates release notes ######################## milestone-check: name: Check release notes updated runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v5 - name: Check for no-changelog label id: check-label uses: ./.github/actions/check-label with: label: 'no-changelog' skip-message: "Changelog check auto-passed due to 'no-changelog' label" - name: Clean up runner space if: steps.check-label.outputs.skip != 'true' uses: ./.github/actions/cleanup-space - name: Release notes check if: steps.check-label.outputs.skip != 'true' run: scripts/check-release-notes.sh ######################## # Backwards Compatibility Test ######################## backwards-compatibility-test: name: Backwards compatibility test runs-on: ubuntu-latest steps: - name: Git checkout uses: actions/checkout@v5 - name: 🐳 Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: 🛡️ Backwards compatibility test run: make backwards-compat-test ######################################### # Auto Cache Cleanup on Pull Requests ######################################### auto-cleanup-cache: name: Cache Cleanup runs-on: ubuntu-latest # This condition checks for pull requests from authors with write access. if: >- contains('OWNER, MEMBER, COLLABORATOR', github.event.pull_request.author_association) steps: - name: Checkout repository uses: actions/checkout@v5 - name: Delete caches older than 12 hours continue-on-error: true env: # GITHUB_TOKEN is required for the gh CLI. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo "Finding caches not used in the last 12 hours..." # Get the current time and the cutoff time (12 hours ago) in Unix # timestamp format. cutoff_timestamp=$(date -d "12 hours ago" +%s) # Use gh and jq to parse caches. Delete any cache last accessed # before the cutoff time. gh cache list --json key,lastAccessedAt | jq -r '.[] | select(.lastAccessedAt != null) | "\(.lastAccessedAt) \(.key)"' | while read -r last_accessed_at key; do last_accessed_timestamp=$(date -d "$last_accessed_at" +%s) if (( last_accessed_timestamp < cutoff_timestamp )); then echo "Deleting old cache. Key: $key, Last Used: $last_accessed_at" gh cache delete "$key" fi done # Notify about the completion of all coverage collecting jobs. finish: name: Send coverage report if: ${{ !cancelled() }} needs: [unit-test, basic-integration-test] runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Send coverage uses: coverallsapp/github-action@v2 continue-on-error: true with: parallel-finished: true