Skip to content

feat: add GitHub Actions workflow to build Android APK via EAS#46

Open
chakrihacker wants to merge 3 commits into
mainfrom
claude/setup-github-actions-builds-e2sDl
Open

feat: add GitHub Actions workflow to build Android APK via EAS#46
chakrihacker wants to merge 3 commits into
mainfrom
claude/setup-github-actions-builds-e2sDl

Conversation

@chakrihacker
Copy link
Copy Markdown
Contributor

@chakrihacker chakrihacker commented Feb 23, 2026

User description

User description

Adds .github/workflows/build-apk.yml that:

  • Triggers manually (workflow_dispatch) with profile choice (preview/production)
  • Triggers automatically on version tags (v*..)
  • Builds Android APK using EAS Build cloud service
  • Downloads the built APK and uploads it as a GitHub Actions artifact (30-day retention)
  • Reuses the existing setup-node-bun-install composite action and staging environment

https://claude.ai/code/session_01AogoKQvPk5RwVsZ52ps165


PR Type

Enhancement


Description

  • Add GitHub Actions workflow for building Android APK and iOS IPA

  • Uses eas build --local to build natively on runners

  • Supports manual trigger with platform selection or automatic tag-based builds

  • Implements caching for Gradle and CocoaPods dependencies


Diagram Walkthrough

flowchart LR
  trigger["Manual dispatch or<br/>version tag push"]
  android["Android Job<br/>ubuntu-latest"]
  ios["iOS Job<br/>macos-latest"]
  eas["eas build --local<br/>with EAS CLI"]
  cache["Gradle/CocoaPods<br/>caching"]
  artifact["Upload APK/IPA<br/>artifacts"]
  
  trigger --> android
  trigger --> ios
  android --> eas
  ios --> eas
  eas --> cache
  cache --> artifact
Loading

File Walkthrough

Relevant files
Enhancement
build-apk-ipa.yml
GitHub Actions workflow for local APK and IPA builds         

.github/workflows/build-apk-ipa.yml

  • Creates new workflow triggered by manual dispatch (with platform
    choice) or version tags
  • Implements parallel Android and iOS build jobs using eas build --local
  • Configures environment variables from staging environment secrets
  • Sets up dependency caching for Gradle and CocoaPods to optimize build
    times
  • Uploads built artifacts (APK and IPA) with 30-day retention policy
+125/-0 


CodeAnt-AI Description

Add GitHub Actions workflow to produce APK and IPA artifacts locally

What Changed

  • Added a new GitHub Actions workflow that can be run manually or on version tags to build Android APKs and iOS IPAs on GitHub runners
  • Builds run locally on runners via EAS CLI (no EAS cloud build required), produce build.apk and build.ipa, and are uploaded as downloadable artifacts with 30-day retention
  • Supports selecting platform (all/android/ios) at dispatch, runs Android on ubuntu-latest and iOS on macos-latest in parallel, and caches Gradle and CocoaPods to speed repeated builds; workflow uses repo vars to populate a .env file and requires EXPO_TOKEN for EAS authentication

Impact

✅ Builds produce downloadable APK and IPA artifacts
✅ CI builds run without using EAS cloud credits
✅ Manual, platform-specific workflow dispatch and tag-triggered releases

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Chores
    • Automated build pipeline added for generating Android and iOS application packages. Supports manual triggering with platform selection or automatic builds on version tags. Optimized build process with dependency caching.

Adds .github/workflows/build-apk.yml that:
- Triggers manually (workflow_dispatch) with profile choice (preview/production)
- Triggers automatically on version tags (v*.*.*)
- Builds Android APK using EAS Build cloud service
- Downloads the built APK and uploads it as a GitHub Actions artifact (30-day retention)
- Reuses the existing setup-node-bun-install composite action and staging environment

https://claude.ai/code/session_01AogoKQvPk5RwVsZ52ps165
Replaces the EAS-based build-apk.yml with a self-contained workflow
that builds natively on GitHub Actions runners (no EAS cloud required):

- Android (ubuntu-latest): expo prebuild → assembleDebug → APK artifact
- iOS (macos-latest): expo prebuild → pod install → xcodebuild unsigned archive → IPA artifact

