name: Build

on:
  # Run on pushes to master and pushed tags, and on pull requests against master, but ignore the docs folder
  push:
    branches: [ master ]
    tags:
      - 'v*'
    paths:
      - '**'
      - '!docs/**'
      - '.github/**'
  pull_request:
    branches: [ master ]
    paths:
      - '**'
      - '!docs/**'
      - '.github/**'
  merge_group:

jobs:
  build-client:
    name: "PhotonClient Build"
    defaults:
      run:
        working-directory: photon-client
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18
      - name: Install Dependencies
        run: npm ci
      - name: Build Production Client
        run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: built-client
          path: photon-client/dist/
  build-examples:

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-2022
            architecture: x64
          - os: macos-14
            architecture: aarch64
          - os: ubuntu-22.04

    name: "Photonlib - Build Examples - ${{ matrix.os }}"
    runs-on: ${{ matrix.os }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Fetch tags
        run: git fetch --tags --force
      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin
      - name: Install RoboRIO Toolchain
        run: ./gradlew installRoboRioToolchain
      # Need to publish to maven local first, so that C++ sim can pick it up
      - name: Publish photonlib to maven local
        run: ./gradlew photon-targeting:publishtomavenlocal photon-lib:publishtomavenlocal -x check
      - name: Build Java examples
        working-directory: photonlib-java-examples
        run: ./gradlew build
      - name: Build C++ examples
        working-directory: photonlib-cpp-examples
        run: ./gradlew build
  build-gradle:
    name: "Gradle Build"
    runs-on: ubuntu-22.04
    steps:
      # Checkout code.
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Fetch tags
        run: git fetch --tags --force
      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin
      - name: Install mrcal deps
        run: sudo apt-get update && sudo apt-get install -y libcholmod3 liblapack3 libsuitesparseconfig5
      - name: Gradle Build
        run: ./gradlew photon-targeting:build photon-core:build photon-server:build -x check
      - name: Gradle Tests
        run: ./gradlew testHeadless -i --stacktrace
      - name: Gradle Coverage
        run: ./gradlew jacocoTestReport
      - name: Publish Coverage Report
        uses: codecov/codecov-action@v4
        with:
          file: ./photon-server/build/reports/jacoco/test/jacocoTestReport.xml
      - name: Publish Core Coverage Report
        uses: codecov/codecov-action@v4
        with:
          file: ./photon-core/build/reports/jacoco/test/jacocoTestReport.xml
  build-offline-docs:
    name: "Build Offline Docs"
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: Install graphviz
        run: |
          sudo apt-get update
          sudo apt-get -y install graphviz
      - name: Install dependencies
        working-directory: docs
        run: |
          python -m pip install --upgrade pip
          pip install sphinx sphinx_rtd_theme sphinx-tabs sphinxext-opengraph doc8
          pip install -r requirements.txt
      - name: Build the docs
        working-directory: docs
        run: |
          make html
      - uses: actions/upload-artifact@v4
        with:
          name: built-docs
          path: docs/build/html

  build-photonlib-vendorjson:
    name: "Build Vendor JSON"
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin

      # grab all tags
      - run: git fetch --tags --force

      # Generate the JSON and give it the ""standard""" name maven gives it
      - run: |
          ./gradlew photon-lib:generateVendorJson
          export VERSION=$(git describe --tags --match=v*)
          mv photon-lib/build/generated/vendordeps/photonlib.json photon-lib/build/generated/vendordeps/photonlib-$(git describe --tags --match=v*).json

      # Upload it here so it shows up in releases
      - uses: actions/upload-artifact@v4
        with:
          name: photonlib-vendor-json
          path: photon-lib/build/generated/vendordeps/photonlib-*.json

  build-photonlib-host:
    env:
      MACOSX_DEPLOYMENT_TARGET: 13
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-2022
            artifact-name: Win64
            architecture: x64
          - os: macos-14
            artifact-name: macOS
            architecture: aarch64
          - os: ubuntu-22.04
            artifact-name: Linux

    name: "Photonlib - Build Host - ${{ matrix.artifact-name }}"
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin
          architecture: ${{ matrix.architecture }}
      - run: git fetch --tags --force
      - run: ./gradlew photon-targeting:build photon-lib:build -i
        name: Build with Gradle
      - run: ./gradlew photon-lib:publish photon-targeting:publish
        name: Publish
        env:
          ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
        if: github.event_name == 'push' && github.repository_owner == 'photonvision'
      # Copy artifacts to build/outputs/maven
      - run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts
      - uses: actions/upload-artifact@v4
        with:
          name: maven-${{ matrix.artifact-name }}
          path: build/outputs

  build-photonlib-docker:
    strategy:
      fail-fast: false
      matrix:
        include:
          - container: wpilib/roborio-cross-ubuntu:2024-22.04
            artifact-name: Athena
            build-options: "-Ponlylinuxathena"
          - container: wpilib/raspbian-cross-ubuntu:bullseye-22.04
            artifact-name: Raspbian
            build-options: "-Ponlylinuxarm32"
          - container: wpilib/aarch64-cross-ubuntu:bullseye-22.04
            artifact-name: Aarch64
            build-options: "-Ponlylinuxarm64"

    runs-on: ubuntu-22.04
    container: ${{ matrix.container }}
    name: "Photonlib - Build Docker - ${{ matrix.artifact-name }}"
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Config Git
        run: |
          git config --global --add safe.directory /__w/photonvision/photonvision
      - name: Build PhotonLib
        # We don't need to run tests, since we specify only non-native platforms
        run: ./gradlew photon-targeting:build photon-lib:build ${{ matrix.build-options }} -i -x test
      - name: Publish
        run: ./gradlew photon-lib:publish photon-targeting:publish ${{ matrix.build-options }}
        env:
          ARTIFACTORY_API_KEY: ${{ secrets.ARTIFACTORY_API_KEY }}
        if: github.event_name == 'push' && github.repository_owner == 'photonvision'
      # Copy artifacts to build/outputs/maven
      - run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts ${{ matrix.build-options }}
      - uses: actions/upload-artifact@v4
        with:
          name: maven-${{ matrix.artifact-name }}
          path: build/outputs

  combine:
    name: Combine
    needs: [build-photonlib-docker, build-photonlib-host]
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: git fetch --tags --force
      # download all maven-* artifacts to outputs/
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true
          path: output
          pattern: maven-*
      - run: find .
      - run: zip -r photonlib-$(git describe --tags --match=v*).zip .
        name: ZIP stuff up
        working-directory: output
      - run: ls output
      - uses: actions/upload-artifact@v4
        with:
          name: photonlib-offline
          path: output/*.zip

  build-package:
    needs: [build-client, build-gradle, build-offline-docs]

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: windows-latest
            artifact-name: Win64
            architecture: x64
            arch-override: winx64
          - os: macos-latest
            artifact-name: macOS
            architecture: x64
            arch-override: macx64
          - os: macos-latest
            artifact-name: macOSArm
            architecture: x64
            arch-override: macarm64
          - os: ubuntu-22.04
            artifact-name: Linux
            architecture: x64
            arch-override: linuxx64
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            architecture: x64
            arch-override: linuxarm64

    runs-on: ${{ matrix.os }}
    name: "Build fat JAR - ${{ matrix.artifact-name }}"

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin
          architecture: ${{ matrix.architecture }}
      - name: Install Arm64 Toolchain
        run: ./gradlew installArm64Toolchain
        if: ${{ (matrix.artifact-name) == 'LinuxArm64' }}
      - run: |
          rm -rf photon-server/src/main/resources/web/*
          mkdir -p photon-server/src/main/resources/web/docs
        if: ${{ (matrix.os) != 'windows-latest' }}
      - run: |
          del photon-server\src\main\resources\web\*.*
          mkdir photon-server\src\main\resources\web\docs
        if: ${{ (matrix.os) == 'windows-latest' }}
      - uses: actions/download-artifact@v4
        with:
          name: built-client
          path: photon-server/src/main/resources/web/
      - uses: actions/download-artifact@v4
        with:
          name: built-docs
          path: photon-server/src/main/resources/web/docs
      - run: ./gradlew photon-targeting:jar photon-server:shadowJar -PArchOverride=${{ matrix.arch-override }}
        if: ${{ (matrix.arch-override != 'none') }}
      - run: ./gradlew photon-server:shadowJar
        if: ${{ (matrix.arch-override == 'none') }}
      - uses: actions/upload-artifact@v4
        with:
          name: jar-${{ matrix.artifact-name }}
          path: photon-server/build/libs
      - uses: actions/upload-artifact@v4
        with:
          name: photon-targeting_jar-${{ matrix.artifact-name }}
          path: photon-targeting/build/libs

  run-smoketest-native:
    needs: [build-package]

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-22.04
            artifact-name: jar-Linux
            extraOpts: -Djdk.lang.Process.launchMechanism=vfork
          - os: windows-latest
            artifact-name: jar-Win64
            extraOpts: ""
          - os: macos-latest
            artifact-name: jar-macOS
            architecture: x64

    runs-on: ${{ matrix.os }}

    steps:
      - name: Install Java 17
        uses: actions/setup-java@v4
        with:
          java-version: 17
          distribution: temurin
      - uses: actions/download-artifact@v4
        with:
          name: ${{ matrix.artifact-name }}
      # On linux, install mrcal packages
      - run: |
          sudo apt-get update
          sudo apt-get install --yes libcholmod3 liblapack3 libsuitesparseconfig5
        if: ${{ (matrix.os) == 'ubuntu-22.04' }}
      # and actually run the jar
      - run: java -jar ${{ matrix.extraOpts }} *.jar --smoketest
        if: ${{ (matrix.os) != 'windows-latest' }}
      - run: ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break }
        if: ${{ (matrix.os) == 'windows-latest' }}

  run-smoketest-chroot:
    needs: [build-package]

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: RaspberryPi
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-4/photonvision_raspi.img.xz
            cpu: cortex-a7
            image_additional_mb: 0
            extraOpts: -Djdk.lang.Process.launchMechanism=vfork

    runs-on: ${{ matrix.os }}
    name: smoketest-${{ matrix.image_suffix }}

    steps:
      - uses: actions/download-artifact@v4
        with:
          name: jar-${{ matrix.artifact-name }}

      - uses: pguyot/arm-runner-action@v2
        name: Run photon smoketest
        id: generate_image
        with:
          base_image: ${{ matrix.image_url }}
          image_additional_mb: ${{ matrix.image_additional_mb }}
          optimize_image: yes
          cpu: ${{ matrix.cpu }}
          # We do _not_ wanna copy photon into the image. Bind mount instead
          bind_mount_repository: true
          # our image better have java installed already
          commands: |
            java -jar ${{ matrix.extraOpts }} *.jar --smoketest

  build-image:
    needs: [build-package]

    if: ${{ github.event_name != 'pull_request' }}

    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: RaspberryPi
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_raspi.img.xz
            cpu: cortex-a7
            image_additional_mb: 0
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: limelight2
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_limelight.img.xz
            cpu: cortex-a7
            image_additional_mb: 0
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: limelight3
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_limelight3.img.xz
            cpu: cortex-a7
            image_additional_mb: 0
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: orangepi5
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5.img.xz
            cpu: cortex-a8
            image_additional_mb: 1024
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: orangepi5b
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5b.img.xz
            cpu: cortex-a8
            image_additional_mb: 1024
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: orangepi5plus
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5plus.img.xz
            cpu: cortex-a8
            image_additional_mb: 1024
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: orangepi5pro
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5pro.img.xz
            cpu: cortex-a8
            image_additional_mb: 1024
          - os: ubuntu-22.04
            artifact-name: LinuxArm64
            image_suffix: orangepi5max
            image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/v2025.0.0-beta-6/photonvision_opi5max.img.xz
            cpu: cortex-a8
            image_additional_mb: 1024

    runs-on: ${{ matrix.os }}
    name: "Build image - ${{ matrix.image_url }}"

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/download-artifact@v4
        with:
          name: jar-${{ matrix.artifact-name }}
      - uses: pguyot/arm-runner-action@HEAD
        name: Generate image
        id: generate_image
        with:
          base_image: ${{ matrix.image_url }}
          image_additional_mb: ${{ matrix.image_additional_mb }}
          optimize_image: yes
          cpu: ${{ matrix.cpu }}
          # We do _not_ wanna copy photon into the image. Bind mount instead
          bind_mount_repository: true
          commands: |
            chmod +x scripts/armrunner.sh
            ./scripts/armrunner.sh
      - name: Compress image
        run: |
          new_jar=$(realpath $(find . -name photonvision\*-linuxarm64.jar))
          new_image_name=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}")
          mv ${{ steps.generate_image.outputs.image }} $new_image_name
          sudo xz -T 0 -v $new_image_name
      - uses: actions/upload-artifact@v4
        name: Upload image
        with:
          name: image-${{ matrix.image_suffix }}
          path: photonvision*.xz
  release:
    needs: [build-package, build-image, combine]
    runs-on: ubuntu-22.04
    steps:
      # Download all fat JARs
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true
          pattern: jar-*
      # Download offline photonlib
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true
          pattern: photonlib-offline
      # Download vendor json
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true
          pattern: photonlib-vendor-json
      # Download all images
      - uses: actions/download-artifact@v4
        with:
          merge-multiple: true
          pattern: image-*

      - run: find
      # Push to dev release
      - uses: pyTooling/Actions/releaser@r0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          tag: 'Dev'
          rm: true
          files: |
            **/*.xz
            **/*.jar
            **/photonlib*.json
            **/photonlib*.zip
        if: github.event_name == 'push'
      # Upload all jars and xz archives
      # Split into two uploads to work around max size limits in action-gh-releases
      # https://github.com/softprops/action-gh-release/issues/353
      - uses: softprops/action-gh-release@v2.0.9
        with:
          files: |
            **/*orangepi5*.xz
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - uses: softprops/action-gh-release@v2.0.9
        with:
          files: |
            **/!(*orangepi5*).xz
            **/*.jar
            **/photonlib*.json
            **/photonlib*.zip
        if: startsWith(github.ref, 'refs/tags/v')
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  dispatch:
    name: dispatch
    needs: [build-photonlib-vendorjson, release]
    runs-on: ubuntu-22.04
    steps:
      - uses: peter-evans/repository-dispatch@v3
        if: |
          github.repository == 'PhotonVision/photonvision' &&
          startsWith(github.ref, 'refs/tags/v')
        with:
          token: ${{ secrets.VENDOR_JSON_REPO_PUSH_TOKEN }}
          repository: PhotonVision/vendor-json-repo
          event-type: tag
          client-payload: '{"run_id": "${{ github.run_id }}", "package_version": "${{ github.ref_name }}"}'
