name: Docker Release

on:
  workflow_dispatch:
  push:
    tags:
      - "v*"

permissions:
  contents: write
  packages: write
  pull-requests: read
  issues: read

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

env:
  REGISTRY: ghcr.io

jobs:
  checks:
    name: Check web and api
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.28.2

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm
          cache-dependency-path: pnpm-lock.yaml

      - name: Install web dependencies
        run: pnpm install --frozen-lockfile --filter newszxcn-email-web...

      - name: Check shadcn/ui usage
        run: pnpm --dir apps/web run check:shadcn

      - name: Build web
        run: pnpm --dir apps/web run build

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version-file: apps/api/go.mod
          cache-dependency-path: apps/api/go.sum

      - name: Test api
        working-directory: apps/api
        run: go test ./...

  release:
    name: Resolve release tag
    runs-on: ubuntu-latest
    needs: checks
    outputs:
      tag: ${{ steps.release.outputs.tag }}
      version: ${{ steps.release.outputs.version }}
      release_url: ${{ steps.release.outputs.release_url }}
    steps:
      - name: Resolve tag version
        id: release
        shell: bash
        run: |
          tag="${GITHUB_REF_NAME}"
          version="${tag#v}"
          version="${version#.}"
          version="${version#-}"
          release_url="https://github.com/${GITHUB_REPOSITORY}/releases/tag/${tag}"
          {
            echo "tag=${tag}"
            echo "version=${version}"
            echo "release_url=${release_url}"
          } >> "$GITHUB_OUTPUT"
          echo "::notice::Docker release tag: ${tag}"

  docker:
    name: Build and publish ${{ matrix.name }}
    runs-on: ubuntu-latest
    needs: release
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: all-in-one
            suffix: ""
            context: .
            file: ./deploy/all-in-one/Dockerfile
            platforms: linux/amd64,linux/arm64
          - name: api
            suffix: -api
            context: .
            file: ./deploy/api.Dockerfile
            platforms: linux/amd64,linux/arm64
          - name: web
            suffix: -web
            context: .
            file: ./deploy/web.Dockerfile
            platforms: linux/amd64,linux/arm64
          - name: postfix
            suffix: -postfix
            context: ./deploy/postfix
            file: ./deploy/postfix/Dockerfile
            platforms: linux/amd64
          - name: dovecot
            suffix: -dovecot
            context: ./deploy/dovecot
            file: ./deploy/dovecot/Dockerfile
            platforms: linux/amd64
          - name: rspamd
            suffix: -rspamd
            context: ./deploy/rspamd
            file: ./deploy/rspamd/Dockerfile
            platforms: linux/amd64

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

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

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

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GHCR_TOKEN || secrets.GITHUB_TOKEN }}

      - name: Prepare image name
        id: image
        shell: bash
        run: |
          image="${REGISTRY}/${GITHUB_REPOSITORY}${{ matrix.suffix }}"
          {
            echo "name=${image,,}"
            echo "tag=${{ needs.release.outputs.tag }}"
            echo "version=${{ needs.release.outputs.version }}"
          } >> "$GITHUB_OUTPUT"

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ steps.image.outputs.name }}
          tags: |
            type=raw,value=${{ steps.image.outputs.tag }}
            type=raw,value=${{ steps.image.outputs.version }}
            type=raw,value=latest
            type=sha,prefix=sha-
          labels: |
            org.opencontainers.image.title=NewSzxcn Email ${{ matrix.name }}
            org.opencontainers.image.version=${{ steps.image.outputs.tag }}

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: ${{ matrix.context }}
          file: ${{ matrix.file }}
          platforms: ${{ matrix.platforms }}
          push: true
          build-args: |
            APP_VERSION=${{ needs.release.outputs.tag }}
            APP_COMMIT=${{ github.sha }}
            VITE_APP_VERSION=${{ needs.release.outputs.tag }}
            VITE_RELEASE_URL=${{ needs.release.outputs.release_url }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  github-release:
    name: Create GitHub release
    runs-on: ubuntu-latest
    needs:
      - release
      - docker
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Generate release notes
        env:
          GH_TOKEN: ${{ github.token }}
        shell: bash
        run: |
          tag="${{ needs.release.outputs.tag }}"
          version="${{ needs.release.outputs.version }}"
          repo="${GITHUB_REPOSITORY}"
          repo_url="https://github.com/${repo}"
          image_base="${REGISTRY}/${repo}"
          image_base="${image_base,,}"
          current_commit="$(git rev-list -n 1 "${tag}")"
          previous_tag="$(git describe --tags --abbrev=0 "${current_commit}^" 2>/dev/null || true)"
          generate_args=(-f "tag_name=${tag}")
          if [[ -n "${previous_tag}" ]]; then
            generate_args+=(-f "previous_tag_name=${previous_tag}")
          fi

          if ! gh api -X POST "repos/${repo}/releases/generate-notes" "${generate_args[@]}" --jq '.body' > generated-release-notes.md; then
            {
              echo "GitHub 自动生成更新日志失败，已回退到提交列表。"
              echo
              if [[ -n "${previous_tag}" ]]; then
                git log --reverse --pretty=format:"- %s ([%h](${repo_url}/commit/%H))" "${previous_tag}..${tag}"
                echo
                echo
                echo "完整更新日志: [${previous_tag}...${tag}](${repo_url}/compare/${previous_tag}...${tag})"
              else
                echo "- 首个公开版本。"
                echo
                echo "当前提交: [${GITHUB_SHA:0:7}](${repo_url}/commit/${GITHUB_SHA})"
              fi
            } > generated-release-notes.md
          fi

          cat > release-notes.md <<EOF
          # NewSzxcn Email ${tag}

          自建邮箱 Webmail 全栈方案，包含 Web、API、Postfix、Dovecot、Rspamd 等组件。

          ## 注意

          如果需要公网正常收发邮件，请确保已正确配置 MX、SPF、DKIM、DMARC 以及 25 / 587 / 993 等端口。

          ## 使用文档

          - [项目文档](${repo_url}#readme)
          - [开源协议](${repo_url}/blob/main/LICENSE)

          ## Docker 镜像

          | 组件 | 镜像 |
          |------|------|
          | All-in-one | \`${image_base}:${tag}\` |
          | API | \`${image_base}-api:${tag}\` |
          | Web | \`${image_base}-web:${tag}\` |
          | Postfix | \`${image_base}-postfix:${tag}\` |
          | Dovecot | \`${image_base}-dovecot:${tag}\` |
          | Rspamd | \`${image_base}-rspamd:${tag}\` |

          同时也会发布 \`${version}\`、\`latest\` 和 \`sha-*\` 标签。
          EOF

          {
            echo
            cat generated-release-notes.md
          } >> release-notes.md

      - name: Create or update GitHub release
        env:
          GH_TOKEN: ${{ github.token }}
        shell: bash
        run: |
          tag="${{ needs.release.outputs.tag }}"
          title="NewSzxcn Email ${tag}"
          if gh release view "${tag}" >/dev/null 2>&1; then
            gh release edit "${tag}" --title "${title}" --notes-file release-notes.md --latest
          else
            gh release create "${tag}" --verify-tag --title "${title}" --notes-file release-notes.md --latest
          fi
