name: Build on: # Run on pushes to main and pushed tags, and on pull requests against main, but ignore the docs folder push: pull_request: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} cancel-in-progress: true env: IMAGE_VERSION: v2027.0.0 jobs: validation: name: "Validation" runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: gradle/actions/wrapper-validation@v5 # build-examples: # strategy: # fail-fast: false # matrix: # include: # - os: windows-2022 # artifact-name: Win64 # - os: macos-14 # artifact-name: macOS # - os: ubuntu-24.04 # artifact-name: Linux # name: "Photonlib - Build Examples - ${{ matrix.os }}" # runs-on: ${{ matrix.os }} # needs: [build-photonlib-host, build-photonlib-docker] # steps: # - name: Checkout code # uses: actions/checkout@v6 # with: # fetch-depth: 0 # - name: Fetch tags # run: git fetch --tags --force # - uses: actions/setup-java@v5 # with: # java-version: 25 # distribution: temurin # - name: Install SystemCore Toolchain # run: ./gradlew installSystemCoreToolchain # - name: Delete duplicate toolchains # run: | # find ~/.gradle/cache/ -name *bookworm* -exec rm -rf {} + # du -h . | sort -h # if: matrix.os == 'ubuntu-24.04' # # Download prebuilt photonlib artifacts # - uses: actions/download-artifact@v7 # with: # name: maven-${{ matrix.artifact-name }} # - uses: actions/download-artifact@v7 # with: # name: maven-SystemCore # - name: Move to maven local # run: | # mkdir -p ~/.m2/repository/ # mv maven/org ~/.m2/repository/ # - name: Copy vendordeps # shell: bash # run: | # for vendordep_folder in photonlib-*-examples/*/; do # # Remove trailing slash for cross-platform compatibility # vendordep_folder="${vendordep_folder%/}" # # Filter for projects only # if [ -e "$vendordep_folder/build.gradle" ]; then # mkdir -p "$vendordep_folder/vendordeps/" # cp vendordeps/photonlib-json-1.0.json "$vendordep_folder/vendordeps/" # fi # done # - name: Build Java examples # working-directory: photonlib-java-examples # run: | # ./gradlew build # ./gradlew clean # - name: Build C++ examples # working-directory: photonlib-cpp-examples # run: | # ./gradlew build # ./gradlew clean typecheck-client: name: "Typecheck Client" runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v6 - name: Install pnpm uses: pnpm/action-setup@v5 with: version: 10 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: 24 cache: pnpm cache-dependency-path: photon-client/pnpm-lock.yaml - name: Typecheck Client working-directory: photon-client run: | pnpm install --frozen-lockfile pnpm type-check playwright-tests: name: "Playwright E2E tests" runs-on: ubuntu-24.04 needs: [validation] steps: - name: Checkout code uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: java-version: 25 distribution: temurin - uses: pnpm/action-setup@v5 with: version: 10 - uses: actions/setup-node@v6 with: cache: pnpm cache-dependency-path: photon-client/pnpm-lock.yaml node-version: 24 - name: Setup tests working-directory: photon-client run: | pnpm install pnpm test-setup - name: Prebuild Gradle run: ./gradlew photon-targeting:build photon-core:build photon-server:build -x check - name: Run Playwright tests working-directory: photon-client run: pnpm test - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: name: "Playwright Report" path: photon-client/playwright-report/ retention-days: 30 build-gradle: name: "Gradle Build" runs-on: ubuntu-24.04 needs: [validation] steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 - name: Fetch tags run: git fetch --tags --force - uses: actions/setup-java@v5 with: java-version: 25 distribution: temurin - uses: pnpm/action-setup@v5 with: version: 10 - uses: actions/setup-node@v6 with: cache: pnpm cache-dependency-path: photon-client/pnpm-lock.yaml node-version: 24 - name: Gradle Build run: ./gradlew photon-targeting:build photon-core:build photon-server:build -x check - name: Gradle Tests and Coverage run: ./gradlew test jacocoTestReport --stacktrace build-offline-docs: name: "Build Offline Docs" runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: 3.14 - 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@v7 with: name: built-docs path: docs/build/html build-photonlib-vendorjson: name: "Build Vendor JSON" runs-on: ubuntu-24.04 needs: [validation] steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-java@v5 with: java-version: 25 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@v7 with: archive: false path: photon-lib/build/generated/vendordeps/photonlib-*.json build-photonlib-host: env: MACOSX_DEPLOYMENT_TARGET: 14 strategy: fail-fast: false matrix: include: - os: windows-2022 artifact-name: Win64 - os: macos-26 artifact-name: macOS - os: ubuntu-24.04 artifact-name: Linux name: "Photonlib - Build Host - ${{ matrix.artifact-name }}" runs-on: ${{ matrix.os }} needs: [validation] steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-java@v5 with: java-version: 25 distribution: temurin - run: git fetch --tags --force - run: ./gradlew photon-targeting:build photon-lib:build 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' && github.ref == 'refs/heads/main' # Copy artifacts to build/outputs/maven - run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts - uses: actions/upload-artifact@v7 with: name: maven-${{ matrix.artifact-name }} path: build/outputs build-photonlib-docker: strategy: fail-fast: false matrix: include: - container: wpilib/systemcore-cross-ubuntu:2027-24.04 artifact-name: SystemCore build-options: "-Ponlylinuxsystemcore" - container: wpilib/raspbian-cross-ubuntu:2027-bookworm-24.04 artifact-name: Raspbian build-options: "-Ponlylinuxarm32" - container: wpilib/aarch64-cross-ubuntu:2027-bookworm-24.04 artifact-name: Aarch64 build-options: "-Ponlylinuxarm64" runs-on: ubuntu-24.04 container: ${{ matrix.container }} name: "Photonlib - Build Docker - ${{ matrix.artifact-name }}" needs: [validation] steps: - uses: actions/checkout@v6 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 }} -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' && github.ref == 'refs/heads/main' # Copy artifacts to build/outputs/maven - run: ./gradlew photon-lib:publish photon-targeting:publish -PcopyOfflineArtifacts ${{ matrix.build-options }} - uses: actions/upload-artifact@v7 with: name: maven-${{ matrix.artifact-name }} path: build/outputs combine: name: Combine needs: [build-photonlib-docker, build-photonlib-host] runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - run: git fetch --tags --force # download all maven-* artifacts to outputs/ - uses: actions/download-artifact@v8 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@v7 with: name: photonlib-offline path: output/*.zip build-package-linux: needs: [build-gradle, build-offline-docs] strategy: fail-fast: false matrix: include: - os: ubuntu-24.04 artifact-name: Linux arch-override: linuxx86-64 - os: ubuntu-24.04 artifact-name: LinuxArm64 arch-override: linuxarm64 runs-on: ${{ matrix.os }} name: "Build fat JAR - ${{ matrix.artifact-name }}" steps: &build-package-steps - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-java@v5 with: java-version: 25 distribution: temurin - uses: pnpm/action-setup@v5 with: version: 10 - uses: actions/setup-node@v6 with: node-version: 24 cache: pnpm cache-dependency-path: photon-client/pnpm-lock.yaml - name: Install Arm64 Toolchain run: ./gradlew installArm64Toolchain if: ${{ (matrix.artifact-name) == 'LinuxArm64' }} - uses: actions/download-artifact@v8 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@v7 with: archive: false path: photon-server/build/libs - uses: actions/upload-artifact@v7 with: name: photon-targeting_jar-${{ matrix.artifact-name }} path: photon-targeting/build/libs build-package-macos: needs: [build-gradle, build-offline-docs] strategy: fail-fast: false matrix: include: - os: macos-latest artifact-name: macOSArm arch-override: macarm64 - os: macos-latest artifact-name: macOS arch-override: macx86-64 runs-on: ${{ matrix.os }} name: "Build fat JAR - ${{ matrix.artifact-name }}" steps: *build-package-steps build-package-windows: needs: [build-gradle, build-offline-docs] strategy: fail-fast: false matrix: include: - os: windows-latest artifact-name: Win arch-override: winx86-64 runs-on: ${{ matrix.os }} name: "Build fat JAR - ${{ matrix.artifact-name }}" steps: *build-package-steps run-smoketest-native: needs: [build-package-linux, build-package-macos, build-package-windows] strategy: fail-fast: false matrix: include: - os: ubuntu-24.04 artifact-name: photonvision-*-linuxx86-64.jar extraOpts: -Djdk.lang.Process.launchMechanism=vfork - os: windows-latest artifact-name: photonvision-*-winx86-64.jar - os: macos-latest artifact-name: photonvision-*-macarm64.jar - os: ubuntu-24.04-arm artifact-name: photonvision-*-linuxarm64.jar runs-on: ${{ matrix.os }} steps: - uses: actions/setup-java@v5 with: java-version: 25 distribution: temurin - uses: actions/download-artifact@v8 with: pattern: ${{ matrix.artifact-name }} # The jar is run twice to exercise different code paths. - run: | echo "=== First run ===" java -jar ${{ matrix.extraOpts }} *.jar --smoketest echo "=== Checking for files to corrupt ===" find ~ -type f \( -name "*.so" -o -name "*.dylib" \) | head -20 if [ -d ~/.wpilib ]; then echo "~/.wpilib directory exists" echo "Contents of ~/.wpilib:" find ~/.wpilib -type f \( -name "*.so" -o -name "*.dylib" \) | head -10 RANDOM_FILE=$(find ~/.wpilib -type f \( -name "*.so" -o -name "*.dylib" \) | sort -R | head -n 1) if [ ! -z "$RANDOM_FILE" ]; then echo "Corrupting file: $RANDOM_FILE" echo "corrupted data" > "$RANDOM_FILE" else echo "No .so or .dylib files found in ~/.wpilib" fi else echo "~/.wpilib directory does not exist" fi echo "=== Second 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 } ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break } if: ${{ (matrix.os) == 'windows-latest' }} build-image: needs: [build-package-linux] strategy: fail-fast: false matrix: include: - os: ubuntu-24.04-arm image_suffix: RaspberryPi plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_raspi.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: limelight2 plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: limelight3 plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight3.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: limelight3G plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight3g.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: limelight4 plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_limelight4.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: luma_p1 plat_override: LINUX_RASPBIAN64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_luma_p1.img.xz minimum_free_mb: 100 - os: ubuntu-24.04-arm image_suffix: orangepi5 plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: orangepi5b plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5b.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: orangepi5plus plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5plus.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: orangepi5pro plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5pro.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: orangepi5max plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_opi5max.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: rock5c plat_override: LINUX_RK3588_64 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_rock5c.img.xz minimum_free_mb: 1024 - os: ubuntu-24.04-arm image_suffix: rubikpi3 plat_override: LINUX_QCS6490 image_url: https://github.com/PhotonVision/photon-image-modifier/releases/download/$IMAGE_VERSION/photonvision_rubikpi3.tar.xz minimum_free_mb: 1024 root_location: 'offset=569376768' shrink_image: 'no' runs-on: ${{ matrix.os }} name: "Build image - ${{ matrix.image_suffix }}" steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/download-artifact@v8 with: pattern: photonvision-*-linuxarm64.jar - uses: photonvision/photon-image-runner@HEAD name: Generate image id: generate_image with: image_url: ${{ matrix.image_url }} minimum_free_mb: ${{ matrix.minimum_free_mb }} root_location: ${{ matrix.root_location || 'partition=2' }} shrink_image: ${{ matrix.shrink_image || 'yes' }} commands: ./scripts/armrunner.sh - name: Compress image # Compress the standard images if: ${{ ! startsWith(matrix.image_suffix, 'rubik') }} run: | set -ex new_jar=$(realpath $(find . -name photonvision\*-linuxarm64.jar)) new_image_name=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}") sudo mv ${{ steps.generate_image.outputs.image }} $new_image_name sudo xz -T 0 -kv $new_image_name echo "smoketest_image_loc=${new_image_name}" >> $GITHUB_ENV - name: Tar built image (Rubik) # Build the RubikPi3-specific tar file if: ${{ startsWith(matrix.image_suffix, 'rubik') }} run: | set -ex new_jar=$(realpath $(find . -name photonvision\*-linuxarm64.jar)) tardir=$(basename "${new_jar/.jar/_${{ matrix.image_suffix }}.img}") imagedir=$(dirname ${{ steps.generate_image.outputs.image }}) sudo mkdir --parents ${tardir} sudo cp ${imagedir}/* ${tardir}/ sudo tar -I 'xz -T0' -cf ${tardir}.tar.xz ${tardir} --checkpoint=10000 --checkpoint-action=echo='%T' # Point smoketest to the old image echo "smoketest_image_loc=${{ steps.generate_image.outputs.image }}" >> $GITHUB_ENV - uses: actions/upload-artifact@v7 with: archive: false path: photonvision*.xz # This is done after uploading the image to avoid contaminating the image with logs, caches, etc. - uses: photonvision/photon-image-runner@HEAD name: Smoketest Image with: image_url: file://${{ env.smoketest_image_loc }} minimum_free_mb: ${{ matrix.minimum_free_mb }} root_location: ${{ matrix.root_location || 'partition=2' }} shrink_image: ${{ matrix.shrink_image || 'yes' }} commands: java -jar *.jar --smoketest --platform=${{ matrix.plat_override }} matrix-checker: # This job always runs last to set the overall result based on the matrix jobs. If any matrix job failed, this job will fail. # This makes it so that we don't need to add each matrix job individually to CI checks. runs-on: ubuntu-latest needs: [build-image] if: always() steps: - run: ${{!contains(needs.*.result, 'failure')}} release: # Require smoketest-native so that if those fail, we don't release broken artifacts needs: [build-photonlib-vendorjson, build-image, combine, build-package-linux, build-package-macos, build-package-windows, run-smoketest-native] if: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'PhotonVision/photonvision' runs-on: ubuntu-24.04 steps: # Download all fat JARs - uses: actions/download-artifact@v8 with: merge-multiple: true pattern: photonvision-*.jar # Download offline photonlib - uses: actions/download-artifact@v8 with: merge-multiple: true pattern: photonlib-offline # Download vendor json - uses: actions/download-artifact@v8 with: pattern: photonlib-*.json # Download all images - uses: actions/download-artifact@v8 with: merge-multiple: true pattern: photonvision-*.xz - run: find # Push to dev release - uses: pyTooling/Actions/releaser@r6 with: token: ${{ secrets.GITHUB_TOKEN }} tag: 'Dev' rm: true snapshots: false files: | **/*.xz **/*linux*.jar **/*win*.jar **/photonlib*.json **/photonlib*.zip if: github.event_name == 'push' - name: Create Vendor JSON Repo PR uses: wpilibsuite/vendor-json-repo/.github/actions/add_vendordep@HEAD with: repo: PhotonVision/vendor-json-repo token: ${{ secrets.VENDOR_JSON_REPO_PUSH_TOKEN }} vendordep_file: ${{ github.workspace }}/photonlib-${{ github.ref_name }}.json pr_title: Update photonlib to ${{ github.ref_name }} pr_branch: photonlib-${{ github.ref_name }} if: github.repository == 'PhotonVision/photonvision' && startsWith(github.ref, 'refs/tags/v')