Aller au contenu principal

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) :

Détection de l'identifiant d'application (app_id)
  1. Option CLI explicite : Flag --app-id <id> (priorité maximale).
  2. Variables Dart Define : Variable --dart-define=APP_ID=....
  3. Fichier de configuration : Fichier de build --dart-define-from-file.
  4. 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)​

  1. Ouvre android/app/build.gradle.kts (prioritaire) ou android/app/build.gradle.
  2. Extrait l'applicationId de base (ou à défaut le namespace).
  3. Si un flavor ou environment est actif (ex: dev ou staging), cherche le bloc correspondant dans productFlavors ou buildTypes (supporte la syntaxe Groovy dev { ... } et la syntaxe Kotlin DSL create("dev") { ... }) :
    • Si le bloc contient un applicationId spécifique (ex: applicationId "com.entreprise.staging.custom"), retourne cet identifiant.
    • Si le bloc contient un applicationIdSuffix (ex: applicationIdSuffix ".dev"), concatène ce suffixe à l'applicationId de base (ex: com.saramoney.retail.dev).
  4. En repli final, lit l'attribut package="..." dans android/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)​

  1. Ouvre ios/Runner.xcodeproj/project.pbxproj.
  2. Collecte toutes les valeurs de PRODUCT_BUNDLE_IDENTIFIER en excluant les cibles de test (RunnerTests, Tests) et les variables Xcode non résolues ($(...)).
  3. Si un flavor est fourni, sélectionne en priorité l'identifiant contenant le nom du flavor (insensible à la casse), sinon le premier identifiant valide.
  4. En repli final, lit <key>CFBundleIdentifier</key> dans ios/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) :

DimensionNiveau d'actionCe qu'elle isoleExemple
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 CPUFormat 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_idSous-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