name: Build and Release

on:
  push:
    branches:
      - main
      - release/*
    tags:
      - 'v*'
  pull_request:
    branches:
      - main

env:
  GOPROXY: https://proxy.golang.org,direct
  GOPRIVATE: git.marketally.com
  GONOSUMDB: git.marketally.com
  GO_VERSION: "1.25"
  NODE_VERSION: "22"

jobs:
  # Lint job - must pass
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install pnpm
        run: npm install -g pnpm

      - name: Install dependencies
        run: make deps-frontend deps-backend

      - name: Run Go linter
        run: make lint-go

      - name: Run frontend linter
        run: make lint-frontend
        continue-on-error: true

  # Unit tests with SQLite (no external database needed)
  test-unit:
    name: Unit Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: false

      - name: Install dependencies
        run: go mod download

      - name: Run unit tests
        run: |
          # Skip tests that require external services (Redis, Elasticsearch, Meilisearch, Azure, SHA256 git)
          go test -tags="sqlite sqlite_unlock_notify" -race \
            -skip "TestRepoStatsIndex|TestRenderHelper|Sha256|SHA256|Redis|redis|Elasticsearch|Meilisearch|AzureBlob|TestLockAndDo|TestLocker|TestBaseRedis" \
            ./modules/... \
            ./services/...
        env:
          GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT: true

  # Integration tests with PostgreSQL
  test-pgsql:
    name: Integration Tests (PostgreSQL)
    runs-on: ubuntu-latest
    services:
      pgsql:
        image: postgres:15
        env:
          POSTGRES_DB: testgitea
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install pnpm
        run: npm install -g pnpm

      - name: Install dependencies
        run: make deps-frontend deps-backend

      - name: Build frontend
        run: make frontend

      - name: Generate bindata
        run: make generate
        env:
          TAGS: bindata

      - name: Build test binary
        run: |
          go build -tags="bindata sqlite sqlite_unlock_notify" -o gitea .

      - name: Generate test config
        run: |
          make generate-ini-pgsql
        env:
          TEST_PGSQL_HOST: localhost:5432
          TEST_PGSQL_DBNAME: testgitea
          TEST_PGSQL_USERNAME: postgres
          TEST_PGSQL_PASSWORD: postgres
          TEST_PGSQL_SCHEMA: gtestschema

      - name: Run PostgreSQL integration tests
        run: |
          make test-pgsql
        continue-on-error: true
        env:
          TEST_PGSQL_HOST: localhost:5432
          TEST_PGSQL_DBNAME: testgitea
          TEST_PGSQL_USERNAME: postgres
          TEST_PGSQL_PASSWORD: postgres
          TEST_PGSQL_SCHEMA: gtestschema
          GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT: true

  # Build job for binaries
  build:
    name: Build Binaries
    runs-on: ubuntu-latest
    needs: [lint]
    strategy:
      matrix:
        include:
          - goos: linux
            goarch: amd64
          - goos: linux
            goarch: arm64
          - goos: darwin
            goarch: amd64
          - goos: darwin
            goarch: arm64
          - goos: windows
            goarch: amd64
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install pnpm
        run: npm install -g pnpm

      - name: Install dependencies
        run: make deps-frontend deps-backend

      - name: Build frontend
        run: make frontend

      - name: Generate bindata
        run: make generate
        env:
          TAGS: bindata

      - name: Build binary
        env:
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}
          TAGS: bindata sqlite sqlite_unlock_notify
        run: |
          VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
          LDFLAGS="-X code.gitea.io/gitea/modules/setting.AppVer=${VERSION}"

          EXT=""
          if [ "$GOOS" = "windows" ]; then
            EXT=".exe"
          fi

          OUTPUT="gitea-${VERSION}-${GOOS}-${GOARCH}${EXT}"

          go build -v -trimpath -tags "${TAGS}" -ldflags "${LDFLAGS}" -o "dist/${OUTPUT}" .

          # Create checksum
          cd dist && sha256sum "${OUTPUT}" > "${OUTPUT}.sha256"

      - name: Upload to release
        if: startsWith(github.ref, 'refs/tags/v')
        run: |
          set -e
          VERSION=$(git describe --tags --always 2>/dev/null || echo "dev")
          echo "Uploading binaries for $VERSION"

          # Get or create release (with retry for race conditions)
          TAG="${{ github.ref_name }}"
          for i in 1 2 3; do
            EXISTING=$(curl -sf \
              -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
              "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/tags/$TAG" 2>/dev/null || echo "")

            if echo "$EXISTING" | grep -q '"id":[0-9]'; then
              RELEASE_ID=$(echo "$EXISTING" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
              echo "Found existing release: $RELEASE_ID"
              break
            else
              echo "Attempt $i: Creating release..."
              RESPONSE=$(curl -sf -X POST \
                -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
                -H "Content-Type: application/json" \
                -d '{"tag_name":"'"$TAG"'","name":"GitCaddy '"$TAG"'","body":"Official release.","draft":false,"prerelease":false}' \
                "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" 2>&1 || echo "")

              if echo "$RESPONSE" | grep -q '"id":[0-9]'; then
                RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2)
                echo "Created release: $RELEASE_ID"
                break
              fi
              echo "Failed to create, retrying in 5s..."
              sleep 5
            fi
          done

          if [ -z "$RELEASE_ID" ]; then
            echo "ERROR: Could not get or create release"
            exit 1
          fi

          # Upload files
          for file in dist/*; do
            if [ -f "$file" ]; then
              filename=$(basename "$file")
              echo "Uploading $filename..."

              UPLOAD_RESPONSE=$(curl -sf -X POST \
                -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
                -F "attachment=@$file" \
                "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$filename" 2>&1)

              if echo "$UPLOAD_RESPONSE" | grep -q '"id":[0-9]'; then
                echo "✓ Uploaded $filename successfully"
              else
                echo "✗ Failed to upload $filename: $UPLOAD_RESPONSE"
                exit 1
              fi
            fi
          done
          echo "All uploads complete!"
