capacitor-ci-cd ยท diff
git:20260501.ed3e92e to git:20261011.86834d4
78 added, 636 removed. Audit A to A.
---
name: capacitor-ci-cd
- description: Complete CI/CD guide for Capacitor apps covering GitHub Actions, GitLab CI, build automation, app signing, and deployment pipelines. Use this skill when users need to automate their build and release process.
+ description: Designs and fixes CI/CD pipelines for Capacitor apps - GitHub Actions, GitLab CI, Fastlane, or Capgo Cloud Build (npx @capgo/cli@latest build request) - covering web build and tests, cap sync, iOS archive/export/upload with App Store Connect API keys, Android AAB signing and Play upload, Capgo live-update uploads, secrets, caching, and build-number bumping. Use when choosing runner images and toolchains (Node 22/24, macos-26 vs xcode-27 runners, JDK 21, Gradle 9 / AGP 9 for Capacitor 9), or when CI fails with errors like "No signing certificate", "requires a provisioning profile", "Unsupported class file major version", "Android Gradle plugin requires Java 17", "The engine node is incompatible", or "Could not find the web assets directory". Do not use for one-off manual store submission (capacitor-app-store), Capgo Cloud Build credential onboarding in depth (capgo-native-builds), Capgo channel/release strategy (capgo-release-workflows), or test authoring (capacitor-testing).
+ allowed-tools:
+ - Bash(node -e *)
+ - Bash(find *)
---
- # CI/CD for Capacitor Applications
-
- Automate building, testing, and deploying Capacitor apps.
-
- ## When to Use This Skill
-
- - User wants to automate builds
- - User needs CI/CD pipeline
- - User asks about GitHub Actions
- - User needs app signing automation
- - User wants automated releases
-
- ## GitHub Actions
-
- ### Complete Workflow
-
- ```yaml
- # .github/workflows/build.yml
- name: Build and Deploy
-
- on:
- push:
- branches: [main, develop]
- pull_request:
- branches: [main]
-
- env:
- NODE_VERSION: '20'
-
- jobs:
- # Run tests and linting
- test:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
-
- - name: Install dependencies
- run: npm install
-
- - name: Lint
- run: npm run lint
-
- - name: Type check
- run: npm run typecheck
-
- - name: Unit tests
- run: npm test -- --coverage
-
- - name: Upload coverage
- uses: codecov/codecov-action@v4
-
- # Security scan
- security:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-node@v4
- - run: npx capsec scan --ci
-
- # Build web assets
- build-web:
- runs-on: ubuntu-latest
- needs: [test, security]
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
-
- - name: Install dependencies
- run: npm install
-
- - name: Build
- run: npm run build
-
- - name: Upload build artifacts
- uses: actions/upload-artifact@v4
- with:
- name: web-build
- path: dist/
-
- # Build iOS
- build-ios:
- runs-on: macos-latest
- needs: build-web
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
-
- - name: Download web build
- uses: actions/download-artifact@v4
- with:
- name: web-build
- path: dist/
-
- - name: Install dependencies
- run: npm install
-
- - name: Sync Capacitor
- run: npx cap sync ios
-
- - name: Setup Ruby
- uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.2'
- bundler-cache: true
- working-directory: ios/App
-
- - name: Install CocoaPods
- run: cd ios/App && pod install
-
- - name: Import certificates
- env:
- CERTIFICATE_P12: ${{ secrets.CERTIFICATE_P12 }}
- CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
- PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
- run: |
- # Create keychain
- security create-keychain -p "" build.keychain
- security default-keychain -s build.keychain
- security unlock-keychain -p "" build.keychain
- security set-keychain-settings -t 3600 -u build.keychain
-
- # Import certificate
- echo "$CERTIFICATE_P12" | base64 --decode > certificate.p12
- security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
- security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain
-
- # Install provisioning profile
- mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
- echo "$PROVISIONING_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\ Profiles/profile.mobileprovision
-
- - name: Build iOS
- run: |
- cd ios/App
- xcodebuild -workspace App.xcworkspace \
- -scheme App \
- -configuration Release \
- -archivePath build/App.xcarchive \
- archive
-
- - name: Export IPA
- run: |
- cd ios/App
- xcodebuild -exportArchive \
- -archivePath build/App.xcarchive \
- -exportPath build/ \
- -exportOptionsPlist ExportOptions.plist
-
- - name: Upload IPA
- uses: actions/upload-artifact@v4
- with:
- name: ios-build
- path: ios/App/build/*.ipa
-
- # Build Android
- build-android:
- runs-on: ubuntu-latest
- needs: build-web
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
-
- - name: Download web build
- uses: actions/download-artifact@v4
- with:
- name: web-build
- path: dist/
-
- - name: Setup Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
-
- - name: Setup Android SDK
- uses: android-actions/setup-android@v3
-
- - name: Install dependencies
- run: npm install
-
- - name: Sync Capacitor
- run: npx cap sync android
-
- - name: Decode keystore
- env:
- KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
- run: |
- echo "$KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore
-
- - name: Build APK
- env:
- KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
- KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
- KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- run: |
- cd android
- ./gradlew assembleRelease \
- -Pandroid.injected.signing.store.file=release.keystore \
- -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
- -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
- -Pandroid.injected.signing.key.password=$KEY_PASSWORD
-
- - name: Build AAB
- env:
- KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
- KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
- KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- run: |
- cd android
- ./gradlew bundleRelease \
- -Pandroid.injected.signing.store.file=release.keystore \
- -Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
- -Pandroid.injected.signing.key.alias=$KEY_ALIAS \
- -Pandroid.injected.signing.key.password=$KEY_PASSWORD
-
- - name: Upload APK
- uses: actions/upload-artifact@v4
- with:
- name: android-apk
- path: android/app/build/outputs/apk/release/*.apk
-
- - name: Upload AAB
- uses: actions/upload-artifact@v4
- with:
- name: android-aab
- path: android/app/build/outputs/bundle/release/*.aab
-
- # Deploy to Capgo
- deploy-capgo:
- runs-on: ubuntu-latest
- needs: build-web
- if: github.ref == 'refs/heads/main'
- steps:
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
-
- - name: Download web build
- uses: actions/download-artifact@v4
- with:
- name: web-build
- path: dist/
-
- - name: Deploy to Capgo
- run: npx @capgo/cli upload
- env:
- CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
-
- # Deploy to App Store
- deploy-ios:
- runs-on: macos-latest
- needs: build-ios
- if: github.ref == 'refs/heads/main'
- steps:
- - uses: actions/checkout@v4
-
- - name: Download IPA
- uses: actions/download-artifact@v4
- with:
- name: ios-build
- path: build/
-
- - name: Upload to App Store Connect
- env:
- APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
- run: |
- xcrun altool --upload-app \
- --type ios \
- --file build/*.ipa \
- --apiKey ${{ secrets.API_KEY_ID }} \
- --apiIssuer ${{ secrets.API_ISSUER_ID }}
-
- # Deploy to Play Store
- deploy-android:
- runs-on: ubuntu-latest
- needs: build-android
- if: github.ref == 'refs/heads/main'
- steps:
- - uses: actions/checkout@v4
-
- - name: Download AAB
- uses: actions/download-artifact@v4
- with:
- name: android-aab
- path: build/
-
- - name: Upload to Play Store
- uses: r0adkll/upload-google-play@v1
- with:
- serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
- packageName: com.yourapp.id
- releaseFiles: build/*.aab
- track: internal
- ```
-
- ### Fastlane Integration
-
- ```yaml
- # .github/workflows/fastlane.yml
- name: Fastlane Build
-
- on:
- push:
- tags: ['v*']
-
- jobs:
- ios:
- runs-on: macos-latest
- steps:
- - uses: actions/checkout@v4
-
- - uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.2'
- bundler-cache: true
-
- - name: Install Fastlane
- run: gem install fastlane
-
- - name: Build and Deploy
- run: fastlane ios release
- env:
- MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
- APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
-
- android:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - uses: ruby/setup-ruby@v1
- with:
- ruby-version: '3.2'
- bundler-cache: true
-
- - name: Install Fastlane
- run: gem install fastlane
-
- - name: Build and Deploy
- run: fastlane android release
- env:
- PLAY_STORE_JSON_KEY: ${{ secrets.PLAY_SERVICE_ACCOUNT }}
- ```
-
- ## Fastlane Setup
-
- ### iOS Fastfile
-
- ```ruby
- # ios/App/fastlane/Fastfile
- default_platform(:ios)
-
- platform :ios do
- desc "Build and deploy to TestFlight"
- lane :release do
- setup_ci
-
- # Match for code signing
- match(
- type: "appstore",
- readonly: true
- )
-
- # Increment build number
- increment_build_number(
- build_number: ENV["GITHUB_RUN_NUMBER"]
- )
-
- # Build
- build_app(
- workspace: "App.xcworkspace",
- scheme: "App",
- export_method: "app-store"
- )
-
- # Upload to TestFlight
- upload_to_testflight(
- skip_waiting_for_build_processing: true
- )
- end
-
- desc "Build for development"
- lane :build do
- match(type: "development", readonly: true)
-
- build_app(
- workspace: "App.xcworkspace",
- scheme: "App",
- export_method: "development"
- )
- end
- end
- ```
-
- ### Android Fastfile
-
- ```ruby
- # android/fastlane/Fastfile
- default_platform(:android)
-
- platform :android do
- desc "Build and deploy to Play Store"
- lane :release do
- # Increment version code
- increment_version_code(
- version_code: ENV["GITHUB_RUN_NUMBER"].to_i
- )
-
- # Build AAB
- gradle(
- task: "bundle",
- build_type: "Release"
- )
-
- # Upload to Play Store
- upload_to_play_store(
- track: "internal",
- aab: lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
- )
- end
-
- desc "Build APK"
- lane :build do
- gradle(
- task: "assemble",
- build_type: "Release"
- )
- end
- end
- ```
-
- ## GitLab CI
-
- ```yaml
- # .gitlab-ci.yml
- stages:
- - test
- - build
- - deploy
-
- variables:
- NODE_VERSION: "20"
-
- .npm-cache: &npm-cache
- cache:
- key: ${CI_COMMIT_REF_SLUG}
- paths:
- - node_modules/
- - ~/.npm
-
- test:
- stage: test
- image: node:20
- <<: *npm-cache
- script:
- - npm install
- - npm run lint
- - npm test -- --coverage
- coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
- artifacts:
- reports:
- coverage_report:
- coverage_format: cobertura
- path: coverage/cobertura-coverage.xml
-
- security:
- stage: test
- image: node:20
- script:
- - npx capsec scan --ci --output json --output-file security.json
- artifacts:
- reports:
- security: security.json
-
- build-web:
- stage: build
- image: node:20
- <<: *npm-cache
- script:
- - npm install
- - npm run build
- artifacts:
- paths:
- - dist/
- expire_in: 1 day
-
- build-ios:
- stage: build
- tags:
- - macos
- needs: [build-web]
- script:
- - npm install
- - npx cap sync ios
- - cd ios/App && fastlane build
- artifacts:
- paths:
- - ios/App/build/*.ipa
- only:
- - main
- - tags
-
- build-android:
- stage: build
- image: thyrlian/android-sdk
- needs: [build-web]
- script:
- - npm install
- - npx cap sync android
- - cd android && ./gradlew assembleRelease
- artifacts:
- paths:
- - android/app/build/outputs/apk/release/*.apk
- only:
- - main
- - tags
-
- deploy-capgo:
- stage: deploy
- image: node:20
- needs: [build-web]
- script:
- - npx @capgo/cli upload --channel production
- only:
- - main
- environment:
- name: production
- ```
-
- ## Secrets Management
-
- ### Required Secrets
-
- | Secret | Description |
- |--------|-------------|
- | `CERTIFICATE_P12` | iOS distribution certificate (base64) |
- | `CERTIFICATE_PASSWORD` | Certificate password |
- | `PROVISIONING_PROFILE` | iOS provisioning profile (base64) |
- | `KEYSTORE_BASE64` | Android keystore (base64) |
- | `KEYSTORE_PASSWORD` | Keystore password |
- | `KEY_ALIAS` | Signing key alias |
- | `KEY_PASSWORD` | Signing key password |
- | `CAPGO_TOKEN` | Capgo API token |
- | `APP_STORE_CONNECT_API_KEY` | App Store Connect API key |
- | `PLAY_SERVICE_ACCOUNT` | Play Store service account JSON |
+ # CI/CD for Capacitor Apps
- ### Encoding Secrets
+ Build, sign, and ship Capacitor apps from CI with the right toolchain for the Capacitor major in use.
- ```bash
- # iOS certificate
- base64 -i certificate.p12 | pbcopy
+ ## When to Use
- # iOS provisioning profile
- base64 -i profile.mobileprovision | pbcopy
+ TRIGGER when:
+ - Creating or fixing `.github/workflows/*.yml`, `.gitlab-ci.yml`, `fastlane/Fastfile`, or a Capgo Cloud Build job.
+ - Picking runner images, Node/Xcode/JDK versions for Capacitor 8 or 9.
+ - Automating signing, TestFlight/Play uploads, version bumps, or Capgo OTA uploads.
+ - A CI build fails while a local build works.
- # Android keystore
- base64 -i release.keystore | pbcopy
- ```
+ Do not use for:
+ - Manual submission, listing, screenshots -> `capacitor-app-store`.
+ - Capgo Cloud Build credential setup and troubleshooting -> `capgo-native-builds`.
+ - Channel promotion, staged OTA rollouts -> `capgo-release-workflows`.
+ - Writing unit/E2E tests -> `capacitor-testing`.
+ - Security scanning rules -> `capacitor-security`.
- ## Version Management
+ ## Live Project Snapshot
- ### Semantic Release
+ Capacitor version, Node engine, 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=[];if(pkg.engines)out.push('engines='+JSON.stringify(pkg.engines));if(pkg.packageManager)out.push('packageManager='+pkg.packageManager);for(const section of ['dependencies','devDependencies']){for(const [name,version] of Object.entries(pkg[section]||{})){if(name.startsWith('@capacitor/')||name==='@capgo/cli'||name==='@capgo/capacitor-updater')out.push(section+'.'+name+'='+version)}}for(const [name,cmd] of Object.entries(pkg.scripts||{})){if(/build|test|lint|sync|release/i.test(name))out.push('scripts.'+name+'='+cmd)}console.log(out.join('\n'))"`
- ```bash
- npm install -D semantic-release @semantic-release/git @semantic-release/changelog
- ```
+ Existing CI, lockfiles, and native build files:
+ !`find . -maxdepth 4 -not -path '*/node_modules/*' \( -path './.github/workflows/*' -o -name '.gitlab-ci.yml' -o -name 'Fastfile' -o -name 'Appfile' -o -name 'Matchfile' -o -name 'ExportOptions.plist' -o -name 'Podfile' -o -name 'Package.swift' -o -name 'gradle-wrapper.properties' -o -name 'variables.gradle' -o -name '.nvmrc' -o -name '.node-version' -o -name 'bun.lock' -o -name 'package-lock.json' -o -name 'pnpm-lock.yaml' -o -name 'yarn.lock' \)`
- ```json
- // .releaserc.json
- {
- "branches": ["main"],
- "plugins": [
- "@semantic-release/commit-analyzer",
- "@semantic-release/release-notes-generator",
- "@semantic-release/changelog",
- [
- "@semantic-release/npm",
- { "npmPublish": false }
- ],
- [
- "@semantic-release/git",
- {
- "assets": ["package.json", "CHANGELOG.md"],
- "message": "chore(release): ${nextRelease.version}"
- }
- ],
- "@semantic-release/github"
- ]
- }
- ```
+ ## Toolchain Matrix
- ### Version Bumping
+ | | Capacitor 8.x (stable, `latest`) | Capacitor 9 (`next`, pre-GA) |
+ |---|---|---|
+ | Node | 22+ | 24+ (ships npm 11) |
+ | Xcode | 26+ (27 OK on 8.5+ with UIScene adopted) | 27+ |
+ | GitHub macOS runner | `macos-26` (default Xcode 26.x) | `xcode-27` image (preview at time of writing) or a `macos-*` image that lists Xcode 27; check actions/runner-images |
+ | iOS deployment target | 15.0 | 16.0 |
+ | JDK | 21 | 21 (AGP 9 itself needs 17+, Capacitor's Android library compiles with Java 21) |
+ | Gradle / AGP | 8.14.3 / 8.13 | 9.5.1 / 9.2.1 |
+ | compile/target SDK | 36 | 37 |
- ```yaml
- # .github/workflows/release.yml
- name: Release
+ Store-side deadlines: Apple requires the iOS 26 SDK now and the iOS 27 SDK from April 2027; Google Play requires target API 36 since Aug 31, 2026. A Capacitor 8 app on `macos-26` meets both today.
- on:
- push:
- branches: [main]
+ Pin Xcode explicitly when several are installed: `sudo xcode-select -s /Applications/Xcode_<version>.app` (exact folder names are listed in the runner image README).
- jobs:
- release:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- persist-credentials: false
+ ## Choose a Pipeline Shape
- - uses: actions/setup-node@v4
+ Ask the user when unclear:
+ 1. **Capgo Cloud Build** - no macOS runner or local signing setup; any Linux runner calls `npx @capgo/cli@latest build request <appId> --platform ios|android`. Credentials are saved once with `npx @capgo/cli@latest build credentials save ...` (or `build init` for guided iOS setup) and passed in CI as env vars. Load `capgo-native-builds` for details.
+ 2. **Self-hosted GitHub Actions / GitLab** - full control; you manage macOS runners, certificates, and keystores. See `references/github-actions.md`, `references/gitlab-ci.md`.
+ 3. **Fastlane** inside either - best when match/pilot/supply are already in use. See `references/fastlane.md`.
- - run: npm install
+ Web-only changes after a native release can skip native builds entirely: upload a Capgo bundle (`npx @capgo/cli@latest bundle upload --channel <channel>`, auth via `CAPGO_TOKEN`). Native changes (plugins, permissions, Capacitor upgrades) always need a native build.
- - name: Release
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: npx semantic-release
- ```
+ ## Procedure
- ## Build Caching
+ 1. Read the snapshot: Capacitor major, package manager + lockfile, iOS dependency manager (`Package.swift` -> SPM, `Podfile` -> CocoaPods), existing workflows.
+ 2. Pick toolchain versions from the matrix. Set Node from `.nvmrc`/`engines` if present.
+ 3. Structure jobs: `test` (lint, typecheck, unit) -> `build-web` (artifact `<webDir>`) -> `ios` + `android` in parallel -> deploy jobs gated on branch/tag. Always run `npx cap sync <platform>` on the native job after restoring the web artifact; never commit synced assets as a CI shortcut.
+ 4. Signing: follow `references/signing-and-secrets.md`. Prefer App Store Connect API keys over Apple ID passwords; keep Android keystore and passwords in secrets, decode at runtime, delete after.
+ 5. Build numbers: derive from CI (`GITHUB_RUN_NUMBER`, `CI_PIPELINE_IID`) or let Capgo Cloud Build bump them. Never reuse a build number.
+ 6. Add caching (`references/caching-and-versioning.md`).
+ 7. Run the pipeline on a branch first, uploading to TestFlight internal / Play internal track only.
- ### Gradle Cache
+ ## Verification
- ```yaml
- - name: Cache Gradle
- uses: actions/cache@v4
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- restore-keys: gradle-${{ runner.os }}-
- ```
+ - The workflow passes on a clean runner twice in a row (cache cold and warm).
+ - Logs show the expected tool versions: add `node -v`, `xcodebuild -version`, `java -version`, `./gradlew --version` steps.
+ - iOS artifact: `unzip -l App.ipa | grep Payload/App.app` and the build appears in TestFlight with the expected build number.
+ - Android artifact: `bundletool dump manifest --bundle app-release.aab | grep -E "versionCode|targetSdkVersion"` matches expectations; build appears in the Play internal track.
+ - Capgo: `npx @capgo/cli@latest bundle list` shows the uploaded version on the intended channel.
+ - No secret values printed in logs (GitHub masks registered secrets; never `echo` decoded files).
- ### CocoaPods Cache
+ ## Error Handling
- ```yaml
- - name: Cache CocoaPods
- uses: actions/cache@v4
- with:
- path: ios/App/Pods
- key: pods-${{ runner.os }}-${{ hashFiles('ios/App/Podfile.lock') }}
- restore-keys: pods-${{ runner.os }}-
- ```
+ | Error in CI log | Fix |
+ |---|---|
+ | `error ... The engine "node" is incompatible with this module. Expected version ">=22.0.0"` (or `>=24`) | Bump `actions/setup-node` `node-version` to match the Capacitor major |
+ | `Could not find the web assets directory: ./dist.` | Web artifact not downloaded to `webDir`, or build skipped; fix artifact path |
+ | `No signing certificate "iOS Distribution" found` | Certificate not imported into the active keychain, or wrong team; see signing reference |
+ | `"App" requires a provisioning profile. Select a provisioning profile in the Signing & Capabilities editor.` | Install the profile, or use `-allowProvisioningUpdates` with API-key auth flags |
+ | `error: exportArchive: No profiles for 'com.x.y' were found` | Profile type mismatch with `ExportOptions.plist` `method` (`app-store-connect`) |
+ | `xcodebuild: error: SDK "iphoneos27.0" cannot be located` / Swift 6 errors on `@UIApplicationMain` | Wrong Xcode selected; `xcode-select` the required version; replace `@UIApplicationMain` with `@main` |
+ | `Unsupported class file major version 65` / `Android Gradle plugin requires Java 17 to run` / `invalid source release: 21` | Install JDK 21 with `actions/setup-java` (`distribution: temurin`, `java-version: '21'`) |
+ | `Plugin [id: 'org.jetbrains.kotlin.android'] ... ` conflicts after Capacitor 9 upgrade | AGP 9 has built-in Kotlin; remove the kotlin-android plugin per `capacitor-app-upgrades` |
+ | `SDK location not found. Define a valid SDK location with an ANDROID_HOME` | Use an image with the Android SDK (`ubuntu-latest` has it) or `android-actions/setup-android` |
+ | Play API: `APK specifies a version code that has already been used.` | Derive `versionCode` from the CI run number |
+ | Capgo: `Cannot find API key in local folder or global, please login first` | Export `CAPGO_TOKEN` from secrets in that step's `env` (or pass `-a`) |
+ | Capgo: `Cannot auth user with apikey` | Key revoked or lacks upload permission; create a new key in the Capgo dashboard |
+ | `npm error 404 Not Found - GET https://registry.npmjs.org/capsec` | Security scanner package is `@capgo/capgo-sec`: `npx @capgo/capgo-sec@latest scan --ci` |
- ## Resources
+ ## References
- - GitHub Actions: https://docs.github.com/actions
- - Fastlane: https://fastlane.tools
- - Capgo CLI: https://capgo.app/docs/cli
- - App Store Connect API: https://developer.apple.com/documentation/appstoreconnectapi
- - Google Play API: https://developers.google.com/android-publisher
+ - `references/github-actions.md` - complete workflow: test, web build, iOS (SPM/CocoaPods), Android, Capgo OTA, Capgo Cloud Build job.
+ - `references/gitlab-ci.md` - GitLab pipeline with macOS runner tags.
+ - `references/fastlane.md` - iOS and Android lanes with API keys and match.
+ - `references/signing-and-secrets.md` - certificate/profile import, API key auth, Android keystore wiring, secret inventory.
+ - `references/caching-and-versioning.md` - npm/Gradle/SPM/CocoaPods caches, build numbers, semantic-release.