Building mobile apps with Apache Cordova means you can write once and ship to both Android and iOS. That is a huge win for teams short on resources. But the build step can become a bottleneck. Running cordova build android and cordova build ios on your local machine for every commit is slow, error-prone, and ties up your computer. Automating Cordova builds with GitHub Actions turns this repetitive task into a hands-free pipeline. Your team pushes code, and GitHub spins up a fresh environment, compiles your app, and hands you a signed APK or IPA. No manual steps, no forgotten signing keys, no “works on my machine” moments.

Key Takeaway

By the end of this guide, you will have a working GitHub Actions workflow that automatically builds signed Android and iOS apps from your Cordova project. You will learn how to set up secrets for certificates and provisioning profiles, cache dependencies to speed up builds, and handle common pitfalls. This approach saves hours each week and ensures every build is consistent and repeatable.

Why Automate Cordova Builds with GitHub Actions

Manual builds are fine for a small project with infrequent updates. Once you have a team pushing multiple times a day, the pain grows. Here is what automation gives you:

  • Consistent environments – Every build runs on the same Ubuntu or macOS runner. No more “it worked on my machine.”
  • Immediate feedback – A broken build shows up in a pull request check, not after you have already merged.
  • Faster releases – With signing integrated, you can generate a release candidate with one push.
  • Security – Keystore passwords and Apple certificates stay in encrypted GitHub Secrets, not on anyone’s laptop.
  • Audit trail – Every build is logged, timestamped, and linked to a commit.

If you are new to CI/CD for hybrid apps, you might also want to read about enhance your Cordova apps with advanced debugging techniques to catch issues before they reach the build server.

Prerequisites

Before writing your workflow file, make sure you have the following ready:

  1. A Cordova project hosted on GitHub. Your config.xml and www folder should be committed.
  2. A GitHub account with Actions enabled (free tier includes 2,000 minutes per month for private repos).
  3. For Android: a keystore file (.jks or .keystore) and the associated passwords. If you do not have one, generate it with keytool.
  4. For iOS: an Apple Developer account, a distribution certificate (.p12), and a provisioning profile (.mobileprovision). You will need a macOS runner for iOS builds.
  5. Basic familiarity with YAML syntax.

Setting Up Your First GitHub Actions Workflow

A workflow is a YAML file stored in .github/workflows/ inside your repository. Let us build one step by step.

Step 1: Define the Trigger and Platform

Start by naming the workflow and telling it when to run. For this example, we will run on every push to the main branch and on pull requests.

name: Build Cordova App

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

Step 2: Choose a Runner and Check Out Code

You need a Linux runner for Android and a macOS runner for iOS. You can use a matrix strategy to build both in parallel.

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        include:
          - os: ubuntu-latest
            platform: android
          - os: macos-latest
            platform: ios

    steps:
      - uses: actions/checkout@v4

Step 3: Install Dependencies

Cordova requires Node.js, the Cordova CLI, and platform-specific SDKs. Use a community action to set up Node, then install Cordova globally.

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install Cordova CLI
        run: npm install -g cordova

      - name: Install project dependencies
        run: npm ci

For Android, the ubuntu-latest runner already has the Android SDK. However, you may need to set the ANDROID_HOME environment variable. For macOS runners, Xcode is pre-installed but you must install CocoaPods if your project uses plugins that rely on it.

      - name: Install CocoaPods (macOS only)
        if: runner.os == 'macOS'
        run: |
          sudo gem install cocoapods
          pod repo update

Step 4: Add the Platforms

Add the Android and iOS platforms to your Cordova project. This step also restores the platforms if they are already in package.json.

      - name: Add platforms
        run: |
          cordova platform add android --no-telemetry
          if [ "${{ matrix.platform }}" == "ios" ]; then cordova platform add ios --no-telemetry; fi

Step 5: Inject Signing Secrets

For Android, you need to Base64 encode your keystore file and store it as a secret. Then decode it during the build.

      - name: Decode Android keystore
        if: matrix.platform == 'android'
        run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > release.keystore

For iOS, you will need the certificate and provisioning profile. Store them as secrets as well and import them into the keychain.

      - name: Setup iOS signing (macOS only)
        if: runner.os == 'macOS'
        env:
          IOS_CERTIFICATE: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
          IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          echo "$IOS_CERTIFICATE" | base64 --decode > cert.p12
          echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision
          security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
          security import cert.p12 -k build.keychain -P "$KEYCHAIN_PASSWORD" -T /usr/bin/codesign
          security default-keychain -s build.keychain
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/

Step 6: Build and Archive

