ionic-appflow-migration ยท diff
git:20260501.ed3e92e to git:20261011.86834d4
98 added, 53 removed. Audit A to A.
---
name: ionic-appflow-migration
- description: Guides the agent through migrating an existing Ionic or Capacitor project away from Ionic Appflow. Use when detecting Appflow live updates, cloud builds, or store deployment flows and replacing them with Capgo live updates plus the repository's CI/CD and store publishing setup. Do not use for Ionic Enterprise SDK plugin migration or for setting up a fresh Capacitor project from scratch.
+ description: Migrates an Ionic or Capacitor project off Ionic Appflow (access ends Dec 31, 2027). Use when the repo contains @capacitor/live-updates, cordova-plugin-ionic, a plugins.LiveUpdates block (appId, channel, autoUpdateMethod, maxVersions), LiveUpdates.sync()/reload() calls, ionic deploy or appflow CLI commands, .io-config.json, or Appflow build/store-deploy workflows, and the user wants Capgo live updates, Capgo Cloud Build or repo-owned CI builds, and repo-owned store publishing. Maps Appflow channels, update strategies, and API calls to @capgo/capacitor-updater. Do not use for Ionic Enterprise plugins such as Identity Vault, Auth Connect, or Secure Storage (ionic-enterprise-sdk-migration), Capawesome live updates (capawesome-live-update-migration), or a fresh Capgo setup with no Appflow history (capgo-live-updates).
allowed-tools:
- Bash(node -e *)
- Bash(find *)
---
# Ionic Appflow Migration
- Migrate an existing Ionic or Capacitor project away from Ionic Appflow.
+ Replace each Appflow feature the project uses - Live Updates, native cloud builds, store deployment - and remove Appflow only after each replacement is verified.
- ## When to Use This Skill
+ Context: Ionic stopped selling Appflow in February 2025; existing customers keep access until December 31, 2027 with no new features. Plan the migration well before that date.
- - User is moving off Ionic Appflow
- - The project uses Appflow Live Updates, cloud builds, or store deployment
- - The repository still references `ionic appflow`, `@capacitor/live-updates`, or `cordova-plugin-ionic`
+ ## When to Use
+ TRIGGER when:
+ - `package.json` has `@capacitor/live-updates` or `cordova-plugin-ionic`.
+ - `capacitor.config.*` has `plugins.LiveUpdates`, or `config.xml` / `Info.plist` / `strings.xml` hold Appflow deploy keys (`APP_ID`, `CHANNEL_NAME`, `UPDATE_METHOD`, `IonAppId`, `IonChannelName`).
+ - CI calls `ionic deploy`, `appflow`, `ionic package build`, or the Appflow dashboard triggers builds and store uploads.
+
+ Do not use for:
+ - `@ionic-enterprise/*` plugins -> `ionic-enterprise-sdk-migration`.
+ - Capawesome Cloud -> `capawesome-live-update-migration`.
+ - Greenfield Capgo setup -> `capgo-live-updates`.
+
## Live Project Snapshot
Detected Appflow-related packages and scripts:
- !`node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const out=[];for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(name==='@capacitor/live-updates'||name==='cordova-plugin-ionic'||name.includes('appflow'))out.push(section+'.'+name+'='+version)}}for(const [name,cmd] of Object.entries(pkg.scripts||{})){if(/appflow|ionic cloud|ionic package|live-updates/i.test(cmd))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"`
+ !`node -e "const fs=require('fs');if(!fs.existsSync('package.json'))process.exit(0);const pkg=JSON.parse(fs.readFileSync('package.json','utf8'));const out=[];for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(name==='@capacitor/live-updates'||name==='cordova-plugin-ionic'||name.includes('appflow')||name==='@ionic/cli'||name==='@capgo/capacitor-updater'||name==='@capacitor/core')out.push(section+'.'+name+'='+version)}}for(const [name,cmd] of Object.entries(pkg.scripts||{})){if(/appflow|ionic deploy|ionic package|live-updates/i.test(cmd))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"`
Possible Appflow config and workflow paths:
- !`find . -maxdepth 4 \( -name '.io-config.json' -o -name 'ionic.config.json' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' -o -path './.github/workflows' \)`
+ !`find . -maxdepth 4 -not -path '*/node_modules/*' \( -name '.io-config.json' -o -name 'ionic.config.json' -o -name 'config.xml' -o -name 'capacitor.config.json' -o -name 'capacitor.config.ts' -o -name 'capacitor.config.js' -o -path './.github/workflows/*' -o -name '.gitlab-ci.yml' -o -name 'appflow.config.json' \)`
- ## Migration Strategy
+ ## Procedure
- Split the Appflow migration by feature instead of treating it as a single package swap.
+ ### Step 1: Inventory (report before editing)
- - Live Updates -> `capgo-live-updates`
- - Native cloud builds -> `capacitor-ci-cd`
- - Store publishing -> `capacitor-app-store`
+ ```bash
+ rg -n "@capacitor/live-updates|cordova-plugin-ionic|LiveUpdates|IonAppId|IonChannelName|UPDATE_METHOD|CHANNEL_NAME|ionic deploy|appflow|dashboard.ionicframework.com|ionic.io/appflow" \
+ -g '!node_modules' .
+ ```
- Use this skill to detect what Appflow is doing today, then hand off each feature area to the right skill.
+ Record per feature:
+ - Live Updates: SDK in use, Appflow app ID, channels and which binaries point at them, update method (`background`, `none`, always-latest on resume, force update), `maxVersions`, any code calling `sync()`, `download()`, `extract()`, `reload()`.
+ - Native builds: build stack, signing certificates/profiles and keystores stored in Appflow, environment variables, native config overrides.
+ - Store deploys: TestFlight / Play destinations and tracks.
- ## Procedures
+ Ask the user to export signing credentials from Appflow (or confirm they have originals) before deleting anything there.
- ### Step 1: Detect Appflow Usage
+ ### Step 2: Live Updates -> Capgo
- Start from the injected snapshot above, then search more broadly if the migration surface is still unclear.
+ ```bash
+ npm uninstall @capacitor/live-updates # or: npm uninstall cordova-plugin-ionic
+ npm install @capgo/capacitor-updater
+ npx cap sync
+ ```
- Search the repository for:
+ For `cordova-plugin-ionic`, also remove its `<plugin>`/`<variable>` entries from `config.xml` and any `Ion*` keys from native files.
- - `ionic appflow`
- - `@capacitor/live-updates`
- - `cordova-plugin-ionic`
- - `dashboard.ionicframework.com`
- - `appflow.ionic.io`
+ Replace the config block:
- Record whether the project currently uses:
+ ```ts
+ // capacitor.config.ts
+ plugins: {
+ // remove LiveUpdates: { appId, channel, autoUpdateMethod, maxVersions }
+ CapacitorUpdater: {
+ autoUpdate: true,
+ autoDeletePrevious: true,
+ },
+ },
+ ```
- - live updates
- - cloud/native builds
- - app store deployment automation
+ | Appflow | Capgo |
+ |---|---|
+ | `appId` | Capgo app created with `npx @capgo/cli@latest init` (uses `appId` from `capacitor.config`) |
+ | `channel` | Capgo channel (dashboard/CLI); set default channel server-side, or `defaultChannel` in config for per-build overrides |
+ | `autoUpdateMethod: 'background'` | `autoUpdate: true` (default behaviour: download in background, apply on next start) |
+ | `autoUpdateMethod: 'none'` + manual `sync()` | Keep `autoUpdate: true` unless the product needs custom timing; otherwise `autoUpdate: false` + manual API |
+ | `maxVersions` | `autoDeletePrevious: true` plus server-side bundle retention |
+ | Binary-version targeting | Channel per native major, or `--min-update-version` / metadata strategy (`capgo-release-workflows`) |
+ | Force update | Mark bundle/channel behaviour in Capgo and listen for `majorAvailable` or use `directUpdate` |
- ### Step 2: Migrate Live Updates
+ Add the one required hook once the app shell renders:
- If Appflow live updates are in use:
+ ```ts
+ import { CapacitorUpdater } from '@capgo/capacitor-updater';
+ void CapacitorUpdater.notifyAppReady();
+ ```
- 1. Remove `@capacitor/live-updates` or `cordova-plugin-ionic`.
- 2. Install and configure Capgo using the `capgo-live-updates` skill.
- 3. Map Appflow channels and rollout behavior onto Capgo channels.
- 4. Verify that `notifyAppReady()` or the equivalent Capgo startup flow is wired correctly.
+ Without it Capgo treats the bundle as broken and rolls back.
- Do not delete Appflow configuration until the Capgo update path is validated.
+ API mapping (keep manual calls only if the product needs custom UI or timing):
- ### Step 3: Replace Cloud Build Automation
+ | Appflow (`@capacitor/live-updates`) | Capgo |
+ |---|---|
+ | `LiveUpdates.sync()` | Not needed with `autoUpdate: true`; manual: `CapacitorUpdater.getLatest()` + `download()` |
+ | `LiveUpdates.reload()` | `CapacitorUpdater.set({ id })` (applies and reloads) or `reload()` |
+ | resume listener calling `sync()` | Remove; auto-update checks on resume |
+ | `SplashScreen.hide()` after sync | Plain `SplashScreen.hide()` after app boot; optional `appReady` listener |
- If Appflow was building the app in the cloud:
+ Upload the first bundle to a test channel:
- 1. Inspect the existing CI/CD workflow for `ionic appflow build`.
- 2. Replace it with repository-owned automation using the `capacitor-ci-cd` skill.
- 3. Preserve signing inputs, environment variables, and platform-specific build arguments.
+ ```bash
+ npm run build
+ npx @capgo/cli@latest bundle upload --channel development
+ ```
- Treat Appflow build settings as migration input, not as a runtime dependency.
+ ### Step 3: Native cloud builds
- ### Step 4: Replace Store Publishing
+ Two replacements; ask which the user prefers:
+ - Capgo Cloud Build: `npx @capgo/cli@latest build credentials save --platform ios|android ...`, then `npx @capgo/cli@latest build request <appId> --platform ios|android`. Details in `capgo-native-builds`.
+ - Repo-owned CI (GitHub Actions, GitLab, Fastlane): `capacitor-ci-cd`.
- If Appflow handled TestFlight or Google Play publishing:
+ Carry over environment variables, build-time config, and signing inputs exactly; change one thing at a time.
- 1. Inspect the current deployment flow.
- 2. Move that workflow to the repository's publishing pipeline using the `capacitor-app-store` skill.
- 3. Keep bundle identifiers, track selection, and credential handling unchanged unless the user wants a new release process.
+ ### Step 4: Store publishing
- ### Step 5: Clean Up
+ Capgo Cloud Build can upload to TestFlight / Play when store credentials are saved; otherwise use CI upload steps (`capacitor-ci-cd`) or manual submission (`capacitor-app-store`). Keep bundle IDs, Play tracks, and credentials unchanged.
- After each migrated feature is verified:
+ ### Step 5: Cut over and clean up
- - remove Appflow packages and scripts
- - remove obsolete Appflow configuration
- - remove stale CI secrets that are no longer needed
+ 1. Ship a native build containing `@capgo/capacitor-updater` to the stores. Devices on older binaries keep pulling from Appflow until they update - keep Appflow channels live until adoption is high enough.
+ 2. Publish web updates to both systems during the overlap, or freeze Appflow at the last bundle shipped with the new binary.
+ 3. Remove Appflow packages, `plugins.LiveUpdates`, `.io-config.json` references, Appflow CI jobs, and secrets once no supported binary depends on them.
+ ## Verification
+
+ - `rg -n "@capacitor/live-updates|cordova-plugin-ionic|LiveUpdates\\." -g '!node_modules' .` returns nothing (after cleanup).
+ - Fresh install of the new binary: logs show `notifyAppReady` called; Capgo dashboard shows the device.
+ - Upload a test bundle to a non-production channel; device downloads and applies it on next start.
+ - Upload a deliberately broken bundle (no `notifyAppReady`) to the test channel; device rolls back.
+ - New native build pipeline produces signed IPA/AAB with the same bundle ID and a higher build number.
+
## Error Handling
- - For live update migrations, validate rollback behavior before deleting the old Appflow setup.
- - For build migrations, preserve the existing signing path first and only simplify later.
- - For publishing migrations, move one destination at a time so App Store and Play failures stay isolated.
+ | Problem | Fix |
+ |---|---|
+ | App rolls back every update | `notifyAppReady()` not reached (called too late, behind login, or code path throws); call it right after the shell renders |
+ | `Cannot find API key in local folder or global, please login first` from the Capgo CLI | `npx @capgo/cli@latest login <apikey>` locally, or `CAPGO_TOKEN` in CI |
+ | Updates never arrive | Device on a channel without the bundle, or bundle incompatible with native version; check channel and `npx @capgo/cli@latest bundle compatibility` |
+ | Old Appflow update overwrote Capgo bundle | Old SDK still installed in the binary; confirm `@capacitor/live-updates` / `cordova-plugin-ionic` removed and `npx cap sync` run before building |
+ | Build fails after removing Appflow | Appflow injected env vars or native config; recreate them in CI or Capgo Cloud Build environment |