Bitbucket Pipelines ne peut pas construire une application iOS sur ses runners hébergés, car chaque étape s'exécute dans un conteneur Linux et Xcode ne s'exécute que sur macOS. Vous avez deux options fonctionnelles : enregistrer votre propre Mac en tant que runner Bitbucket auto-hébergé, ou maintenir l'étape sur Linux et confier la compilation, la signature et l'envoi vers TestFlight à un service de build cloud. Ce guide montre les deux, avec des fichiers complets pour une application __CAPGO_KEEP_0__. bitbucket-pipelines.yml fichiers pour une application Capacitor.
If your team already lives in Bitbucket for code review, Jira links, and deployments, you do not need a second CI tool just for iOS. The pipeline stays in Bitbucket. Only the part that needs a Mac moves.
Why les builds iOS échouent sur Bitbucket Pipelines
Bitbucket Pipelines exécute chaque étape à l'intérieur d'une image Docker que vous choisissez (node:22, cimg/android, et ainsi de suite) sur l'infrastructure Linux d'Atlassian. C'est bien pour les bun install, unit tests, linting, and even full Android builds. It stops at iOS:
xcodebuild,codesign, et les iOS SDK ne sont que sur macOS.- Apple autorise uniquement macOS sur matériel Apple, il n'y a donc pas d'image Linux pouvant produire une application signée.
.ipa. - Depuis le 28 avril 2026, App Store Connect rejette les téléchargements qui n'ont pas été construits avec Xcode 26 et les iOS 26 SDK, donc votre Mac doit rester à jour. Voir Les exigences d'Apple pour Xcode 26 pour les applications Capacitor.
Bitbucket prend en charge les exécutants auto-hébergés sur Linux, Windows et macOS. Atlassian ne facture pas les minutes de build pour les étapes qui s'exécutent sur vos propres exécutants, mais vous payez en matériel et en maintenance.
Vos trois options
| Option | Où les builds iOS s'exécutent | Ce que vous maintenez | Bon choix |
|---|---|---|---|
| Exécuteur macOS auto-hébergé | Votre Mac mini ou un Mac loué | macOS, Xcode, clé de chaîne, certificats, espace disque, temps d'activité | Équipes qui possèdent déjà des Mac et ont quelqu'un pour les exécuter |
| Deuxième CI juste pour les appareils mobiles | Un autre fournisseur de Macs | Deux pipelines, deux ensembles de secrets et de déclencheurs | Équipes qui veulent un CI mobile dédié de toute façon |
| Construction cloud appelée depuis une étape Linux | Capgo Flotte de Mac de Build | Une seule CLI et quelques variables sécurisées | Capacitor qui veulent que Bitbucket reste la seule CI |
Le reste de ce guide couvre les premières et troisièmes options en détail. Pour une vue plus large des plateformes, lisez Comparer les plateformes CI/CD pour les applications iOS.
Option 1 : Construire iOS à partir d'une étape Linux avec Capgo Build
Capgo Build compilera et signera les applications Capacitor sur les Macs gérés par Capgo. L'étape Bitbucket fait ce qu'elle fait bien sur Linux : installe les dépendances, construit la couche web et exécute cap sync ios. Ensuite, le Capgo CLI télécharge le projet natif préparé (pas votre source web code ou node_modules), exécute la construction Xcode, la signe avec votre certificat et peut télécharger le résultat vers TestFlight. Les journaux s'écoulent à nouveau dans l'étape Bitbucket, et la commande sort avec un code non nul si la construction échoue, ce qui fait que la construction iOS échoue la pipeline.
Puisque votre exécuteur envoie le projet vers Capgo, Capgo n'a jamais besoin d'accéder à votre référentiel Bitbucket. Il n'y a pas de connexion de référentiel à autoriser et pas d'application OAuth à approuver.
Prerequisites
- Une application Capacitor qui se construit localement (Capacitor 8 nécessite Node.js 22 ou plus récent)
- Un abonnement au programme Apple Developer avec les droits administrateur sur l'équipe
- Un compte Capgo et une clé API avec la permission d'upload
- Le programme d'application enregistré dans Capgo :
bunx @capgo/cli@latest app add
Une configuration unique sur votre machine
Exécutez l'inscription une fois. Sur iOS, cela crée ou réutilise votre certificat de distribution et votre profil de provisionnement, et demande une clé API d'App Store Connect :
bunx @capgo/cli@latest login
bunx @capgo/cli@latest build init --platform ios
Si vous n'avez pas un Mac à portée de main, vous pouvez toujours créer la demande de signature de certificat dans le navigateur avec le Générateur de certificat iOS, ou suivez Construirez une application iOS à partir de Windows avec Capgo Build.
Exécutez une build à partir de votre ordinateur portable avant de toucher CI. CI est un mauvais endroit pour déboguer une première build :
bun run build
bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
Export the credentials for Bitbucket
CLI conserve les informations d'identification sur votre machine. Pour les déplacer dans Bitbucket, exportez-les dans un fichier dotenv :
bunx @capgo/cli@latest build credentials manage --appId com.example.app --platform ios
# choose "Export to .env" in the menu
Ceci écrit .env.capgo.com.example.app.ios avec mode 0600. Il contient ces clés :
| Variable | Contenu |
|---|---|
BUILD_CERTIFICATE_BASE64 |
Base64 de votre .p12 certificat de distribution |
P12_PASSWORD |
Mot de passe du certificat (vide si aucun) |
CAPGO_IOS_PROVISIONING_MAP_BASE64 |
Mappage de profil de provisionnement généré par le CLI |
APPLE_KEY_ID |
Clé ID de App Store Connect API |
APPLE_ISSUER_ID |
ID émetteur de App Store Connect |
APPLE_KEY_CONTENT |
Base64 de le .p8 API clé |
APP_STORE_CONNECT_TEAM_ID |
Votre ID d'équipe Apple |
Ajoutez chaque ligne en tant que secured variable dans Bitbucket sous Paramètres de l'archive > Flux > Variables de l'archive, plus CAPGO_TOKEN avec votre Capgo API clé. Les variables sécurisées sont masquées dans les journaux et ne peuvent pas être lues à partir de l'interface utilisateur.
Si vous préférez l’API, cette boucle les crée en une seule passe. Elle utilise un jeton de Bitbucket API avec l'étendue d'écriture des variables de pipeline :
WORKSPACE=my-team
REPO=my-app
ENV_FILE=.env.capgo.com.example.app.ios
grep -v '^#' "$ENV_FILE" | grep '=' | while IFS= read -r line; do
key="${line%%=*}"
value="${line#*=}"
value="${value%\"}"; value="${value#\"}"
jq -n --arg k "$key" --arg v "$value" '{key:$k, value:$v, secured:true}' |
curl -s -u "$BB_EMAIL:$BB_API_TOKEN" \
-H 'Content-Type: application/json' \
-X POST --data @- \
"https://api.bitbucket.org/2.0/repositories/$WORKSPACE/$REPO/pipelines_config/variables" > /dev/null
echo "created $key"
done
rm "$ENV_FILE"
Supprimez ensuite le fichier et ajoutez .env.capgo.* to .gitignore. Il contient des matériaux de signature en clair.
For production, utilisez des variables de déploiement variables de déploiement ) et vous pouvez limiter cet environnement aux administrateurs ou aux branches spécifiques sur les plans Premium. Production), et vous pouvez restreindre cet environnement aux administrateurs ou aux branches spécifiques dans les plans Premium.
Le fichier de pipeline
Bitbucket injects repository and deployment variables as environment variables, so the Capgo CLI picks them up without any mapping. This file runs tests on every push, builds an ad hoc iOS build on demand, and ships to TestFlight on version tags:
# bitbucket-pipelines.yml
image: node:22
definitions:
caches:
bun: ~/.bun/install/cache
pipelines:
default:
- step:
name: Lint and test
caches:
- bun
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run lint
- bun run test
tags:
'v*':
- step:
name: Build iOS and upload to TestFlight
deployment: Production
caches:
- bun
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run build
- bunx cap sync ios
- >
bunx @capgo/cli@latest build request com.example.app
--platform ios
--build-mode release
--ios-distribution app_store
custom:
ios-adhoc:
- step:
name: Ad hoc iOS build for testers
caches:
- bun
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run build
- bunx cap sync ios
- echo "$ADHOC_PROFILE_BASE64" | base64 --decode > adhoc.mobileprovision
- >
bunx @capgo/cli@latest build request com.example.app
--platform ios
--build-mode release
--ios-distribution ad_hoc
--ios-provisioning-profile ./adhoc.mobileprovision
--output-upload
--output-retention 2d
--output-record /tmp/build.json
- bunx @capgo/cli@latest build last-output --path /tmp/build.json --field outputUrl
L'image est un Linux standard
- . Rien dans ce fichier n'a besoin de macOS.
node:22Rien dans ce fichier n'est nécessaire pour macOS. - Sur Linux,
cap sync ios__CAPGO_KEEP_0__ lit le numéro de build le plus récent depuis App Store Connect et l'incrémente, afin que vous ne rencontriez jamais « la version du bundle doit être supérieure à la version précédemment téléchargée ». Passer - Capgo reads the latest build number from App Store Connect and increments it, so you never hit “bundle version must be higher than the previously uploaded version”. Pass
--skip-build-number-bumpSi vous le gérez vous-même. - L'ad hoc build nécessite un profil de provisioning Ad Hoc, qui ne s'installe que sur les appareils qu'il liste. Stockez-le comme une variable sécurisée ; le
ADHOC_PROFILE_BASE64variable; le--ios-provisioning-profileCe drapeau remplace le profil de l'App Store provenant de l'environnement pour cette seule build. Collectez les UDIDs des testeurs avec le. Identifiant UDID iOS. - Fixer l'CLI à une version exacte (par exemple)
@capgo/cli@7.104.0Une fois votre pipeline stable, afin qu'une mise en production CLI ne modifie pas le comportement au milieu d'un sprint.
Pipelines > Exécutez une pipeline > ios-adhoc Pipelines > Exécuter la pipeline > ios-adhocLe dernier commandement imprime un lien de téléchargement qui expire après la période de conservation.
Bitbucket prend en charge
les pipelines personnalisées. trigger: manual on a step. Split the release into a build step and a gated upload step if you want a human to approve each TestFlight upload:
tags:
'v*':
- step:
name: Test
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run test
- step:
name: Ship iOS to TestFlight
trigger: manual
deployment: Production
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run build
- bunx cap sync ios
- bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
Pour passer directement à la Revue d'App après le téléchargement TestFlight, ajoutez --submit-to-store-review avec --store-release-name "$BITBUCKET_TAG"Votre liste d'application sur l'App Store, vos captures d'écran et vos réponses de conformité doivent être complètes dans App Store Connect en premier lieu.
Ajoutez Android en parallèle
Android builds can run directly on Bitbucket’s Linux runners, but you can also send them to Capgo Build so both platforms use the same signing and upload flow. Use a parallel block:
tags:
'v*':
- parallel:
- step:
name: iOS release
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile && bun run build
- bunx cap sync ios
- bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
- step:
name: Android release
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile && bun run build
- bunx cap sync android
- bunx @capgo/cli@latest build request com.example.app --platform android --build-mode release --android-track internal
L'étape Android nécessite ANDROID_KEYSTORE_FILE, KEYSTORE_KEY_ALIAS, KEYSTORE_KEY_PASSWORD, KEYSTORE_STORE_PASSWORD, and PLAY_CONFIG_JSON Envoyez les modifications web uniquement sans une construction native --platform android.
Envoyer les modifications web sans build natif
La plupart des commits d'une application Capacitor ne concernent que l'HTML, le CSS et le JavaScript. Ceux-ci n'ont pas besoin d'un nouveau binaire. Capgo mises à jour en temps réelUne étape Bitbucket peut décider entre une mise en magasin et un bundle en ligne.
branches:
main:
- step:
name: Release (live update or native)
script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- bun install --frozen-lockfile
- bun run build
- |
if bunx @capgo/cli@latest build needed com.example.app --channel production; then
echo "No native change, shipping a live update"
bunx @capgo/cli@latest bundle upload com.example.app --channel production
else
echo "Native change detected, building iOS"
bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
fi
build needed sort avec un code code 0 lorsque aucune mise en production native n'est requise et 1 lorsque c'est le cas. Elle compare les versions des plugins natives avec celles qui sont en direct sur le canal. Elle ne voit pas les modifications manuelles sous ios/, il faut donc forcer le chemin natif lorsque ces fichiers changent. Le modèle complet est dans Choisissez automatiquement live update ou une build native.
Option 2 : Un exécuteur macOS autonome
Si vous possédez déjà des Macs, vous pouvez les enregistrer comme exécuteurs Bitbucket et exécuter Xcode vous-même.
Enregistrer l'exécuteur
- Ouvrez dans Bitbucket Paramètres de la répertoire > Exécutants (ou Workspace settings > Workspace runners pour la partager et cliquez Add runner.
- Choisissez MacOS comme le système. Bitbucket affiche une commande de démarrage avec un UUID d'exécuteur et des informations d'authentification OAuth.
- Sur le Mac, installez un JDK pris en charge (l'exécuteur est un programme Java), Xcode 26 et fastlane. Ensuite, collez la commande de démarrage.
- Maintenez le runner en cours en tant qu'agent de démarrage pour qu'il survive aux redémarrages.
Une étape cible l'exécuteur avec les étiquettes :
pipelines:
tags:
'v*':
- step:
name: iOS build on our Mac
runs-on:
- self.hosted
- macos
script:
- bun install --frozen-lockfile
- bun run build
- bunx cap sync ios
- echo "$BUILD_CERTIFICATE_BASE64" | base64 --decode > dist.p12
- echo "$PROFILE_BASE64" | base64 --decode > profile.mobileprovision
- bundle exec fastlane ios beta
Les exécutants macOS s'exécutent directement sur l'hôte avec l'environnement de votre utilisateur, et non dans un conteneur. Installez Bun et Ruby sur le Mac lui-même.
Un Fastfile minimal
default_platform(:ios)
platform :ios do
lane :beta do
setup_ci(force: true)
api_key = app_store_connect_api_key(
key_id: ENV["APPLE_KEY_ID"],
issuer_id: ENV["APPLE_ISSUER_ID"],
key_content: ENV["APPLE_KEY_CONTENT"],
is_key_content_base64: true
)
import_certificate(
certificate_path: "dist.p12",
certificate_password: ENV["P12_PASSWORD"],
keychain_name: "fastlane_tmp_keychain",
keychain_password: ""
)
install_provisioning_profile(path: "profile.mobileprovision")
update_code_signing_settings(
path: "ios/App/App.xcodeproj",
use_automatic_signing: false,
team_id: ENV["APP_STORE_CONNECT_TEAM_ID"],
code_sign_identity: "Apple Distribution",
profile_name: ENV["PROFILE_NAME"]
)
increment_build_number(
xcodeproj: "ios/App/App.xcodeproj",
build_number: latest_testflight_build_number(api_key: api_key) + 1
)
build_app(
project: "ios/App/App.xcodeproj", # use workspace: "ios/App/App.xcworkspace" for CocoaPods projects
scheme: "App",
export_method: "app-store"
)
upload_to_testflight(api_key: api_key, skip_waiting_for_build_processing: true)
end
end
Nouveaux Capacitor 8 projets utilisent le gestionnaire de packages Swift, donc vous construisez App.xcodeprojLes anciens projets qui utilisent encore CocoaPods construisent App.xcworkspace et besoin pod install sur l'ordinateur de la plateforme. Notre GitHub guide Actions iOS explique chaque action de fastlane en profondeur; la voie est la même sur Bitbucket.
Ce que vous vous inscrivez
La sauvegarde est la partie facile. Un Mac autonome signifie aussi :
- Mises à jour de Xcode lorsque Apple augmente le minimum SDK, testé avant la date limite.
- État de la clé de chaîne. Une tâche crashée peut laisser derrière elle une clé de cryptage temporaire, et la prochaine tâche récupère la mauvaise identité.
- Aucune isolation. Les tâches s'exécutent sur le même disque. DerivedData, anciens archives et simulateurs l'emplissent.
- Une construction à la fois par exécutant, à moins que vous achetiez plus de Macs.
- Certificats et profils qui expirent tous les ans et doivent être renouvelés sur la machine.
C'est un échange raisonnable si votre équipe gère déjà des Macs. Si personne ne veut s'occuper de cette box, Option 1 la supprime.
Troubleshooting
| Symptôme | Cause probable et correction |
|---|---|
CAPGO_TOKEN is not set |
La variable manque, ou il s'agit d'une variable de déploiement et l'étape n'a pas de deployment: clé. |
Étape fonctionne sur main mais pas sur les étiquettes |
Les variables de déploiement ne s'appliquent qu'aux étapes qui déclarent l'environnement de déploiement. |
Provisioning profile doesn't match bundle ID |
Le profil a été créé pour un autre ID d'application. Re-run build init et re-exporter. |
| L'authentification de App Store Connect a échoué | Identifiant de l'émetteur ou de la clé ID incorrect, ou une clé révoquée. Vérifiez la clé dans App Store Connect > Utilisateurs et accès > Intégrations. |
| L'upload a été rejeté pour la version SDK | La build utilisait un Xcode plus ancien que 26. Mettez à jour le Mac auto-hébergé. |
| L'étape auto-hébergée est bloquée en « En attente » | Runner offline or labels do not match self.hosted et macos. |
errSecInternalComponent pendant la signature de code |
Le cléchain est verrouillé dans une session non interactive. Utilisez setup_ci et importer le certificat dans la clé temporaire. |
| Le build passe mais rien n'est disponible sur TestFlight. | La mise à jour prend plusieurs minutes, et la conformité à l'exportation peut être en attente. Ajoutez ITSAppUsesNonExemptEncryption à Info.plist if your app uses only standard encryption. |
More fixes for failing pipelines are in Résoudre les erreurs de construction dans les pipelines CI/CD de Capacitor. et le Capgo Build troubleshooting guide.
guide de dépannage de construction __CAPGO_KEEP_0__
Pick a self-hosted Mac when you already own the hardware, need full control of the Xcode environment, or build native Swift targets that are not Capacitor apps. Pick a cloud build from a Linux step when your app is a Capacitor app and you want Bitbucket to stay the single place your team configures CI. Either way, keep the build logic in bitbucket-pipelines.yml Placez les matériaux de signature à côté de votre code, dans des variables sécurisées, et n'exécutez les builds natifs que lorsque les code natifs ont effectivement changé.