Skip to content

Latest commit

 

History

History
177 lines (159 loc) · 10.4 KB

File metadata and controls

177 lines (159 loc) · 10.4 KB

Publier TwinLens sur Google Play (gratuit, self-hosted, Fastlane)

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.

1. Clé de signature (upload key) — une seule fois

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 (⚠️ ne committe jamais le keystore) :

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.

2. Compte de service Google (API Play) — une seule fois

  1. Google Cloud Console → crée un projet → IAM & Admin → Service Accounts → nouveau compte de service → crée une clé JSON.
  2. ⚠️ 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ête X-Goog-User-Project (projet de quota) qui exige serviceusage.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"
  3. 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.
  4. Secret GitHub :
    gh secret set PLAY_SERVICE_ACCOUNT_JSON --repo Devitek/dual < play-service-account.json

3. Créer l'app dans la Play Console — une seule fois

  • 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, jamais ACCESS_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 contient ACCESS_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é).
  • Content rating (IARC), audience cible, déclaration pub = non.
  • ⚠️ Erreur You 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 le Fastfile) ne suffit PAS (testé : fastlane retente même via rescue_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) :
      1. Onglet « Attention requise » de Contenu de l'application → traiter tout item santé en attente ; sinon Applis de santé → Gérer → re-répondre + Enregistrer.
      2. Si toujours bloqué : 1 upload MANUEL via la console (Test interne → Créer une release → déposer TwinLens-vX.Y.Z.aab depuis la release GitHub). Ça débloque en général les uploads API suivants.
      3. 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 artefact twinlens-aab). Télécharge-le, puis Play Console → Internal testing → Create release → upload. Ensuite, tout passe par Fastlane.

4. Publier une build

  1. Couper une release applicative : merge sur main → PR release-please → merge → tag vX.Y.Z (le versionName en découle ; le versionCode = numéro de run CI).
  2. 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
  3. Play Console → vérifier la release (draft) → Rollout.

5. Mettre à jour la fiche (textes/captures) sans binaire — depuis la CI

gh workflow run store-metadata.yml --repo Devitek/dual

Le 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).

6. (Option) Vraies captures TÉLÉPHONE depuis ton appareil — en local

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

La 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.

Secrets & sauvegardes (SOPS + environment GitHub)

  • 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.yml s'exécute dans l'environment GitHub play-internal (déploiements restreints à main + tags v*). 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 des pistes & rollback

  • Promotion (interne → fermé → production) : workflow Play Promote (workflow_dispatch) → lane fastlane 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) :
    1. Play Console → Production → Arrêter le déploiement de la version fautive ;
    2. release patch (fix: → merge → merge release PR) → nouvelle version ;
    3. issue 🚨 « Incident de release » (cause racine en clôture).

Rappels

  • Chaque upload doit augmenter le versionCode → git rev-list --count HEAD (monotone tant qu'on ne réécrit jamais l'historique de main — 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.txt est 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).