Triggers: manual workflow_dispatch (all/android/ios) or push to v*.*.* tags.
Both jobs run in parallel. Gradle and CocoaPods caches speed up repeat builds.
iOS IPA is unsigned so it can be re-signed by tools like LiveContainer.

https://claude.ai/code/session_01AogoKQvPk5RwVsZ52ps165
Replaces manual Gradle/xcodebuild steps with `eas build --local`, which
runs the EAS build pipeline on the GitHub Actions runner (no cloud credits)
while using EAS for credential management and build configuration.

- Android (ubuntu-latest): eas build --local --platform android --profile preview
- iOS (macos-latest): eas build --local --platform ios --profile preview
- Both jobs use --output flag to produce a predictable artifact path
- expo/expo-github-action installs EAS CLI and authenticates via EXPO_TOKEN

https://claude.ai/code/session_01AogoKQvPk5RwVsZ52ps165
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Feb 23, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Feb 23, 2026

Reviewer's Guide

Adds a new GitHub Actions workflow to locally build Android APK and iOS IPA via EAS, with manual and tag-based triggers, environment setup, caching, and artifact upload for both platforms.

File-Level Changes

Change Details Files
Introduce a unified GitHub Actions workflow to build Android APK and iOS IPA using EAS with manual and tag-based triggers.
  • Add workflow_dispatch input to choose platform (all/android/ios) and push trigger on semantic version tags
  • Configure concurrency to cancel in-progress runs for the same workflow and ref
  • Define separate jobs for Android (Ubuntu) and iOS (macOS) builds gated by the selected platform input
.github/workflows/build-apk-ipa.yml
Set up consistent build environments and secrets/vars handling for Android and iOS EAS local builds.
  • Reuse existing composite action to set up Node, Bun, and install dependencies in both jobs
  • Configure Expo EAS CLI via expo/expo-github-action with EXPO_TOKEN secret
  • Generate .env file at runtime using repository environment variables for Supabase and backend URLs
  • Add Java 17 setup for Android builds using actions/setup-java
.github/workflows/build-apk-ipa.yml
Optimize dependencies and publish build artifacts for both Android APK and iOS IPA outputs.
  • Cache Gradle caches and wrapper for Android builds keyed by OS and package.json hash
  • Cache CocoaPods artifacts for iOS builds keyed by OS and package.json hash
  • Run local EAS builds for Android and iOS with the preview profile and non-interactive mode, outputting build.apk/build.ipa
  • Upload APK/IPA as GitHub Actions artifacts named with platform and commit SHA, with 30-day retention and strict failure if missing
.github/workflows/build-apk-ipa.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Walkthrough

A new GitHub Actions workflow file is introduced to automate building Android APK and iOS IPA artifacts. The workflow supports manual trigger with platform selection or automatic trigger on version tag pushes. It includes separate jobs for Android and iOS builds using EAS CLI with dependency caching.

Changes

Cohort / File(s) Summary
Build Automation
.github/workflows/build-apk-ipa.yml
New workflow file with two jobs: build-android (Ubuntu environment, APK build via EAS CLI with Gradle caching) and build-ios (macOS environment, IPA build via EAS CLI with CocoaPods caching). Both include environment setup, dependency installation, and artifact uploads.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

Review effort 2/5, size:M

Poem

🐰 A workflow hops into place with glee,
Android and iOS build wild and free,
EAS CLI caches the way,
APK and IPA artifacts at play! 📱✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions only Android APK via EAS, but the changeset implements a complete workflow for both Android APK and iOS IPA builds. Update the title to reflect both Android and iOS builds, e.g., 'feat: add GitHub Actions workflow to build Android APK and iOS IPA via EAS local build'.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/setup-github-actions-builds-e2sDl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Copy Markdown

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unpinned GH Actions

Description: The workflow uses multiple GitHub Actions pinned only to mutable version tags (e.g.,
actions/checkout@v4, actions/setup-java@v4, actions/cache@v4, actions/upload-artifact@v4,
expo/expo-github-action@v8), which creates a supply-chain risk because a
compromised/upstream-changed action tag could execute attacker-controlled code in CI with
access to secrets.EXPO_TOKEN and the repository workspace.
build-apk-ipa.yml [34-121]

Referred Code
- name: Checkout
  uses: actions/checkout@v4
  with:
    fetch-depth: 0

