Build de l'AAB signé dans GitHub Actions (gratuit) → envoi via Fastlane
supply avec un compte de service Google. Aucun service cloud payant, tu
déclenches la publication manuellement.
⚠️ Compte personnel : Google impose, pour un nouveau compte développeur personnel, un closed testing d'au moins 12 testeurs pendant 14 jours avant d'ouvrir la Production. On vise donc : Internal → Closed → Production.
Play App Signing gère la clé d'app finale ; toi tu ne gères qu'une upload key (récupérable en cas de perte).
keytool -genkeypair -v -keystore upload.keystore -alias twinlens \
-keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=Devitek, O=Devitek, C=FR"
# (note le mot de passe choisi)Enregistre les secrets GitHub (
gh secret set ANDROID_KEYSTORE_BASE64 --repo Devitek/dual < <(base64 -w0 upload.keystore)
gh secret set ANDROID_KEYSTORE_PASSWORD --repo Devitek/dual -b '<motdepasse>'
gh secret set ANDROID_KEY_ALIAS --repo Devitek/dual -b 'twinlens'
gh secret set ANDROID_KEY_PASSWORD --repo Devitek/dual -b '<motdepasse>'Garde une sauvegarde de upload.keystore hors du dépôt.
- Google Cloud Console → crée un projet → IAM & Admin → Service Accounts → nouveau compte de service → crée une clé JSON.
⚠️ Rôle IAM Cloud (sinon 403) : sur le projet Google Cloud, accorde au compte de service le rôle « Service Usage Consumer » (roles/serviceusage.serviceUsageConsumer). Les clients Google envoient un en-têteX-Goog-User-Project(projet de quota) qui exigeserviceusage.services.use. Sans ce rôle :The caller does not have permission, même avec tous les droits Play.gcloud projects add-iam-policy-binding <PROJECT_ID> \ --member="serviceAccount:<SA_EMAIL>" \ --role="roles/serviceusage.serviceUsageConsumer"
- Play Console → Users & permissions → invite l'e-mail du compte de service avec les droits Releases + Gérer la présence sur le Play Store.
- Secret GitHub :
gh secret set PLAY_SERVICE_ACCOUNT_JSON --repo Devitek/dual < play-service-account.json
- Nouvelle application, package
fr.devitek.twinlens, langue par défaut, gratuite. - Play App Signing : laisser activé (par défaut).
- Fiche : les textes/images sont déjà prêts dans
fastlane/metadata/android/(fr-FR + en-US). Tu peux les saisir à la main, ou les pousser (étape 5). - Politique de confidentialité :
https://devitek.github.io/dual/privacy.html. - Data safety : « aucune donnée collectée / partagée » (tout est local) — reste
valable même avec le géotag (v1.9.0). La localisation est traitée uniquement sur
l'appareil (écrite dans l'EXIF de la photo) et n'est jamais transmise. Au sens
Google, « collecte » = données envoyées hors de l'appareil → il n'y a donc rien
à déclarer comme collecté/partagé. Ne rien changer.
- ✅ Géotag = localisation PREMIER PLAN uniquement (
ACCESS_FINE_LOCATION+COARSE, jamaisACCESS_BACKGROUND_LOCATION). Le formulaire de déclaration de localisation N'APPARAÎT PAS et n'est PAS requis : d'après la doc Google (Understanding location in the background permissions), ce formulaire ne se déclenche que si le manifeste contientACCESS_BACKGROUND_LOCATION. Rien à remplir côté « Autorisations sensibles » pour la localisation. - Pas de « prominent disclosure » séparée à ajouter : exigée pour le background seulement ; ici le réglage opt-in + le prompt de permission système suffisent.
- Politique de confidentialité (
docs/privacy.html) : déjà à jour (géotag optionnel, on-device, jamais partagé).
- ✅ Géotag = localisation PREMIER PLAN uniquement (
- Content rating (IARC), audience cible, déclaration pub = non.
⚠️ ErreurYou must let us know whether your app includes any health featuresà l'upload Fastlane — même quand « Applis de santé » apparaît « Traitée » dans Contenu de l'application. L'AAB se construit bien + est attaché à la release GitHub ; seul l'upload_to_play_storeéchoue au commit de l'edit.- ❌
changes_not_sent_for_review: true(dans leFastfile) ne suffit PAS (testé : fastlane retente même viarescue_changes_not_sent_for_review, l'erreur persiste). - Cause probable : Google a étendu le formulaire « Applis de santé » (Health Connect / types de données) ; une réponse ancienne s'affiche « Traitée » mais l'API la considère incomplète.
- Contournement (dans l'ordre) :
- Onglet « Attention requise » de Contenu de l'application → traiter tout item santé en attente ; sinon Applis de santé → Gérer → re-répondre + Enregistrer.
- Si toujours bloqué : 1 upload MANUEL via la console (Test interne → Créer une
release → déposer
TwinLens-vX.Y.Z.aabdepuis la release GitHub). Ça débloque en général les uploads API suivants. - Puis relancer
gh workflow run release-android.yml -f upload_to_play=true.
- ❌
- 1er dépôt manuel : Google exige que le premier AAB soit déposé à la main.
Lance
gh workflow run release-android.yml -f upload_to_play=false→ l'AAB signé est attaché à la release GitHub (TwinLens-vX.Y.Z.aab, aussi en artefacttwinlens-aab). Télécharge-le, puis Play Console → Internal testing → Create release → upload. Ensuite, tout passe par Fastlane.
- Couper une release applicative : merge sur
main→ PR release-please → merge → tagvX.Y.Z(leversionNameen découle ; leversionCode= numéro de run CI). - Lancer le workflow Release (Play Store) :
# 1re fois (AAB attaché à la release, PAS d'envoi Play → dépôt manuel) : gh workflow run release-android.yml --repo Devitek/dual -f upload_to_play=false # ensuite (build + envoi auto sur la piste choisie) : gh workflow run release-android.yml --repo Devitek/dual \ -f upload_to_play=true -f track=internal -f release_status=draft
- Play Console → vérifier la release (draft) → Rollout.
gh workflow run store-metadata.yml --repo Devitek/dualLe workflow Store Metadata pousse fastlane/metadata/android/ (6 langues :
titres, descriptions, icône, feature graphic, captures phone + 7"/10") via l'API
Google Play brute (store/upload_listing.py). On n'utilise PAS fastlane supply
ici : supply veut rattacher des notes de version à un versionCode même en mode
« fiche seule », ce qui casse sur une app neuve. Les notes de version restent
poussées avec l'AAB par release-android.yml (fastlane internal).
Les captures livrées sont des mockups. Pour des captures réelles de l'app :
# pré-requis : adb (platform-tools) + ImageMagick, téléphone en débogage USB.
# règle d'abord la langue du téléphone sur la locale visée.
bundle exec fastlane android capture_phone locale:fr-FR count:4La lane te fait naviguer écran par écran (Entrée entre chaque), capture via adb,
normalise au ratio ≤ 2:1 exigé par Play, et écrit dans
fastlane/metadata/android/<locale>/images/phoneScreenshots/. Publie ensuite via
le workflow Store Metadata.
🔜 Tablettes 7"/10" : la lane est prête à évoluer via une option
device:(phone|seveninch|teninch) pointant vers le bon dossier — il suffira de brancher une tablette. Les mockups 7"/10" actuels restent en attendant.
- Source de vérité des 5 secrets de release (
ANDROID_KEYSTORE_BASE64,ANDROID_KEYSTORE_PASSWORD,ANDROID_KEY_ALIAS,ANDROID_KEY_PASSWORD,PLAY_SERVICE_ACCOUNT_JSON) : gérés en SOPS (chiffrés, versionnés) dans le dépôt d'infrastructure NixOS du mainteneur, répliqués sur plusieurs machines. Le keystore d'upload y est sauvegardé : GitHub n'est qu'une copie de travail. - Perte de l'upload key (dernier recours) : Play App Signing détient la clé d'app → demander la réinitialisation de la clé d'upload dans la Play Console (Configuration → Signature de l'app), puis re-provisionner les secrets depuis SOPS.
- Le job
release-android.ymls'exécute dans l'environment GitHubplay-internal(déploiements restreints àmain+ tagsv*). Pour que les secrets soient invisibles des autres workflows : les recréer au niveau de l'environment (Settings → Environments → play-internal → Environment secrets, valeurs depuis SOPS), puis supprimer les copies repo-level — les secrets d'environment priment. - Pas de reviewer requis sur l'environment : le merge de la release PR est déjà la validation humaine (une seule porte, assumé).
- Promotion (interne → fermé → production) : workflow Play Promote
(
workflow_dispatch) → lanefastlane android promote. Aucun rebuild : la version déjà uploadée change de piste. Production = rollout progressif (défaut 10 %) ; augmenter ensuite depuis la console (ou relancer le workflow avec une fraction plus grande). - Rollback binaire (pas d'OTA — ADR 0001) :
- Play Console → Production → Arrêter le déploiement de la version fautive ;
- release patch (
fix:→ merge → merge release PR) → nouvelle version ; - issue 🚨 « Incident de release » (cause racine en clôture).
- Chaque upload doit augmenter le
versionCode→git rev-list --count HEAD(monotone tant qu'on ne réécrit jamais l'historique demain— cf. AGENTS.md §2.4). release_status: draft= rien n'est diffusé tant que tu n'as pas cliqué Rollout.- R8/minify actif (expo-build-properties) : le
mapping.txtest uploadé sur Play (crashs Vitals désobfusqués) et attaché à la GitHub Release. Ne jamais le perdre : sans lui, les stacks de cette version sont illisibles. - Assets de la fiche :
fastlane/metadata/android/· sources des captures :store/screenshots/src/(régénérables via Chrome headless).