name: Build and Release

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

env:
  GOPROXY: https://proxy.golang.org,direct
  GO_VERSION: "1.25"
  NODE_VERSION: "22"

jobs:
  # Lint and test job
  lint-test:
    name: Lint and Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check for existing Go
        id: go-check
        run: |
          if command -v go &> /dev/null; then
            GO_VER=$(go version | grep -oP 'go\d+\.\d+' | head -1)
            echo "version=$GO_VER" >> $GITHUB_OUTPUT
            echo "Found Go: $(go version)"
          fi

      - name: Setup Go
        if: steps.go-check.outputs.version == ''
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Check for existing Node.js
        id: node-check
        run: |
          if command -v node &> /dev/null; then
            NODE_VER=$(node --version)
            echo "version=$NODE_VER" >> $GITHUB_OUTPUT
            echo "Found Node.js: $NODE_VER"
          fi

      - name: Setup Node.js
        if: steps.node-check.outputs.version == ''
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Check for existing pnpm
        id: pnpm-check
        run: |
          if command -v pnpm &> /dev/null; then
            PNPM_VER=$(pnpm --version)
            echo "version=$PNPM_VER" >> $GITHUB_OUTPUT
            echo "Found pnpm: $PNPM_VER"
          fi

      - name: Install pnpm
        if: steps.pnpm-check.outputs.version == ''
        run: npm install -g pnpm

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

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

      - name: Run tests
        run: make test
        continue-on-error: true

  # Build job for binaries
  build:
    name: Build Binaries
    runs-on: ubuntu-latest
    needs: lint-test
    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: Check for existing Go
        id: go-check
        run: |
          if command -v go &> /dev/null; then
            GO_VER=$(go version | grep -oP 'go\d+\.\d+' | head -1)
            echo "version=$GO_VER" >> $GITHUB_OUTPUT
            echo "Found Go: $(go version)"
          fi

      - name: Setup Go
        if: steps.go-check.outputs.version == ''
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Check for existing Node.js
        id: node-check
        run: |
          if command -v node &> /dev/null; then
            NODE_VER=$(node --version)
            echo "version=$NODE_VER" >> $GITHUB_OUTPUT
            echo "Found Node.js: $NODE_VER"
          fi

      - name: Setup Node.js
        if: steps.node-check.outputs.version == ''
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Check for existing pnpm
        id: pnpm-check
        run: |
          if command -v pnpm &> /dev/null; then
            PNPM_VER=$(pnpm --version)
            echo "version=$PNPM_VER" >> $GITHUB_OUTPUT
            echo "Found pnpm: $PNPM_VER"
          fi

      - name: Install pnpm
        if: steps.pnpm-check.outputs.version == ''
        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

      - 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 artifacts
        uses: actions/upload-artifact@v4
        with:
          name: gitea-${{ matrix.goos }}-${{ matrix.goarch }}
          path: dist/gitea-*
          retention-days: 7

  # Docker build job
  docker:
    name: Build Docker Image
    runs-on: ubuntu-latest
    needs: lint-test
    if: github.event_name == 'push'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Get version
        id: version
        run: |
          if [[ $GITHUB_REF == refs/tags/* ]]; then
            VERSION=${GITHUB_REF#refs/tags/}
          else
            VERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")
          fi
          echo "version=${VERSION}" >> $GITHUB_OUTPUT

      - name: Build Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: false
          tags: |
            gitea/gitea:${{ steps.version.outputs.version }}
            gitea/gitea:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # Release job - only runs on tags
  release:
    name: Create Release
    runs-on: ubuntu-latest
    needs: build
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true

      - name: List artifacts
        run: ls -la dist/

      - name: Get version
        id: version
        run: |
          VERSION=${GITHUB_REF#refs/tags/}
          echo "version=${VERSION}" >> $GITHUB_OUTPUT

      - name: Generate changelog
        id: changelog
        run: |
          # Get commits since last tag
          PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
          if [ -n "$PREV_TAG" ]; then
            CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..HEAD)
          else
            CHANGELOG=$(git log --pretty=format:"- %s (%h)" -20)
          fi

          # Escape for GitHub Actions
          CHANGELOG="${CHANGELOG//'%'/'%25'}"
          CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
          CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"

          echo "changelog=${CHANGELOG}" >> $GITHUB_OUTPUT

      - name: Create Release
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          IS_PRERELEASE="false"
          if [[ "$VERSION" == *"-rc"* ]] || [[ "$VERSION" == *"-beta"* ]] || [[ "$VERSION" == *"-alpha"* ]]; then
            IS_PRERELEASE="true"
          fi

          RELEASE_BODY="## Changes in ${VERSION}

          ${{ steps.changelog.outputs.changelog }}

          ## Downloads

          | Platform | Architecture | Download |
          |----------|--------------|----------|
          | Linux | amd64 | gitea-${VERSION}-linux-amd64 |
          | Linux | arm64 | gitea-${VERSION}-linux-arm64 |
          | macOS | amd64 | gitea-${VERSION}-darwin-amd64 |
          | macOS | arm64 | gitea-${VERSION}-darwin-arm64 |
          | Windows | amd64 | gitea-${VERSION}-windows-amd64.exe |

          ## Checksums

          SHA256 checksums are provided in .sha256 files alongside each binary."

          # Create release via Gitea API
          RELEASE_DATA=$(jq -n \
            --arg tag "$VERSION" \
            --arg name "Gitea $VERSION" \
            --arg body "$RELEASE_BODY" \
            --argjson prerelease "$IS_PRERELEASE" \
            '{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: $prerelease}')

          RESPONSE=$(curl -s -X POST \
            -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d "$RELEASE_DATA" \
            "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases")

          echo "Release response: $RESPONSE"
          RELEASE_ID=$(echo "$RESPONSE" | jq -r '.id')
          echo "Created release with ID: $RELEASE_ID"

          # Upload assets
          for file in dist/*; do
            if [ -f "$file" ]; then
              filename=$(basename "$file")
              echo "Uploading $filename..."
              curl -s -X POST \
                -H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
                -H "Content-Type: multipart/form-data" \
                -F "attachment=@$file" \
                "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/$RELEASE_ID/assets?name=$filename"
              echo "Uploaded $filename"
            fi
          done
