Flavors natifs, Détection automatique de l'app_id et Matrice Multi-Canal
Dans une architecture d'entreprise, une même base de code Flutter est généralement compilée selon plusieurs variantes (Flavors : dev, staging, prod), déployée sur deux plateformes (Android et iOS) et diffusée à travers plusieurs canaux (Channels : staging, beta, stable).
Cette page détaille comment tk_ota_push résout automatiquement l'identifiant d'application (app_id) et articule les combinaisons Flavor × Plateforme × Canal.
1. Cascade de résolution de l'app_id en 9 niveaux
La méthode FlutterProject.resolveEffectiveAppId(...) (lib/src/cli/project.dart) détermine l'app_id effectif selon un ordre de priorité strict de 1 (plus haute priorité) à 8/9 (détection native automatique) :
app_id)- Option CLI explicite : Flag
--app-id <id>(priorité maximale). - Variables Dart Define : Variable
--dart-define=APP_ID=.... - Fichier de configuration : Fichier de build
--dart-define-from-file. - Fichier
shorebird.yaml: Valeur par défaut configurée à la racine du projet.
Injection automatique dans shorebird.yaml (ensureShorebirdYamlAppId)
Même si vous ne renseignez aucun app_id dans votre fichier shorebird.yaml source (approche Zero-Config), la CLI appelle project.ensureShorebirdYamlAppId(effectiveAppId) avant de lancer la compilation afin que le moteur Rust embarqué dans le binaire mobile dispose toujours de la clé app_id requise dans flutter_assets/shorebird.yaml.
2. Détection native automatique sur Android et iOS (detectNativeAppId)
Lorsque aucun app_id n'est forcé par flag ou par shorebird.yaml, detectNativeAppId(platform, flavor, environment) inspecte directement les fichiers natifs du projet :
Sur Android (_detectAndroidApplicationId)
- Ouvre
android/app/build.gradle.kts(prioritaire) ouandroid/app/build.gradle. - Extrait l'
applicationIdde base (ou à défaut lenamespace). - Si un
flavorouenvironmentest actif (ex:devoustaging), cherche le bloc correspondant dansproductFlavorsoubuildTypes(supporte la syntaxe Groovydev { ... }et la syntaxe Kotlin DSLcreate("dev") { ... }) :- Si le bloc contient un
applicationIdspécifique (ex:applicationId "com.entreprise.staging.custom"), retourne cet identifiant. - Si le bloc contient un
applicationIdSuffix(ex:applicationIdSuffix ".dev"), concatène ce suffixe à l'applicationIdde base (ex:com.saramoney.retail.dev).
- Si le bloc contient un
- En repli final, lit l'attribut
package="..."dansandroid/app/src/main/AndroidManifest.xml.
// Exemple android/app/build.gradle
android {
defaultConfig {
applicationId "com.saramoney.retail"
}
flavorDimensions "env"
productFlavors {
dev {
dimension "env"
applicationIdSuffix ".dev" // Résolu automatiquement en : com.saramoney.retail.dev
}
staging {
dimension "env"
applicationId "com.saramoney.staging.custom" // Résolu en : com.saramoney.staging.custom
}
prod {
dimension "env" // Résolu en : com.saramoney.retail
}
}
}
Sur iOS (_detectIosBundleId)
- Ouvre
ios/Runner.xcodeproj/project.pbxproj. - Collecte toutes les valeurs de
PRODUCT_BUNDLE_IDENTIFIERen excluant les cibles de test (RunnerTests,Tests) et les variables Xcode non résolues ($(...)). - Si un
flavorest fourni, sélectionne en priorité l'identifiant contenant le nom du flavor (insensible à la casse), sinon le premier identifiant valide. - En repli final, lit
<key>CFBundleIdentifier</key>dansios/Runner/Info.plist.
3. Déclaration explicite dans shorebird.yaml (environments & flavors)
Si vos identifiants d'application sur le serveur OTA diffèrent de vos applicationId / PRODUCT_BUNDLE_IDENTIFIER natifs, déclarez-les dans shorebird.yaml :
tenant_id: sara-money
base_url: https://ota.saramoney.internal
channel: stable
auto_update: false
patch_verification: strict
# Identifiant par défaut si aucun flavor/env n'est spécifié
app_id: com.saramoney.retail
# Priorité 1 dans shorebird.yaml
environments:
dev: sara-money-dev
staging: sara-money-staging
prod: sara-money-prod
# Priorité 2 dans shorebird.yaml
flavors:
dev: sara-money-dev
staging: sara-money-staging
prod: sara-money-prod
4. Comprendre la combinaison : Flavor × Plateforme × Canal
Il est essentiel de distinguer le rôle du Flavor (au moment de la compilation du binaire) et du Canal / Track (au moment de la diffusion d'un patch) :
| Dimension | Niveau d'action | Ce qu'elle isole | Exemple |
|---|---|---|---|
Flavor (--flavor) | Compilation binaire (Gradle / Xcode) | Deux applications distinctes avec leur propre app_id, icône, backend API et certificat. | dev (com.app.dev), prod (com.app) |
Plateforme (-p) & Arch (-a) | Format binaire CPU | Format d'exécutable spécifique à l'OS et au processeur. | android (aarch64, arm), ios (aarch64) |
Canal (-c, --channel / UpdateTrack) | Diffusion OTA au sein d'un même app_id | Sous-population d'utilisateurs d'une même Release recevant un patch avant les autres. | staging (QA / Canary), beta, stable (Public) |
Exemples de commandes combinant Flavor, Plateforme et Canal
# 1. Publier la Release Production Android (AAB) sur le flavor 'prod'
tk-ota-push release -p android \
--flavor prod \
-t lib/main_prod.dart \
--dart-define-from-file=env/prod.json \
--artifact aab
# 2. Soumettre un patch sur le flavor 'prod', mais uniquement sur le canal 'staging' (Canary QA)
tk-ota-push patch -p android \
--flavor prod \
-t lib/main_prod.dart \
--dart-define-from-file=env/prod.json \
--artifact aab \
--channel staging \
-m "Validation QA sur le binaire de production"
# 3. Une fois validé par la QA sur le canal 'staging', promouvoir le patch vers 'stable'
tk-ota-push promote --patch-id <UUID_DU_PATCH> --to-channel stable