From fb0a70690eed20d82950d50097b214b9269b2d26 Mon Sep 17 00:00:00 2001 From: Uruk Date: Tue, 30 Sep 2025 01:20:11 +0200 Subject: [PATCH] refactor: consolidate build workflows into unified app builder Merges separate Android and iOS build workflows into a single "Build Apps" workflow to reduce duplication and simplify maintenance. Updates the artifact comment workflow to handle both the new unified workflow and legacy separate workflows for backward compatibility during transition. Removes redundant workflow files while preserving all existing functionality. --- .github/workflows/artifact-comment.yml | 54 ++--- .github/workflows/build-android.yml | 93 -------- .github/workflows/build-apps.yml | 280 +++++++++++++++++++++++++ .github/workflows/build-ios.yml | 95 --------- 4 files changed, 309 insertions(+), 213 deletions(-) delete mode 100644 .github/workflows/build-android.yml create mode 100644 .github/workflows/build-apps.yml delete mode 100644 .github/workflows/build-ios.yml diff --git a/.github/workflows/artifact-comment.yml b/.github/workflows/artifact-comment.yml index c4773b62..08710fe8 100644 --- a/.github/workflows/artifact-comment.yml +++ b/.github/workflows/artifact-comment.yml @@ -10,8 +10,7 @@ on: types: [opened, synchronize, reopened] workflow_run: # Triggered when build workflows complete workflows: - - "🤖 Android APK Build (Phone + TV)" - - "🤖 iOS IPA Build (Phone + TV)" + - "🏗️ Build Apps" types: - completed @@ -116,6 +115,7 @@ jobs: // Filter for build workflows only and sort by creation time (most recent first) const buildRuns = workflowRuns.workflow_runs .filter(run => + run.name.includes('Build Apps') || run.name.includes('Android APK Build') || run.name.includes('iOS IPA Build') ) @@ -132,57 +132,61 @@ jobs: let allArtifacts = []; let buildStatuses = {}; - // Get the most recent run for each workflow type + // Get the most recent run for the unified apps workflow + const latestAppsRun = buildRuns.find(run => run.name.includes('Build Apps')); const latestAndroidRun = buildRuns.find(run => run.name.includes('Android APK Build')); const latestIOSRun = buildRuns.find(run => run.name.includes('iOS IPA Build')); - // Store status for each workflow type - if (latestAndroidRun) { + // Store status for unified workflow (preferred) or fallback to separate workflows + const appsWorkflowRun = latestAppsRun || latestAndroidRun; + if (appsWorkflowRun) { buildStatuses['Android'] = { - name: latestAndroidRun.name, - status: latestAndroidRun.status, - conclusion: latestAndroidRun.conclusion, - url: latestAndroidRun.html_url, - runId: latestAndroidRun.id, - created_at: latestAndroidRun.created_at + name: appsWorkflowRun.name, + status: appsWorkflowRun.status, + conclusion: appsWorkflowRun.conclusion, + url: appsWorkflowRun.html_url, + runId: appsWorkflowRun.id, + created_at: appsWorkflowRun.created_at }; // Collect artifacts if completed successfully - if (latestAndroidRun.conclusion === 'success') { + if (appsWorkflowRun.conclusion === 'success') { try { const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, - run_id: latestAndroidRun.id + run_id: appsWorkflowRun.id }); allArtifacts.push(...artifacts.artifacts); } catch (error) { - console.log(`Failed to get Android artifacts for run ${latestAndroidRun.id}:`, error.message); + console.log(`Failed to get apps artifacts for run ${appsWorkflowRun.id}:`, error.message); } } } - if (latestIOSRun) { + // For iOS, use the same workflow run (since it's all in one now) or fallback to separate + const iosWorkflowRun = latestAppsRun || latestIOSRun; + if (iosWorkflowRun) { buildStatuses['iOS'] = { - name: latestIOSRun.name, - status: latestIOSRun.status, - conclusion: latestIOSRun.conclusion, - url: latestIOSRun.html_url, - runId: latestIOSRun.id, - created_at: latestIOSRun.created_at + name: iosWorkflowRun.name, + status: iosWorkflowRun.status, + conclusion: iosWorkflowRun.conclusion, + url: iosWorkflowRun.html_url, + runId: iosWorkflowRun.id, + created_at: iosWorkflowRun.created_at }; - // Collect artifacts if completed successfully - if (latestIOSRun.conclusion === 'success') { + // Only collect artifacts if not already collected from apps workflow + if (!latestAppsRun && iosWorkflowRun.conclusion === 'success') { try { const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, - run_id: latestIOSRun.id + run_id: iosWorkflowRun.id }); allArtifacts.push(...artifacts.artifacts); } catch (error) { - console.log(`Failed to get iOS artifacts for run ${latestIOSRun.id}:`, error.message); + console.log(`Failed to get iOS artifacts for run ${iosWorkflowRun.id}:`, error.message); } } } diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml deleted file mode 100644 index bc9b9ea1..00000000 --- a/.github/workflows/build-android.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: 🤖 Android APK Build (Phone + TV) - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -on: - workflow_dispatch: - pull_request: - branches: [develop, master] - push: - branches: [develop, master] - -jobs: - build-android: - if: (!contains(github.event.head_commit.message, '[skip ci]')) - runs-on: ubuntu-24.04 - name: 🏗️ Build Android APK - permissions: - contents: read - - strategy: - fail-fast: false - matrix: - target: [phone, tv] - - steps: - - name: 📥 Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - fetch-depth: 0 - submodules: recursive - show-progress: false - - - name: 🍞 Setup Bun - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 - with: - bun-version: latest - - - name: 💾 Cache Bun dependencies - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-${{ runner.arch }}-bun-develop - ${{ runner.os }}-bun-develop - - - name: 📦 Install dependencies and reload submodules - run: | - bun install --frozen-lockfile - bun run submodule-reload - - - name: 💾 Cache Gradle global - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} - restore-keys: ${{ runner.os }}-gradle-develop - - - name: 🛠️ Generate project files - run: | - if [ "${{ matrix.target }}" = "tv" ]; then - bun run prebuild:tv - else - bun run prebuild - fi - - - name: 💾 Cache project Gradle (.gradle) - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: android/.gradle - key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} - restore-keys: ${{ runner.os }}-android-gradle-develop - - - name: 🚀 Build APK - env: - EXPO_TV: ${{ matrix.target == 'tv' && 1 || 0 }} - run: bun run build:android:local - - - name: 📅 Set date tag - run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV - - - name: 📤 Upload APK artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: streamyfin-android-${{ matrix.target }}-apk-${{ env.DATE_TAG }} - path: | - android/app/build/outputs/apk/release/*.apk - retention-days: 7 diff --git a/.github/workflows/build-apps.yml b/.github/workflows/build-apps.yml new file mode 100644 index 00000000..ad770414 --- /dev/null +++ b/.github/workflows/build-apps.yml @@ -0,0 +1,280 @@ +name: 🏗️ Build Apps + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + workflow_dispatch: + pull_request: + branches: [develop, master] + push: + branches: [develop, master] + +jobs: + build-android-phone: + if: (!contains(github.event.head_commit.message, '[skip ci]')) + runs-on: ubuntu-24.04 + name: 🤖 Build Android APK (Phone) + permissions: + contents: read + + steps: + - name: 📥 Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + submodules: recursive + show-progress: false + + - name: 🍞 Setup Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + with: + bun-version: latest + + - name: 💾 Cache Bun dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-bun-develop + ${{ runner.os }}-bun-develop + + - name: 📦 Install dependencies and reload submodules + run: | + bun install --frozen-lockfile + bun run submodule-reload + + - name: 💾 Cache Gradle global + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: 🛠️ Generate project files + run: bun run prebuild + + - name: 💾 Cache project Gradle (.gradle) + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: android/.gradle + key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: ${{ runner.os }}-android-gradle-develop + + - name: 🚀 Build APK + env: + EXPO_TV: 0 + run: bun run build:android:local + + - name: 📅 Set date tag + run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV + + - name: 📤 Upload APK artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: streamyfin-android-phone-apk-${{ env.DATE_TAG }} + path: | + android/app/build/outputs/apk/release/*.apk + retention-days: 7 + + build-android-tv: + if: (!contains(github.event.head_commit.message, '[skip ci]')) + runs-on: ubuntu-24.04 + name: 🤖 Build Android APK (TV) + permissions: + contents: read + + steps: + - name: 📥 Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + submodules: recursive + show-progress: false + + - name: 🍞 Setup Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + with: + bun-version: latest + + - name: 💾 Cache Bun dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }} + restore-keys: | + ${{ runner.os }}-${{ runner.arch }}-bun-develop + ${{ runner.os }}-bun-develop + + - name: 📦 Install dependencies and reload submodules + run: | + bun install --frozen-lockfile + bun run submodule-reload + + - name: 💾 Cache Gradle global + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: 🛠️ Generate project files + run: bun run prebuild:tv + + - name: 💾 Cache project Gradle (.gradle) + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: android/.gradle + key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: ${{ runner.os }}-android-gradle-develop + + - name: 🚀 Build APK + env: + EXPO_TV: 1 + run: bun run build:android:local + + - name: 📅 Set date tag + run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV + + - name: 📤 Upload APK artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: streamyfin-android-tv-apk-${{ env.DATE_TAG }} + path: | + android/app/build/outputs/apk/release/*.apk + retention-days: 7 + + build-ios-phone: + if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin')) + runs-on: macos-15 + name: 🍎 Build iOS IPA (Phone) + permissions: + contents: read + + steps: + - name: 📥 Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 + submodules: recursive + show-progress: false + + - name: 🍞 Setup Bun + uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + with: + bun-version: latest + + - name: 💾 Cache Bun dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun-cache + + - name: 📦 Install dependencies and reload submodules + run: | + bun install --frozen-lockfile + bun run submodule-reload + + - name: 🛠️ Generate project files + run: bun run prebuild + + - name: 🏗️ Setup EAS + uses: expo/expo-github-action@main + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + eas-cache: true + + - name: ⚙️ Ensure iOS SDKs installed + run: xcodebuild -downloadPlatform iOS + + - name: 🚀 Build iOS app + env: + EXPO_TV: 0 + run: eas build -p ios --local --non-interactive + + - name: 📅 Set date tag + run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV + + - name: 📤 Upload IPA artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: streamyfin-ios-phone-ipa-${{ env.DATE_TAG }} + path: build-*.ipa + retention-days: 7 + + # Disabled for now - uncomment when ready to build iOS TV + # build-ios-tv: + # if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin')) + # runs-on: macos-15 + # name: 🍎 Build iOS IPA (TV) + # permissions: + # contents: read + # + # steps: + # - name: 📥 Checkout code + # uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + # with: + # ref: ${{ github.event.pull_request.head.sha || github.sha }} + # fetch-depth: 0 + # submodules: recursive + # show-progress: false + # + # - name: 🍞 Setup Bun + # uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 + # with: + # bun-version: latest + # + # - name: 💾 Cache Bun dependencies + # uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + # with: + # path: ~/.bun/install/cache + # key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} + # restore-keys: | + # ${{ runner.os }}-bun-cache + # + # - name: 📦 Install dependencies and reload submodules + # run: | + # bun install --frozen-lockfile + # bun run submodule-reload + # + # - name: 🛠️ Generate project files + # run: bun run prebuild:tv + # + # - name: 🏗️ Setup EAS + # uses: expo/expo-github-action@main + # with: + # eas-version: latest + # token: ${{ secrets.EXPO_TOKEN }} + # eas-cache: true + # + # - name: ⚙️ Ensure tvOS SDKs installed + # run: xcodebuild -downloadPlatform tvOS + # + # - name: 🚀 Build iOS app + # env: + # EXPO_TV: 1 + # run: eas build -p ios --local --non-interactive + # + # - name: 📅 Set date tag + # run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV + # + # - name: 📤 Upload IPA artifact + # uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + # with: + # name: streamyfin-ios-tv-ipa-${{ env.DATE_TAG }} + # path: build-*.ipa + # retention-days: 7 diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml deleted file mode 100644 index 76e4a4d2..00000000 --- a/.github/workflows/build-ios.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: 🤖 iOS IPA Build (Phone + TV) - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -on: - workflow_dispatch: - pull_request: - branches: [develop, master] - paths-ignore: - - "*.md" - push: - branches: [develop, master] - paths-ignore: - - "*.md" - -jobs: - build-ios: - if: (!contains(github.event.head_commit.message, '[skip ci]') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'streamyfin/streamyfin')) - runs-on: macos-15 - name: 🏗️ Build iOS IPA - permissions: - contents: read - - strategy: - fail-fast: false - matrix: - target: [phone] - # target: [phone, tv] - - steps: - - name: 📥 Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - fetch-depth: 0 - submodules: recursive - show-progress: false - - - name: 🍞 Setup Bun - uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2 - with: - bun-version: latest - - - name: 💾 Cache Bun dependencies - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ~/.bun/install/cache - key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }} - restore-keys: | - ${{ runner.os }}-bun-cache - - - name: 📦 Install dependencies and reload submodules - run: | - bun install --frozen-lockfile - bun run submodule-reload - - - name: 🛠️ Generate project files - run: | - if [ "${{ matrix.target }}" = "tv" ]; then - bun run prebuild:tv - else - bun run prebuild - fi - - - name: 🏗️ Setup EAS - uses: expo/expo-github-action@main - with: - eas-version: latest - token: ${{ secrets.EXPO_TOKEN }} - eas-cache: true - - - name: ⚙️ Ensure iOS/tvOS SDKs installed - run: | - if [ "${{ matrix.target }}" = "tv" ]; then - xcodebuild -downloadPlatform tvOS - else - xcodebuild -downloadPlatform iOS - fi - - - name: 🚀 Build iOS app - env: - EXPO_TV: ${{ matrix.target == 'tv' && 1 || 0 }} - run: eas build -p ios --local --non-interactive - - - name: 📅 Set date tag - run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV - - - name: 📤 Upload IPA artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: streamyfin-ios-${{ matrix.target }}-ipa-${{ env.DATE_TAG }} - path: build-*.ipa - retention-days: 7