---
name: "Build Sing-box"

on:
  push:
    branches: [master, dev]
    paths:
      - "core/sing-box/**"
      - "!core/sing-box/**/*.md"
      - ".github/workflows/build-singbox.yml"
  pull_request:
    types: [opened, reopened, synchronize]
    paths:
      - "core/sing-box/**"
      - "!core/sing-box/**/*.md"
      - ".github/workflows/build-singbox.yml"
  schedule:
    - cron: "11 16 * * *"
  workflow_dispatch:

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

env:
  WORK_DIR: core/sing-box
  DOCKERHUB_REPO: ${{ secrets.DOCKER_USERNAME || github.repository_owner }}/sing-box

jobs:
  check:
    name: "Pre check"
    runs-on: ubuntu-latest
    outputs:
      renew: ${{ steps.vers.outputs.renew }}
      singbox_version: ${{ steps.vers.outputs.singbox_version }}

    steps:
      - name: "Fetch Versions and Decide"
        id: vers
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -eEuxo pipefail
          RENEW="false"
          LATEST_VER="$(gh release view -R SagerNet/sing-box --json tagName -q .tagName)"
          CURRENT_VER="$(skopeo list-tags "docker://docker.io/$DOCKERHUB_REPO" 2>/dev/null | jq -r '.Tags[]' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$'  | sort -rV | head -n 1 || true)"
          if [[ "$(printf '%s\n%s\n' "$LATEST_VER" "$CURRENT_VER" | sort -V | head -n 1)" != "$LATEST_VER" ]] || [[ -z "$CURRENT_VER" && -n "$LATEST_VER" ]]; then
            echo "Build required."
            RENEW="true"
          fi
          echo "renew=${RENEW}" >> "$GITHUB_OUTPUT"
          echo "singbox_version=${LATEST_VER}" >> "$GITHUB_OUTPUT"

  build:
    name: "Build image"
    runs-on: ubuntu-latest
    needs: [check]
    if: needs.check.outputs.renew == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
    strategy:
      fail-fast: true
      matrix:
        platform:
          - linux/386
          - linux/amd64
          - linux/arm/v6
          - linux/arm/v7
          - linux/arm64
          - linux/ppc64le
          - linux/riscv64
          - linux/s390x

    steps:
      - name: "Checkout repository"
        uses: actions/checkout@v7

      - name: "Prepare"
        run: |
          PLATFORM_PAIR="$(awk -F/ '{print $1"-"$2$3}' <<< ${{ matrix.platform }})"
          echo "PLATFORM_PAIR=$PLATFORM_PAIR" >> "$GITHUB_ENV"

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

      - name: "Login to DockerHub"
        uses: docker/login-action@v4
        if: ${{ github.event_name != 'pull_request' }}
        with:
          registry: docker.io
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: "Docker metadata"
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.DOCKERHUB_REPO }}
          tags: |
            type=raw,value=${{ needs.check.outputs.singbox_version }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.description=The universal proxy platform.
            org.opencontainers.image.source=https://github.com/SagerNet/sing-box
            org.opencontainers.image.title=sing-box
            org.opencontainers.image.url=https://sing-box.sagernet.org

      - name: "Build and export"
        id: build
        uses: docker/build-push-action@v7
        with:
          context: ${{ env.WORK_DIR }}
          platforms: ${{ matrix.platform }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: SINGBOX_VERSION=${{ needs.check.outputs.singbox_version }}
          outputs: >
            type=image,
            name=${{ env.DOCKERHUB_REPO }},
            push-by-digest=${{ github.ref_name == 'master' }},
            name-canonical=${{ github.ref_name == 'master' }},
            push=${{ github.ref_name == 'master' }}

      - name: "Export digest"
        if: github.ref_name == 'master'
        run: |
          rm -rf /tmp/digests
          mkdir -p /tmp/digests
          digest="${{ steps.build.outputs.digest }}"
          touch "/tmp/digests/${digest#sha256:}"

      - name: "Upload artifact"
        if: github.ref_name == 'master'
        uses: actions/upload-artifact@v7
        with:
          name: digest-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    runs-on: ubuntu-latest
    name: "Merge manifest"
    needs: [check, build]
    if: github.ref_name == 'master'

    steps:
      - name: "Download digests"
        uses: actions/download-artifact@v8
        with:
          path: /tmp/digests
          pattern: digest-*
          merge-multiple: true

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

      - name: "Login to DockerHub"
        uses: docker/login-action@v4
        with:
          registry: docker.io
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: "Docker metadata"
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.DOCKERHUB_REPO }}
          tags: |
            type=raw,value=${{ needs.check.outputs.singbox_version }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.description=The universal proxy platform.
            org.opencontainers.image.source=https://github.com/SagerNet/sing-box
            org.opencontainers.image.title=sing-box
            org.opencontainers.image.url=https://sing-box.sagernet.org

      - name: "Create manifest list and push"
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create \
            $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf '${{ env.DOCKERHUB_REPO }}@sha256:%s ' *)

      - name: "Inspect image"
        run: |
          docker buildx imagetools inspect ${{ env.DOCKERHUB_REPO }}:${{ needs.check.outputs.singbox_version }}
