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@v4

      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
              management:
                - 'management/**'

      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          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@v4
        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-3-dev libayatana-appindicator3-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 .

  test:
    name: "Client / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: [ '386','amd64' ]
    runs-on: ubuntu-22.04
    steps:
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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-3-dev libayatana-appindicator3-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
        run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} CI=true go test -tags devcert -exec 'sudo' -timeout 10m -p 1 $(go list ./... | grep -v -e /management -e /signal -e /relay)

  test_client_on_docker:
    name: "Client (Docker) / Unit"
    needs: [build-cache]
    runs-on: ubuntu-22.04
    steps:
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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 }}
        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} \
            golang:1.23-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 -v -timeout 10m -p 1 $(go list -buildvcs=false ./... | grep -v -e /management -e /signal -e /relay -e /client/ui -e /upload-server)
            '

  test_relay:
    name: "Relay / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: [ '386','amd64' ]
    runs-on: ubuntu-22.04
    steps:
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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' \
            -timeout 10m ./signal/...

  test_signal:
    name: "Signal / Unit"
    needs: [build-cache]
    strategy:
      fail-fast: false
      matrix:
        arch: [ '386','amd64' ]
    runs-on: ubuntu-22.04
    steps:
      - name: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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' \
            -timeout 10m ./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: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref)
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - 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 \
            -exec "sudo --preserve-env=CI,NETBIRD_STORE_ENGINE" \
            -timeout 20m ./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: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref)
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - 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 -run=^$ -bench=. \
          -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
          -timeout 20m ./management/...

  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: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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: matrix.store == 'mysql' && (github.repository == github.head.repo.full_name || !github.head_ref)
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USER }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - 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 \
          GIT_BRANCH=${{ github.ref_name }} \
          go test -tags=benchmark \
            -run=^$ \
            -bench=. \
            -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE,GIT_BRANCH,GITHUB_RUN_ID' \
            -timeout 20m ./management/...

  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: Install Go
        uses: actions/setup-go@v5
        with:
          go-version: "1.23.x"
          cache: false

      - name: Checkout code
        uses: actions/checkout@v4

      - 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@v4
        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 \
          -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' \
          -timeout 20m ./management/...
