From d996911cda68dc8682fadc1f52972dab3fce2765 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 15 Mar 2026 21:34:18 +0000 Subject: [PATCH] feat: add cross-platform CI for Linux, macOS, and Windows - Split build.yml into lint, test (matrix: 3 OS), build-android, and build-desktop (matrix: 3 OS with native packaging) jobs - Tests now run on ubuntu, macos, and windows to validate cross-platform compatibility - Desktop distributions (DEB, DMG, MSI) built on their native OS and uploaded as artifacts - Split create-release.yml into create-release, deploy-android, and deploy-desktop jobs so desktop distributions are included in releases - Android APK/AAB builds and Quartz publishing remain on Linux https://claude.ai/code/session_016dxFUErY4P6WRir4xxhCEr --- .github/workflows/build.yml | 130 ++++++++++++++++++++++++--- .github/workflows/create-release.yml | 128 +++++++++++++++++++------- 2 files changed, 215 insertions(+), 43 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8387a5298..9bb6fd295 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Test/Build Android +name: Test/Build on: pull_request: @@ -7,10 +7,9 @@ on: branches: [main] jobs: - build: + lint: runs-on: ubuntu-latest - timeout-minutes: 30 - + timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@v4 @@ -24,20 +23,84 @@ jobs: - name: Cache gradle uses: actions/cache@v4 with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - name: Linter (gradle) run: ./gradlew spotlessCheck + test: + needs: lint + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + + - name: Cache gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Test (gradle) run: ./gradlew test --no-daemon - name: Android Test Report uses: asadmansr/android-test-report-action@v1.2.0 - if: ${{ always() }} # IMPORTANT: run Android Test Report regardless + if: ${{ always() && matrix.os == 'ubuntu-latest' }} + + - name: Upload Test Results + uses: actions/upload-artifact@v4 + if: ${{ always() && matrix.os == 'ubuntu-latest' }} + with: + name: Test Reports + path: amethyst/build/reports + + build-android: + needs: test + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + + - name: Cache gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- - name: Build APK (gradle) run: ./gradlew assembleDebug @@ -75,9 +138,54 @@ jobs: name: Compose Reports path: amethyst/build/compose_compiler - - name: Upload Test Results + build-desktop: + needs: test + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + task: packageDeb + artifact-name: Desktop Linux DEB + artifact-path: desktopApp/build/compose/binaries/main/deb/*.deb + - os: macos-latest + task: packageDmg + artifact-name: Desktop macOS DMG + artifact-path: desktopApp/build/compose/binaries/main/dmg/*.dmg + - os: windows-latest + task: packageMsi + artifact-name: Desktop Windows MSI + artifact-path: desktopApp/build/compose/binaries/main/msi/*.msi + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + defaults: + run: + shell: bash + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + + - name: Cache gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Build Desktop Distribution + run: ./gradlew :desktopApp:${{ matrix.task }} + + - name: Upload Desktop Distribution uses: actions/upload-artifact@v4 with: - name: Test Reports - path: amethyst/build/reports - + name: ${{ matrix.artifact-name }} + path: ${{ matrix.artifact-path }} diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 44f3d56fd..70f337ecd 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -6,7 +6,24 @@ on: - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: - deploy: + create-release: + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: true + + deploy-android: + needs: create-release runs-on: ubuntu-latest steps: - name: Checkout code @@ -21,8 +38,10 @@ jobs: - name: Cache gradle uses: actions/cache@v4 with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- @@ -38,7 +57,6 @@ jobs: keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} env: - # override default build-tools version (29.0.3) -- optional BUILD_TOOLS_VERSION: "36.0.0" - name: Sign AAB (F-Droid) @@ -50,7 +68,6 @@ jobs: keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} env: - # override default build-tools version (29.0.3) -- optional BUILD_TOOLS_VERSION: "36.0.0" - name: Build APK @@ -65,7 +82,6 @@ jobs: keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} env: - # override default build-tools version (29.0.3) -- optional BUILD_TOOLS_VERSION: "36.0.0" - name: Sign APK (F-Droid) @@ -77,20 +93,8 @@ jobs: keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }} env: - # override default build-tools version (29.0.3) -- optional BUILD_TOOLS_VERSION: "36.0.0" - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: true - # Google Play APK - name: Upload Play APK Universal Asset id: upload-release-asset-play-universal-apk @@ -98,7 +102,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-universal-release-unsigned-signed.apk asset_name: amethyst-googleplay-universal-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -109,7 +113,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-x86-release-unsigned-signed.apk asset_name: amethyst-googleplay-x86-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -120,7 +124,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-x86_64-release-unsigned-signed.apk asset_name: amethyst-googleplay-x86_64-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -131,7 +135,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-arm64-v8a-release-unsigned-signed.apk asset_name: amethyst-googleplay-arm64-v8a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -142,7 +146,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/play/release/amethyst-play-armeabi-v7a-release-unsigned-signed.apk asset_name: amethyst-googleplay-armeabi-v7a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -154,7 +158,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-universal-release-unsigned-signed.apk asset_name: amethyst-fdroid-universal-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -165,7 +169,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-x86-release-unsigned-signed.apk asset_name: amethyst-fdroid-x86-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -176,7 +180,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-x86_64-release-unsigned-signed.apk asset_name: amethyst-fdroid-x86_64-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -187,7 +191,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-arm64-v8a-release-unsigned-signed.apk asset_name: amethyst-fdroid-arm64-v8a-${{ github.ref_name }}.apk asset_content_type: application/zip @@ -198,13 +202,11 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/apk/fdroid/release/amethyst-fdroid-armeabi-v7a-release-unsigned-signed.apk asset_name: amethyst-fdroid-armeabi-v7a-${{ github.ref_name }}.apk asset_content_type: application/zip - - # Google Play AAB - name: Upload Google Play AAB Asset id: upload-release-asset-play-aab @@ -212,7 +214,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/bundle/playRelease/amethyst-play-release.aab asset_name: amethyst-googleplay-${{ github.ref_name }}.aab asset_content_type: application/zip @@ -224,7 +226,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.create_release.outputs.upload_url }} + upload_url: ${{ needs.create-release.outputs.upload_url }} asset_path: amethyst/build/outputs/bundle/fdroidRelease/amethyst-fdroid-release.aab asset_name: amethyst-fdroid-${{ github.ref_name }}.aab asset_content_type: application/zip @@ -236,3 +238,65 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} + + deploy-desktop: + needs: create-release + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + task: packageDeb + format: deb + platform: linux + - os: macos-latest + task: packageDmg + format: dmg + platform: macos + - os: windows-latest + task: packageMsi + format: msi + platform: windows + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + + - name: Cache gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Build Desktop Distribution + run: ./gradlew :desktopApp:${{ matrix.task }} + + - name: Find distribution file + id: find-dist + run: | + DIST_FILE=$(find desktopApp/build/compose/binaries/main/${{ matrix.format }} -type f \( -name "*.deb" -o -name "*.dmg" -o -name "*.msi" \) | head -1) + echo "path=$DIST_FILE" >> $GITHUB_OUTPUT + echo "name=$(basename $DIST_FILE)" >> $GITHUB_OUTPUT + + - name: Upload Desktop Distribution to Release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: ${{ steps.find-dist.outputs.path }} + asset_name: amethyst-desktop-${{ matrix.platform }}-${{ github.ref_name }}.${{ matrix.format }} + asset_content_type: application/octet-stream