Auto Choose Live Update or Native Build
Copy a setup prompt with the install steps and the full markdown guide for this plugin.
Most Capacitor releases are JavaScript-only and should ship as a live update. Some changes touch native code and need a new binary from Capgo Build. This guide shows how to make GitHub Actions, GitLab CI, or any other CI/CD platform pick the correct path on every push — without a human deciding.
The Decision
Section titled “The Decision”Capgo already knows which path is safe. After your web build (and before you upload or request a native build), run:
npx @capgo/cli@latest bundle releaseType com.example.app --channel production# → OTA ship with bundle upload# → native ship with Capgo BuildOTA means the native packages match what is already live on the channel. native means a plugin, Capacitor version, or other native dependency changed — an over-the-air bundle alone cannot update those devices safely.
releaseType compares native package metadata (Capacitor/Cordova plugins and versions). It does not see every edit under ios/, android/, or capacitor.config.*. Gate those paths in git first, then use releaseType for dependency compatibility — the examples below do both.
See Native Compatibility for the full rules and the manual bundle compatibility table.
Prerequisites
Section titled “Prerequisites”- Capgo app registered and a Capgo API key in CI secrets as
CAPGO_TOKEN - Live Updates upload working (
bundle upload) — see CI/CD Integration - Capgo Build credentials in CI if you expect native jobs — see GitHub Actions or Credentials
- A channel name that matches production (examples use
production)
How the Pipeline Should Work
Section titled “How the Pipeline Should Work”flowchart TD A[Push / merge] --> B[Install + web build] B --> C["bundle releaseType"] C -->|OTA| D["bundle upload"] C -->|native| E["build request iOS + Android"] E --> F[Store / TestFlight / Play]
- Build web assets as usual.
- If the commit touches
ios/,android/, orcapacitor.config.*, force the native path. - Otherwise ask Capgo
releaseTypewhether the commit is OTA-safe. - If
OTA, upload the bundle (with--fail-on-incompatibleas a safety rail). - If
native, run Capgo Build, then upload the matching bundle with--auto-min-update-versionso the channel’s native metadata advances (use the metadata strategy). Do not use--fail-on-incompatiblefor that baseline upload — the new native packages are supposed to differ.
GitHub Actions
Section titled “GitHub Actions”One workflow that gates native paths, then branches on releaseType:
name: Capgo Release
on: push: branches: [main]
jobs: decide: runs-on: ubuntu-latest outputs: release_type: ${{ steps.verdict.outputs.type }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm'
- run: npm ci - run: npm run build
- name: Decide OTA vs native id: verdict env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | BEFORE="${{ github.event.before }}" if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then BEFORE="$(git rev-parse HEAD~1 2>/dev/null || echo '')" fi if [ -z "$BEFORE" ] || git diff --name-only "$BEFORE" "${{ github.sha }}" \ | grep -qE '^(ios/|android/|capacitor\.config\.)'; then TYPE=native echo "Native path/config changed (or no prior commit) — forcing native" else TYPE=$(npx @capgo/cli@latest bundle releaseType com.example.app --channel production | tr -d '[:space:]') fi echo "type=$TYPE" >> "$GITHUB_OUTPUT" echo "Capgo release type: $TYPE"
live_update: needs: decide if: needs.decide.outputs.release_type == 'OTA' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: npm ci - run: npm run build - name: Upload live update env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | npx @capgo/cli@latest bundle upload com.example.app \ --channel production \ --fail-on-incompatible
native_build: needs: decide if: needs.decide.outputs.release_type == 'native' runs-on: ubuntu-latest strategy: fail-fast: false matrix: platform: [ios, android] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: npm ci - run: npm run build - run: npx cap sync ${{ matrix.platform }} - name: Capgo Build ${{ matrix.platform }} env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} CAPGO_IOS_PROVISIONING_MAP: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP }} APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }} APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }} APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }} ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE_FILE }} KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }} KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} PLAY_CONFIG_JSON: ${{ secrets.PLAY_CONFIG_JSON }} run: | npx @capgo/cli@latest build request com.example.app \ --platform ${{ matrix.platform }} \ --build-mode release
native_bundle: needs: [decide, native_build] if: needs.decide.outputs.release_type == 'native' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v6 with: node-version: '24' cache: 'npm' - run: npm ci - run: npm run build - name: Upload bundle for new native baseline env: CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }} run: | # One-time: channel set production --disable-auto-update metadata npx @capgo/cli@latest bundle upload com.example.app \ --channel production \ --auto-min-update-versionReplace com.example.app and wire the signing secrets as described in GitHub Actions for Capgo Build.
GitLab CI
Section titled “GitLab CI”GitLab evaluates rules when the pipeline is created, so branch with a shell if inside one deploy job (or generate a dynamic child pipeline if you need separate native matrix jobs):
image: node:24
stages: - build - deploy
variables: APP_ID: com.example.app CHANNEL: production
build_web: stage: build script: - npm ci - npm run build artifacts: paths: - dist/ - node_modules/ expire_in: 1 hour only: - main
deploy: stage: deploy needs: [build_web] script: - | BEFORE="${CI_COMMIT_BEFORE_SHA:-}" if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then BEFORE="$(git rev-parse HEAD~1 2>/dev/null || echo '')" fi if [ -z "$BEFORE" ] || git diff --name-only "$BEFORE" "$CI_COMMIT_SHA" \ | grep -qE '^(ios/|android/|capacitor\.config\.)'; then TYPE=native else TYPE=$(npx @capgo/cli@latest bundle releaseType "$APP_ID" --channel "$CHANNEL" | tr -d '[:space:]') fi echo "Capgo release type: $TYPE" if [ "$TYPE" = "OTA" ]; then npx @capgo/cli@latest bundle upload "$APP_ID" \ --channel "$CHANNEL" \ --fail-on-incompatible elif [ "$TYPE" = "native" ]; then npx cap sync npx @capgo/cli@latest build request "$APP_ID" --platform ios --build-mode release npx @capgo/cli@latest build request "$APP_ID" --platform android --build-mode release npx @capgo/cli@latest bundle upload "$APP_ID" \ --channel "$CHANNEL" \ --auto-min-update-version else echo "Unexpected release type: $TYPE" >&2 exit 1 fi only: - mainStore CAPGO_TOKEN and Capgo Build signing variables as masked/protected CI/CD variables.
Other CI Platforms
Section titled “Other CI Platforms”The same three steps work anywhere:
| Step | Command |
|---|---|
| Verdict | npx @capgo/cli@latest bundle releaseType APP_ID --channel production |
| OTA path | npx @capgo/cli@latest bundle upload APP_ID --channel production --fail-on-incompatible |
| Native path | npx @capgo/cli@latest build request APP_ID --platform ios (or android) --build-mode release |
Map the shell exit / stdout into your platform’s conditionals (or keep a single job with a shell if, like GitLab above):
- Azure Pipelines — set an output variable from a script step, then use
condition: eq(variables['releaseType'], 'OTA') - Bitbucket Pipelines — write
RELEASE_TYPE=…to$BITBUCKET_PIPELINES_VARIABLES_PATH, declare it underoutput-variables, and branch later steps withcondition: state: RELEASE_TYPE == "OTA"(file artifacts alone cannot drivecondition) - CircleCI —
whenis evaluated at config-compile time, so branch with a runtime shellif(or dynamic config / continuation), not a workspace value inwhen - Jenkins — capture stdout into an env var and use
when { environment name: 'RELEASE_TYPE', value: 'OTA' }
Paths Filters (Optional Speedup)
Section titled “Paths Filters (Optional Speedup)”Path filters are a cost optimization, not a substitute for the Capgo check. Prefer excluding docs-only paths rather than maintaining a fragile allowlist — web builds often also depend on vite.config.*, tsconfig*.json, and framework config files:
on: push: branches: [main] paths-ignore: - '**.md' - 'docs/**' - '.github/**'If you use an allowlist instead, include every input your web and native builds read, not only src/ and package.json.
After a Native Verdict
Section titled “After a Native Verdict”When CI chooses native:
- Capgo Build produces signed binaries and can submit to TestFlight / Play (see configuration).
- Upload the matching JS bundle with
--auto-min-update-version(metadata strategy) so the channel records the new native packages — otherwise the next JS-only commit still returnsnative. - Once users install the new binary, later JavaScript-only commits return to
OTAagain.