- name: Setup Node + Bun + Install Dependencies
  uses: ./.github/actions/setup-node-bun-install

- name: Setup EAS CLI
  uses: expo/expo-github-action@v8
  with:
    eas-version: latest
    token: ${{ secrets.EXPO_TOKEN }}

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

- name: Create .env file


 ... (clipped 67 lines)
Floating tool version

Description: Using a floating tool version (eas-version: latest) can unexpectedly pull a malicious or
vulnerable release of the EAS CLI at runtime, impacting build integrity and potentially
exposing CI secrets via arbitrary code execution during the build.
build-apk-ipa.yml [42-47]

Referred Code
- name: Setup EAS CLI
  uses: expo/expo-github-action@v8
  with:
    eas-version: latest
    token: ${{ secrets.EXPO_TOKEN }}
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Trigger edge cases: The workflow relies on inputs.platform conditionals and hard-codes --profile preview,
which may behave unexpectedly on tag pushes (where inputs may be unset) and does not
implement the advertised profile selection, requiring verification and/or guard defaults.

Referred Code
if: ${{ inputs.platform != 'ios' }}
environment:
  name: staging

steps:
  - name: Checkout
    uses: actions/checkout@v4
    with:
      fetch-depth: 0

  - name: Setup Node + Bun + Install Dependencies
    uses: ./.github/actions/setup-node-bun-install

  - name: Setup EAS CLI
    uses: expo/expo-github-action@v8
    with:
      eas-version: latest
      token: ${{ secrets.EXPO_TOKEN }}

  - name: Setup Java 17
    uses: actions/setup-java@v4


 ... (clipped 21 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Build config inputs: The workflow description claims a manual profile choice but the implementation accepts
only platform and always uses --profile preview, so confirm intended behavior and ensure
any future user-provided inputs are validated and mapped safely before being passed to CLI
commands.

Referred Code
  workflow_dispatch:
    inputs:
      platform:
        description: "Platform to build"
        required: true
        default: "all"
        type: choice
        options:
          - all
          - android
          - ios

  push:
    tags:
      - "v*.*.*"

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

jobs:


 ... (clipped 93 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The if: ${{ inputs.platform != 'ios' }} / != 'android' conditions rely on inputs.platform, which is only present for workflow_dispatch events; for tag push events this input is undefined—consider guarding on github.event_name == 'workflow_dispatch' or using a default/fallback so the jobs behave predictably on tag pushes.
  • Both Android and iOS builds are hardcoded to use the preview EAS profile; if you intend different behavior for tag builds or want flexibility, consider making the profile configurable via workflow inputs or derived from the tag/branch.
  • The Gradle and CocoaPods cache keys are based on package.json; using more specific lockfiles (e.g., Gradle wrapper/build.gradle, Podfile.lock) would make cache invalidation better aligned with the actual native dependency changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `if: ${{ inputs.platform != 'ios' }}` / `!= 'android'` conditions rely on `inputs.platform`, which is only present for `workflow_dispatch` events; for tag `push` events this input is undefined—consider guarding on `github.event_name == 'workflow_dispatch'` or using a default/fallback so the jobs behave predictably on tag pushes.
- Both Android and iOS builds are hardcoded to use the `preview` EAS profile; if you intend different behavior for tag builds or want flexibility, consider making the profile configurable via workflow inputs or derived from the tag/branch.
- The Gradle and CocoaPods cache keys are based on `package.json`; using more specific lockfiles (e.g., Gradle wrapper/`build.gradle`, `Podfile.lock`) would make cache invalidation better aligned with the actual native dependency changes.

## Individual Comments

### Comment 1
<location> `.github/workflows/build-apk-ipa.yml:60-65` </location>
<code_context>
+          path: |
+            ~/.gradle/caches
+            ~/.gradle/wrapper
+          key: ${{ runner.os }}-gradle-${{ hashFiles('package.json') }}
+          restore-keys: ${{ runner.os }}-gradle-
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Gradle cache key should be based on Gradle-related files instead of `package.json`.

Keying the Gradle cache off `package.json` means changes in `android/build.gradle`, `android/app/build.gradle`, or `gradle-wrapper.properties` won’t invalidate the cache, while unrelated JS changes will. Prefer something like:

```yaml
key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/build.gradle', 'android/**/gradle-wrapper.properties') }}
```

Optionally include `package.json` too, but ensure Gradle files are part of the hash so cache invalidation matches Gradle dependency changes.

```suggestion
      - name: Cache Gradle dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/build.gradle', 'android/**/gradle-wrapper.properties', 'package.json') }}
          restore-keys: ${{ runner.os }}-gradle-
```
</issue_to_address>

### Comment 2
<location> `.github/workflows/build-apk-ipa.yml:109-112` </location>
<code_context>
+        uses: actions/cache@v4
+        with:
+          path: ~/Library/Caches/CocoaPods
+          key: ${{ runner.os }}-cocoapods-${{ hashFiles('package.json') }}
+          restore-keys: ${{ runner.os }}-cocoapods-
+
</code_context>

<issue_to_address>
**suggestion (performance):** CocoaPods cache key would be more precise if it used Pod-related files instead of `package.json`.

CocoaPods resolution is driven by `ios/Podfile` and especially `ios/Podfile.lock`, not `package.json`. Using `package.json` here can cause both unnecessary cache invalidations and stale CocoaPods caches. Prefer something like:

```yaml
key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock', 'ios/Podfile') }}
```

```suggestion
      - name: Cache CocoaPods
        uses: actions/cache@v4
        with:
          path: ~/Library/Caches/CocoaPods
          key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock', 'ios/Podfile') }}
          restore-keys: |
            ${{ runner.os }}-cocoapods-
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +60 to +65
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Gradle cache key should be based on Gradle-related files instead of package.json.

Keying the Gradle cache off package.json means changes in android/build.gradle, android/app/build.gradle, or gradle-wrapper.properties won’t invalidate the cache, while unrelated JS changes will. Prefer something like:

key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/build.gradle', 'android/**/gradle-wrapper.properties') }}

Optionally include package.json too, but ensure Gradle files are part of the hash so cache invalidation matches Gradle dependency changes.

Suggested change
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/build.gradle', 'android/**/gradle-wrapper.properties', 'package.json') }}
restore-keys: ${{ runner.os }}-gradle-

Comment on lines +109 to +112
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: ~/Library/Caches/CocoaPods
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): CocoaPods cache key would be more precise if it used Pod-related files instead of package.json.

CocoaPods resolution is driven by ios/Podfile and especially ios/Podfile.lock, not package.json. Using package.json here can cause both unnecessary cache invalidations and stale CocoaPods caches. Prefer something like:

key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock', 'ios/Podfile') }}
Suggested change
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: ~/Library/Caches/CocoaPods
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: ~/Library/Caches/CocoaPods
key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock', 'ios/Podfile') }}
restore-keys: |
${{ runner.os }}-cocoapods-

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Feb 23, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Feb 23, 2026

CodeAnt AI finished reviewing your PR.

@qodo-code-review
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Use EAS cloud builds instead

Instead of building natively on GitHub runners with eas build --local, consider
using the standard EAS Build cloud service. This simplifies the workflow,
reduces maintenance, and can lower costs by offloading the build process.

Examples:

.github/workflows/build-apk-ipa.yml [70]
        run: eas build --local --platform android --profile preview --non-interactive --output build.apk
.github/workflows/build-apk-ipa.yml [117]
        run: eas build --local --platform ios --profile preview --non-interactive --output build.ipa

Solution Walkthrough:

Before:

jobs:
  build-android:
    runs-on: ubuntu-latest
    steps:
      - name: Setup Java 17
        ...
      - name: Cache Gradle dependencies
        ...
      - name: Build APK (local)
        run: eas build --local --platform android ...
      - name: Upload APK
        uses: actions/upload-artifact@v4
  build-ios:
    runs-on: macos-latest
    steps:
      - name: Cache CocoaPods
        ...
      - name: Build IPA (local)
        run: eas build --local --platform ios ...
      - name: Upload IPA
        uses: actions/upload-artifact@v4

After:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        ...
      - name: Setup Node + Bun + Install Dependencies
        ...
      - name: Setup EAS CLI
        ...
      # No need for Java setup, or platform-specific caching
      - name: Start EAS Cloud Build
        run: eas build --platform all --profile preview --non-interactive
      # The workflow would then end, or could optionally wait
      # for the build and download the artifact from EAS.
Suggestion importance[1-10]: 9

__

Why: This is a high-impact architectural suggestion that correctly identifies the trade-offs of local vs. cloud builds, proposing a simpler, more standard, and potentially cheaper solution.

High
General
Cache based on Gradle files

Update the Gradle cache key to be based on Android-specific Gradle files instead
of package.json.

.github/workflows/build-apk-ipa.yml [66]

-key: ${{ runner.os }}-gradle-${{ hashFiles('package.json') }}
+key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion correctly improves the Gradle caching strategy by keying on Gradle-specific files, which is more accurate than hashing package.json for native Android dependencies.

Medium
Cache based on Podfile.lock

Update the CocoaPods cache key to hash ios/Podfile.lock instead of package.json
for more accurate caching.

.github/workflows/build-apk-ipa.yml [113]

-key: ${{ runner.os }}-cocoapods-${{ hashFiles('package.json') }}
+key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock') }}
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion provides a more accurate caching strategy for CocoaPods by using Podfile.lock, which directly reflects changes in native iOS dependencies, unlike package.json.

Medium
Handle push events without inputs

Refactor the build-android job's if condition to explicitly handle both
workflow_dispatch and push events.

.github/workflows/build-apk-ipa.yml [29]

-if: ${{ inputs.platform != 'ios' }}
+if: ${{ (github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == 'android')) || (github.event_name == 'push') }}
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out that the existing if condition relies on implicit behavior and provides a more explicit and readable condition, improving the workflow's maintainability.

Low
  • More

@coderabbitai coderabbitai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Feb 23, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
.github/workflows/build-apk-ipa.yml (5)

20-22: Verify cancel-in-progress: true is appropriate for both tag and manual triggers.

The concurrency group is ${{ github.workflow }}-${{ github.ref }}. For different tags (e.g., refs/tags/v1.0.0 vs refs/tags/v1.0.1) the groups are distinct, so cross-tag cancellation won't occur. However, if the same tag is somehow re-triggered (e.g., a manual re-run after a runner failure), cancel-in-progress: true will cancel the in-flight build. For release tags where a completed artifact is required, you may want to set this to false, or scope it differently for push vs workflow_dispatch events.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml around lines 20 - 22, The workflow's
concurrency uses group: `${{ github.workflow }}-${{ github.ref }}` with
`cancel-in-progress: true`, which can cancel in-flight builds for the same ref
(e.g., manual re-runs of a tag); update the concurrency config to avoid unwanted
cancellation for release/tag runs by either setting `cancel-in-progress: false`
for tag-triggered runs or by separating groups based on event type (e.g.,
include `${{ github.event_name }}` or conditionally set `cancel-in-progress` for
`workflow_dispatch` vs `push`), modifying the `concurrency` block and its
`group`/`cancel-in-progress` settings accordingly so tag releases are not
canceled unintentionally.

109-114: CocoaPods cache key hashes package.json instead of ios/Podfile.lock.

The authoritative source for CocoaPods dependency resolution is ios/Podfile.lock. Hashing package.json means the cache won't be invalidated when pod versions change independently of JS packages.

♻️ Proposed fix
-          key: ${{ runner.os }}-cocoapods-${{ hashFiles('package.json') }}
+          key: ${{ runner.os }}-cocoapods-${{ hashFiles('ios/Podfile.lock') }}
           restore-keys: ${{ runner.os }}-cocoapods-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml around lines 109 - 114, The "Cache
CocoaPods" step is using hashFiles('package.json') for the cache key so
CocoaPods caches won't invalidate when iOS Pod dependencies change; update the
key expression in that step (the key under the step named "Cache CocoaPods" that
currently uses hashFiles('package.json')) to hashFiles('ios/Podfile.lock') so
the cache is tied to the Podfile.lock content (you can keep the same
restore-keys pattern and actions/cache@v4 usage).

54-58: Duplicated .env creation block in both jobs.

The three-line .env injection is identical in build-android and build-ios. Consider extracting it into the existing .github/actions/setup-node-bun-install composite action (if env setup is always needed there), or into a dedicated composite action, to avoid divergence.

Also applies to: 103-107

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml around lines 54 - 58, The .env creation
block is duplicated in both build-android and build-ios jobs; move the three
echo lines into the existing composite action
.github/actions/setup-node-bun-install (or create a new composite action like
.github/actions/setup-env) and have both jobs call that action instead of
repeating the lines. Update the composite action to accept vars.EXPO_PUBLIC_* as
inputs or read from environment and write the same .env file, then remove the
echoed .env lines from the build-android and build-ios job steps so the env
setup is centralized and DRY.

60-67: Gradle cache key hashes package.json instead of Gradle build files.

package.json changes do not track Android native dependency changes (e.g., edits to android/build.gradle or android/app/build.gradle). This will produce stale cache hits after Gradle dependency updates and misses after irrelevant JS package changes.

♻️ Proposed fix
         key: ${{ runner.os }}-gradle-${{ hashFiles('package.json') }}
+        key: ${{ runner.os }}-gradle-${{ hashFiles('android/**/*.gradle*', 'android/gradle/wrapper/gradle-wrapper.properties') }}
         restore-keys: ${{ runner.os }}-gradle-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml around lines 60 - 67, The Gradle cache
step ("name: Cache Gradle dependencies" using actions/cache@v4) currently keys
the cache by hashFiles('package.json'), which causes JS changes to influence the
Gradle cache; change the key to hash the Android Gradle files instead (for
example use
hashFiles('android/build.gradle','android/app/build.gradle','android/settings.gradle','android/gradle.properties','android/gradle/wrapper/gradle-wrapper.properties'))
so the key becomes something like ${{ runner.os }}-gradle-${{ hashFiles(...) }},
and keep a broader restore-keys like ${{ runner.os }}-gradle- to allow partial
restores.

116-117: Consider adding App Store Connect API key for credential repair resilience.

The eas build --local --non-interactive command retrieves iOS credentials from EAS servers using EXPO_TOKEN (line 101) without interactive Apple login. If credentials in EAS need repair—such as an expired provisioning profile or re-signed certificate—eas build will fail silently because --non-interactive prevents prompts and no EXPO_ASC_API_KEY_PATH or EXPO_ASC_API_KEY_ID/EXPO_ASC_API_KEY_ISSUER_ID are provided for automated credential repair.

This is only a concern if credentials are not pre-registered or need periodic maintenance in EAS. For added resilience, optionally pass App Store Connect API credentials as environment variables to the Build IPA (local) step when credentials may require repair.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml around lines 116 - 117, The "Build IPA
(local)" step runs "eas build --local --platform ios --profile preview
--non-interactive --output build.ipa" and can fail silently if EAS needs to
repair iOS credentials; update this step to accept App Store Connect API
credentials so automated repair can occur by supplying EXPO_ASC_API_KEY_PATH (or
EXPO_ASC_API_KEY_ID and EXPO_ASC_API_KEY_ISSUER_ID) alongside the existing
EXPO_TOKEN environment variable, and ensure the workflow conditionally uses
these env vars (or secrets) when present so "eas build" can perform credential
repair non-interactively.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/build-apk-ipa.yml:
- Line 70: Add a new workflow_dispatch input named "profile" (default
"preview"), create a job-level env var PROFILE that chooses "production" when
the run is a version tag (e.g., startsWith(github.ref, 'refs/tags/v')) otherwise
uses the provided inputs.profile or "preview", and replace the hardcoded
"--profile preview" in both eas build run steps with "--profile ${{ env.PROFILE
}}"; refer to the existing workflow_dispatch definition, the run lines that
invoke "eas build --local --platform android --profile preview --profile ..."
and the job env PROFILE to locate where to add the input, expression, and
substitution.

---

Nitpick comments:
In @.github/workflows/build-apk-ipa.yml:
- Around line 20-22: The workflow's concurrency uses group: `${{ github.workflow
}}-${{ github.ref }}` with `cancel-in-progress: true`, which can cancel
in-flight builds for the same ref (e.g., manual re-runs of a tag); update the
concurrency config to avoid unwanted cancellation for release/tag runs by either
setting `cancel-in-progress: false` for tag-triggered runs or by separating
groups based on event type (e.g., include `${{ github.event_name }}` or
conditionally set `cancel-in-progress` for `workflow_dispatch` vs `push`),
modifying the `concurrency` block and its `group`/`cancel-in-progress` settings
accordingly so tag releases are not canceled unintentionally.
- Around line 109-114: The "Cache CocoaPods" step is using
hashFiles('package.json') for the cache key so CocoaPods caches won't invalidate
when iOS Pod dependencies change; update the key expression in that step (the
key under the step named "Cache CocoaPods" that currently uses
hashFiles('package.json')) to hashFiles('ios/Podfile.lock') so the cache is tied
to the Podfile.lock content (you can keep the same restore-keys pattern and
actions/cache@v4 usage).
- Around line 54-58: The .env creation block is duplicated in both build-android
and build-ios jobs; move the three echo lines into the existing composite action
.github/actions/setup-node-bun-install (or create a new composite action like
.github/actions/setup-env) and have both jobs call that action instead of
repeating the lines. Update the composite action to accept vars.EXPO_PUBLIC_* as
inputs or read from environment and write the same .env file, then remove the
echoed .env lines from the build-android and build-ios job steps so the env
setup is centralized and DRY.
- Around line 60-67: The Gradle cache step ("name: Cache Gradle dependencies"
using actions/cache@v4) currently keys the cache by hashFiles('package.json'),
which causes JS changes to influence the Gradle cache; change the key to hash
the Android Gradle files instead (for example use
hashFiles('android/build.gradle','android/app/build.gradle','android/settings.gradle','android/gradle.properties','android/gradle/wrapper/gradle-wrapper.properties'))
so the key becomes something like ${{ runner.os }}-gradle-${{ hashFiles(...) }},
and keep a broader restore-keys like ${{ runner.os }}-gradle- to allow partial
restores.
- Around line 116-117: The "Build IPA (local)" step runs "eas build --local
--platform ios --profile preview --non-interactive --output build.ipa" and can
fail silently if EAS needs to repair iOS credentials; update this step to accept
App Store Connect API credentials so automated repair can occur by supplying
EXPO_ASC_API_KEY_PATH (or EXPO_ASC_API_KEY_ID and EXPO_ASC_API_KEY_ISSUER_ID)
alongside the existing EXPO_TOKEN environment variable, and ensure the workflow
conditionally uses these env vars (or secrets) when present so "eas build" can
perform credential repair non-interactively.

restore-keys: ${{ runner.os }}-gradle-

- name: Build APK (local)
run: eas build --local --platform android --profile preview --non-interactive --output build.apk
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded --profile preview prevents production builds from ever being triggered.

Both build commands hard-code --profile preview. Version-tag pushes (v*.*.*) signal a production release intent, yet they will always produce a preview artifact. There is also no profile input on workflow_dispatch, so there is no path to a production build through this workflow at all.

Add a profile input to workflow_dispatch and derive it dynamically for tag-triggered runs:

🛠 Proposed fix – add `profile` input and use it in both build commands
   workflow_dispatch:
     inputs:
       platform:
         description: "Platform to build"
         required: true
         default: "all"
         type: choice
         options:
           - all
           - android
           - ios
+      profile:
+        description: "EAS build profile"
+        required: true
+        default: "preview"
+        type: choice
+        options:
+          - preview
+          - production

Then add a job-level env (or evaluate inline) to pick the profile at run time:

+env:
+  BUILD_PROFILE: ${{ github.event_name == 'push' && 'production' || inputs.profile }}
-        run: eas build --local --platform android --profile preview --non-interactive --output build.apk
+        run: eas build --local --platform android --profile ${{ env.BUILD_PROFILE }} --non-interactive --output build.apk
-        run: eas build --local --platform ios --profile preview --non-interactive --output build.ipa
+        run: eas build --local --platform ios --profile ${{ env.BUILD_PROFILE }} --non-interactive --output build.ipa

Also applies to: 117-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-apk-ipa.yml at line 70, Add a new workflow_dispatch
input named "profile" (default "preview"), create a job-level env var PROFILE
that chooses "production" when the run is a version tag (e.g.,
startsWith(github.ref, 'refs/tags/v')) otherwise uses the provided
inputs.profile or "preview", and replace the hardcoded "--profile preview" in
both eas build run steps with "--profile ${{ env.PROFILE }}"; refer to the
existing workflow_dispatch definition, the run lines that invoke "eas build
--local --platform android --profile preview --profile ..." and the job env
PROFILE to locate where to add the input, expression, and substitution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review effort 2/5 size:L This PR changes 100-499 lines, ignoring generated files size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants