---
name: "Build Xray"

on:
  push:
    branches: [master, dev]
    paths:
      - "core/xray/**"
      - "!core/xray/**/*.md"
      - ".github/workflows/build-xray.yml"
  pull_request:
    paths:
      - "core/xray/**"
      - "!core/xray/**/*.md"
      - ".github/workflows/build-xray.yml"
  schedule:
    - cron: "11 16 * * *"
  workflow_dispatch:

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

env:
  WORK_DIR: core/xray
  SRC_DIR: core/xray/src
  DOCKERHUB_REPO: ${{ secrets.DOCKER_USERNAME || github.repository_owner }}/xray

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

    steps:
      - name: "Resolve upstream versions"
        id: vers
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -eEuxo pipefail

          RENEW="false"
          LATEST_TAG="$(gh api 'repos/XTLS/Xray-core/tags?per_page=100' --jq '.[].name' | sort -V | tail -1)"
          LATEST_VER="${LATEST_TAG#v}"
          CURRENT_VER="$(skopeo list-tags "docker://docker.io/$DOCKERHUB_REPO" 2>/dev/null | jq -r '.Tags[]' | sed -nE '/^[0-9]+\.[0-9]+\.[0-9]+$/p' | sort -V | tail -n1 || 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"
          else
            echo "Build skipped."
          fi

          echo "renew=$RENEW" >> "$GITHUB_OUTPUT"
          echo "xray_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
          echo "xray_version=$LATEST_VER" >> "$GITHUB_OUTPUT"

  build-binary:
    name: "Build binary"
    runs-on: ubuntu-latest
    needs: [check]
    if: needs.check.outputs.renew == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request'
    defaults:
      run:
        shell: bash
    strategy:
      fail-fast: true
      matrix:
        include:
          - { goos: linux, goarch: "386", platform: "linux/386" }
          - { goos: linux, goarch: amd64, platform: "linux/amd64" }
          - { goos: linux, goarch: arm, goarm: "6", platform: "linux/arm/v6" }
          - { goos: linux, goarch: arm, goarm: "7", platform: "linux/arm/v7" }
          - { goos: linux, goarch: arm64, platform: "linux/arm64" }
          - { goos: linux, goarch: ppc64le, platform: "linux/ppc64le" }
          - { goos: linux, goarch: riscv64, platform: "linux/riscv64" }
          - { goos: linux, goarch: s390x, platform: "linux/s390x" }
    env:
      GOOS: ${{ matrix.goos }}
      GOARCH: ${{ matrix.goarch }}
      GOARM: ${{ matrix.goarm }}
      CGO_ENABLED: 0

    steps:
      - name: "Checkout repository"
        uses: actions/checkout@v7
        with:
          fetch-depth: 0

      - name: "Checkout Xray-core repository"
        uses: actions/checkout@v7
        with:
          repository: XTLS/Xray-core
          ref: ${{ needs.check.outputs.xray_tag }}
          path: ${{ env.SRC_DIR }}
          persist-credentials: false

      - name: "Setup Go"
        uses: actions/setup-go@v7
        with:
          go-version-file: ${{ env.SRC_DIR }}/go.mod
          check-latest: true
          cache-dependency-path: ${{ env.SRC_DIR }}/go.sum

      - name: "Download dependencies"
        working-directory: ${{ env.SRC_DIR }}
        run: go mod download

      - name: "Compile Xray"
        working-directory: ${{ env.SRC_DIR }}
        run: |
          set -Eexuo pipefail

          short_id="$(git rev-parse --short HEAD)"
          echo "GOOS: ${GOOS} GOARCH: ${GOARCH} GOARM: ${GOARM:-}"

          go build -v -trimpath -o xray -buildvcs=false -gcflags="all=-l=4" \
            -ldflags="-X github.com/xtls/xray-core/core.build=${short_id} -s -w -buildid=" \
            ./main

      - name: "Prepare assets"
        working-directory: ${{ env.SRC_DIR }}
        run: |
          set -Eexuo pipefail

          PLATFORM_PAIR="$(awk -F/ '{print $1"-"$2$3}' <<< "${{ matrix.platform }}")"
          BINARY_NAME="xray-${GOARCH}${GOARM:+v${GOARM}}"
          mv -f -- xray "$BINARY_NAME"
          chmod 0755 "$BINARY_NAME"
          echo "BINARY_NAME=$BINARY_NAME" >> "$GITHUB_ENV"
          echo "PLATFORM_PAIR=$PLATFORM_PAIR" >> "$GITHUB_ENV"

      - name: "Upload artifact"
        uses: actions/upload-artifact@v7
        with:
          name: xray-${{ env.PLATFORM_PAIR }}
          path: ${{ env.SRC_DIR }}/${{ env.BINARY_NAME }}
          if-no-files-found: error
          retention-days: 1

  build-image:
    name: "Build image"
    runs-on: ubuntu-latest
    needs: [check, build-binary]
    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: "Set platform pair"
        run: |
          PLATFORM_PAIR=$(awk -F/ '{print $1"-"$2$3}' <<< "${{ matrix.platform }}")
          echo "PLATFORM_PAIR=$PLATFORM_PAIR" >> "$GITHUB_ENV"

      - name: "Download binary"
        uses: actions/download-artifact@v8
        with:
          name: xray-${{ env.PLATFORM_PAIR }}
          path: ${{ env.WORK_DIR }}

      - name: "Prepare build context"
        working-directory: ${{ env.WORK_DIR }}
        run: |
          set -Eexuo pipefail

          test -f xray*
          chmod 0755 xray*
          for g in geoip geosite; do
            curl -L -O "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/$g.dat"
            curl -L -O "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/$g.dat.sha256sum"
            sha256sum -c "$g.dat.sha256sum"
          done

      - name: "Setup Docker QEMU"
        uses: docker/setup-qemu-action@v4

      - 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' && github.ref_name == 'master' }}
        with:
          registry: docker.io
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: "Extract Docker metadata"
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.DOCKERHUB_REPO }}
          tags: |
            type=raw,value=${{ needs.check.outputs.xray_version }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.title=Xray
            org.opencontainers.image.description=Xray, Penetrates Everything. Also the best v2ray-core. Where the magic happens. An open platform for various uses.
            org.opencontainers.image.url=https://xtls.github.io
            org.opencontainers.image.source=https://github.com/XTLS/Xray-core
            org.opencontainers.image.version=${{ needs.check.outputs.xray_version }}
            org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
            org.opencontainers.image.licenses=MPL-2.0

      - 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 }}
          outputs: type=image,name=${{ env.DOCKERHUB_REPO }},push-by-digest=${{ github.event_name != 'pull_request' && github.ref_name == 'master' }},name-canonical=${{ github.event_name != 'pull_request' && github.ref_name == 'master' }},push=${{ github.event_name != 'pull_request' && github.ref_name == 'master' }}

      - name: "Export digest"
        if: ${{ github.event_name != 'pull_request' && 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.event_name != 'pull_request' && 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-manifest:
    runs-on: ubuntu-latest
    name: "Merge manifest"
    needs: [check, build-image]
    if: ${{ github.event_name != 'pull_request' && 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: "Extract Docker metadata"
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.DOCKERHUB_REPO }}
          tags: |
            type=raw,value=${{ needs.check.outputs.xray_version }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.title=Xray
            org.opencontainers.image.description=Xray, Penetrates Everything. Also the best v2ray-core. Where the magic happens. An open platform for various uses.
            org.opencontainers.image.url=https://xtls.github.io
            org.opencontainers.image.source=https://github.com/XTLS/Xray-core
            org.opencontainers.image.version=${{ needs.check.outputs.xray_version }}
            org.opencontainers.image.created={{commit_date 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'}}
            org.opencontainers.image.licenses=MPL-2.0

      - 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.xray_version }}

  sync:
    name: "Sync readme to dockerhub"
    runs-on: ubuntu-latest
    needs: [merge-manifest]
    if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' }}

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

      - name: "Sync readme to dockerhub"
        uses: peter-evans/dockerhub-description@v5
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
          repository: ${{ env.DOCKERHUB_REPO }}
          readme-filepath: ${{ env.WORK_DIR }}/README.md
          enable-url-completion: false