Now assemble the app. For Android, output an APK or AAB. For iOS, create an .xcarchive and export an IPA.

      - name: Build Android
        if: matrix.platform == 'android'
        run: |
          cordova build android --release -- --keystore=release.keystore \
            --storePassword=${{ secrets.ANDROID_STORE_PASSWORD }} \
            --alias=${{ secrets.ANDROID_KEY_ALIAS }} \
            --password=${{ secrets.ANDROID_KEY_PASSWORD }}

      - name: Build iOS
        if: matrix.platform == 'ios'
        run: |
          cordova build ios --release --device \
            --codeSignIdentity="iPhone Distribution" \
            --developmentTeam=${{ secrets.IOS_TEAM_ID }}
          xcodebuild -workspace platforms/ios/YourApp.xcworkspace \
            -scheme YourApp -configuration Release \
            -archivePath build/YourApp.xcarchive archive \
            -allowProvisioningUpdates
          xcodebuild -exportArchive -archivePath build/YourApp.xcarchive \
            -exportPath build/ -exportOptionsPlist exportOptions.plist \
            -allowProvisioningUpdates

Step 7: Upload Artifacts

Make the build output available as a downloadable artifact so you can install it on a device or submit to stores.

      - name: Upload Android APK
        if: matrix.platform == 'android'
        uses: actions/upload-artifact@v4
        with:
          name: android-app
          path: platforms/android/app/build/outputs/apk/release/*.apk

      - name: Upload iOS IPA
        if: matrix.platform == 'ios'
        uses: actions/upload-artifact@v4
        with:
          name: ios-app
          path: build/*.ipa

The full workflow file can be extended with caching, linting, and tests. If you want to learn more about plugin compatibility, check out mastering Cordova plugin development for cross-platform compatibility.

Common Pitfalls and How to Avoid Them

Even with a solid workflow, a few gotchas can trip you up. Here is a table of frequent mistakes and solutions.

Pitfall Why It Happens Fix
Build fails with “No suitable Android SDK found” GitHub’s Ubuntu runner has multiple Android SDK versions; the environment variable may be missing. Add echo "ANDROID_HOME=$ANDROID_HOME" to your step and ensure ANDROID_HOME is set. You can use the android-actions/setup-android action.
iOS build fails on signing The certificate or provisioning profile is expired or not installed properly. Verify the certificate date, re-upload secrets, and remember that macOS runners have a 7-day limit for Xcode certificates.
Node modules not found Dependencies are not installed because npm ci was skipped after caching. Use actions/cache for node_modules but ensure npm ci runs on cache miss.
Cordova plugin fails on iOS A plugin expects CocoaPods but pod repo update did not run. Add the CocoaPods step shown earlier. If your plugin requires a specific iOS deployment target, set it in config.xml.
Secrets exposed in logs Running a script that echoes a secret value. Never use echo ${{ secrets.NAME }} directly. Instead, pass secrets as environment variables.

Expert tip: Always test your workflow with a small, non-production app first. Use a throwaway branch and a test keystore. Once you see the green check mark, move to your real signing credentials.

Caching Dependencies to Speed Up Builds

Cordova builds can take 10–15 minutes per platform. Caching Node modules and platform SDKs cuts that down significantly. Add this before any install steps:

      - uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            node_modules
            platforms/android/.gradle
            ~/.gradle/caches
          key: ${{ runner.os }}-${{ hashFiles('package-lock.json', 'config.xml') }}
          restore-keys: |
            ${{ runner.os }}-

For iOS, you can also cache the DerivedData folder and CocoaPods spec repo. The exact paths depend on your project. Experiment and adjust.

When to Use a Separate Workflow for Release Builds

If your main branch flow is for debug builds, consider a second workflow that triggers only on tags or manual dispatch. This release workflow would include signing with your production certificates and possibly upload to app stores. For a deeper look at deployment, see streamlining app deployment for Apache Cordova projects.

Set up a matrix that runs only on macOS for iOS and Ubuntu for Android, and add steps to sign with your store credentials. You can then use actions like r0adkll/upload-google-play for Android or apple-actions/upload-testflight-build for iOS.

Testing Your Pipeline

After you push your workflow file, go to the Actions tab in your GitHub repository. You will see the workflow kick off. Watch the logs for each step. Common errors include:

  • Missing secrets – the build will fail with “secret not found”.
  • Incorrect keystore path – update the path in the build command.
  • Platform mismatches – ensure you only build iOS on macOS.

Once the build succeeds, download the artifact and install it on your device. If you see any runtime issues, those are often related to plugins. You can debug them using essential debugging tips for hybrid Cordova applications.

Making Automation a Habit

Setting up automated Cordova builds with GitHub Actions is one of those investments that pays off on day two. You stop wasting time on manual steps. Your team gains confidence because every build is created the same way. And when a build breaks, you know immediately what changed.

Start with a simple workflow that builds one platform. Add the second platform after it passes. Then introduce caching, signing, and artifact uploads. Before long, you will wonder how you ever lived without it.

Go ahead and create that .github/workflows/build.yml file now. Your future self will thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Post