name: Linux

on:
  push:
    branches:
      - main
  pull_request:

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

jobs:
  build-cache:
    name: "Build Cache"
    runs-on: ubuntu-22.04
    outputs:
      management: ${{ steps.filter.outputs.management }}
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
        id: filter
        with:
          filters: |
            management:
              - 'management/**'

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        id: cache
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}

      - name: Install dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: sudo apt update && sudo apt install -y -q libgtk-4-dev libwebkitgtk-6.0-dev libsoup-3.0-dev libgl1-mesa-dev xorg-dev gcc-multilib libpcap-dev

      - name: Install 32-bit libpcap
        if: steps.cache.outputs.cache-hit != 'true'
        run: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt-get install -y libpcap0.8-dev:i386

      - name: Build client
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: client
        run: CGO_ENABLED=1 go build .

      - name: Build client 386
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: client
        run: CGO_ENABLED=1 GOARCH=386 go build -o client-386 .

      - name: Build management
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: management
        run: CGO_ENABLED=1 go build .

      - name: Build management 386
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: management
        run: CGO_ENABLED=1 GOARCH=386 go build -o management-386 .

      - name: Build signal
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: signal
        run: CGO_ENABLED=1 go build .

      - name: Build signal 386
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: signal
        run: CGO_ENABLED=1 GOARCH=386 go build -o signal-386 .

      - name: Build relay
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: relay
        run: CGO_ENABLED=1 go build .

      - name: Build relay 386
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: relay
        run: CGO_ENABLED=1 GOARCH=386 go build -o relay-386 .

      - name: Build combined
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: combined
        run: CGO_ENABLED=1 go build .

      - name: Build combined 386
        if: steps.cache.outputs.cache-hit != 'true'
        working-directory: combined
        run: CGO_ENABLED=1 GOARCH=386 go build -o combined-386 .

  test:
    name: "Client / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: ["386", "amd64"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install dependencies
        run: sudo apt update && sudo apt install -y -q libgtk-4-dev libwebkitgtk-6.0-dev libsoup-3.0-dev libgl1-mesa-dev xorg-dev gcc-multilib libpcap-dev

      - name: Install 32-bit libpcap
        if: matrix.arch == '386'
        run: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt-get install -y libpcap0.8-dev:i386

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Test
        # Exclude client/ui: its main.go uses //go:embed all:frontend/dist,
        # which fails to compile until the frontend has been built. The Wails UI
        # has no Go-side unit tests, and its release pipeline runs `pnpm build`
        # before goreleaser.
        # `go list -e` lets the listing succeed even though the embed fails to
        # resolve; the grep then drops the broken package by path. Without -e,
        # go list aborts with empty stdout and `go test` falls back to the repo
        # root, which has no Go files.
        run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} CI=true go test -coverprofile=coverage.txt -tags 'devcert privileged' -exec 'sudo --preserve-env=CI,CGO_ENABLED' -timeout 10m -p 1 $(go list -e ./... | grep -v -e /management -e /signal -e /relay -e /proxy -e /combined -e /client/ui -e /client/testutil/privileged)

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: unit,client

  test_client_on_docker:
    name: "Client (Docker) / Unit"
    needs: [build-cache]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        id: go-env
        run: |
          echo "cache_dir=$(go env GOCACHE)" >> $GITHUB_OUTPUT
          echo "modcache_dir=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        id: cache-restore
        with:
          path: |
            ${{ steps.go-env.outputs.cache_dir }}
            ${{ steps.go-env.outputs.modcache_dir }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Run tests in container
        env:
          HOST_GOCACHE: ${{ steps.go-env.outputs.cache_dir }}
          HOST_GOMODCACHE: ${{ steps.go-env.outputs.modcache_dir }}
          CONTAINER: "true"
        run: |
          CONTAINER_GOCACHE="/root/.cache/go-build"
          CONTAINER_GOMODCACHE="/go/pkg/mod"

          docker run --rm \
            --cap-add=NET_ADMIN \
            --privileged \
            -v $PWD:/app \
            -w /app \
            -v "${HOST_GOCACHE}:${CONTAINER_GOCACHE}" \
            -v "${HOST_GOMODCACHE}:${CONTAINER_GOMODCACHE}" \
            -e CGO_ENABLED=1 \
            -e CI=true \
            -e DOCKER_CI=true \
            -e GOARCH=${GOARCH_TARGET} \
            -e GOCACHE=${CONTAINER_GOCACHE} \
            -e GOMODCACHE=${CONTAINER_GOMODCACHE} \
            -e CONTAINER=${CONTAINER} \
            golang:1.25-alpine \
            sh -c ' \
              apk update; apk add --no-cache \
                ca-certificates iptables ip6tables dbus dbus-dev libpcap-dev build-base; \
              go test -buildvcs=false -tags "devcert privileged" -v -timeout 10m -p 1 $(go list -e -buildvcs=false ./... | grep -v -e /management -e /signal -e /relay -e /proxy -e /combined -e /client/ui -e /upload-server -e /client/testutil/privileged)
            '

  test_relay:
    name: "Relay / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        include:
          - arch: "386"
            raceFlag: ""
          - arch: "amd64"
            raceFlag: "-race"
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Install dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: sudo apt update && sudo apt install -y gcc-multilib g++-multilib libc6-dev-i386

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          go test ${{ matrix.raceFlag }} \
            -exec 'sudo' -coverprofile=coverage.txt \
            -timeout 10m -p 1 ./relay/... ./shared/relay/...

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: unit,relay

  test_proxy:
    name: "Proxy / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: ["386", "amd64"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Install dependencies
        run: sudo apt update && sudo apt install -y gcc-multilib g++-multilib libc6-dev-i386

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          go test -timeout 10m -p 1 -coverprofile=coverage.txt ./proxy/...

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: unit,proxy

  test_signal:
    name: "Signal / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: ["386", "amd64"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Install dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: sudo apt update && sudo apt install -y gcc-multilib g++-multilib libc6-dev-i386

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          go test \
            -exec 'sudo' -coverprofile=coverage.txt \
            -timeout 10m ./signal/... ./shared/signal/...

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: unit,signal

  test_management:
    name: "Management / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: ["amd64"]
        store: ["sqlite", "postgres", "mysql"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Login to Docker hub
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: docker login for root user
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        env:
          DOCKER_USER: ${{ secrets.DOCKER_USER }}
          DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
        run: echo "$DOCKER_TOKEN" | sudo docker login --username "$DOCKER_USER" --password-stdin

      - name: download mysql image
        if: matrix.store == 'mysql'
        run: docker pull mlsmaycon/warmed-mysql:8

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          NETBIRD_STORE_ENGINE=${{ matrix.store }} \
            CI=true \
          go test -tags=devcert -coverprofile=coverage.txt \
            -exec "sudo --preserve-env=CI,NETBIRD_STORE_ENGINE" \
            -timeout 20m ./management/... ./shared/management/...

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: unit,management

  benchmark:
    name: "Management / Benchmark"
    needs: [build-cache]
    if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
    strategy:
      fail-fast: false
      matrix:
        arch: ["amd64"]
        store: ["sqlite", "postgres"]
    runs-on: ubuntu-22.04
    steps:
      - name: Create Docker network
        run: docker network create promnet

      - name: Start Prometheus Pushgateway
        run: docker run -d --name pushgateway --network promnet -p 9091:9091 prom/pushgateway

      - name: Start Prometheus (for Pushgateway forwarding)
        run: |
          echo '
          global:
            scrape_interval: 15s
          scrape_configs:
            - job_name: "pushgateway"
              static_configs:
                - targets: ["pushgateway:9091"]
          remote_write:
            - url: ${{ secrets.GRAFANA_URL }}
              basic_auth:
                username: ${{ secrets.GRAFANA_USER }}
                password: ${{ secrets.GRAFANA_API_KEY }}
          ' > prometheus.yml

          docker run -d --name prometheus --network promnet \
            -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml \
            -p 9090:9090 \
            prom/prometheus

      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Login to Docker hub
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: docker login for root user
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        env:
          DOCKER_USER: ${{ secrets.DOCKER_USER }}
          DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
        run: echo "$DOCKER_TOKEN" | sudo docker login --username "$DOCKER_USER" --password-stdin

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          NETBIRD_STORE_ENGINE=${{ matrix.store }} \
          CI=true \
          go test -tags devcert -run=^$ -bench=. \
          -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE,GIT_BRANCH,GITHUB_RUN_ID' \
          -timeout 20m ./management/... ./shared/management/... $(go list ./management/... ./shared/management/... | grep -v -e /management/server/http)
        env:
          GIT_BRANCH: ${{ github.ref_name }}

  api_benchmark:
    name: "Management / Benchmark (API)"
    needs: [build-cache]
    if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
    strategy:
      fail-fast: false
      matrix:
        arch: ["amd64"]
        store: ["sqlite", "postgres"]
    runs-on: ubuntu-22.04
    steps:
      - name: Create Docker network
        run: docker network create promnet

      - name: Start Prometheus Pushgateway
        run: docker run -d --name pushgateway --network promnet -p 9091:9091 prom/pushgateway

      - name: Start Prometheus (for Pushgateway forwarding)
        run: |
          echo '
          global:
            scrape_interval: 15s
          scrape_configs:
            - job_name: "pushgateway"
              static_configs:
                - targets: ["pushgateway:9091"]
          remote_write:
            - url: ${{ secrets.GRAFANA_URL }}
              basic_auth:
                username: ${{ secrets.GRAFANA_USER }}
                password: ${{ secrets.GRAFANA_API_KEY }}
          ' > prometheus.yml

          docker run -d --name prometheus --network promnet \
            -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml \
            -p 9090:9090 \
            prom/prometheus

      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Login to Docker hub
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: docker login for root user
        if: github.event.pull_request && github.event.pull_request.head.repo && github.event.pull_request.head.repo.full_name == '' || github.repository == github.event.pull_request.head.repo.full_name || !github.head_ref
        env:
          DOCKER_USER: ${{ secrets.DOCKER_USER }}
          DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
        run: echo "$DOCKER_TOKEN" | sudo docker login --username "$DOCKER_USER" --password-stdin

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          NETBIRD_STORE_ENGINE=${{ matrix.store }} \
          CI=true \
          go test -tags=benchmark \
            -run=^$ \
            -bench=. \
            -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE,GIT_BRANCH,GITHUB_RUN_ID' \
            -timeout 20m ./management/server/http/...
        env:
          GIT_BRANCH: ${{ github.ref_name }}

  api_integration_test:
    name: "Management / Integration"
    needs: [build-cache]
    if: ${{ needs.build-cache.outputs.management == 'true' || github.event_name != 'pull_request' }}
    strategy:
      fail-fast: false
      matrix:
        arch: ["amd64"]
        store: ["sqlite", "postgres"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout code
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false

      - name: Install Go
        uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
        with:
          go-version-file: "go.mod"
          cache: false

      - name: Get Go environment
        run: |
          echo "cache=$(go env GOCACHE)" >> $GITHUB_ENV
          echo "modcache=$(go env GOMODCACHE)" >> $GITHUB_ENV

      - name: Cache Go modules
        uses: actions/cache/restore@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
        with:
          path: |
            ${{ env.cache }}
            ${{ env.modcache }}
          key: ${{ runner.os }}-gotest-cache-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-gotest-cache-

      - name: Install modules
        run: go mod tidy

      - name: check git status
        run: git --no-pager diff --exit-code

      - name: Test
        run: |
          CGO_ENABLED=1 GOARCH=${{ matrix.arch }} \
          NETBIRD_STORE_ENGINE=${{ matrix.store }} \
          CI=true \
          go test -tags=integration -coverprofile=coverage.txt \
          -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
          -timeout 20m ./management/server/http/...

      - name: Upload coverage reports to Codecov
        if: matrix.arch == 'amd64'
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f #v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: netbirdio/netbird
          flags: integration,management
