Chez Uptime, nous utilisons généralement React Native et notamment Expo pour proposer à nos clients des applications cross-plateforme, disponibles à terme sur tous les smartphones, qu’ils tournent sur Android ou sur iOS.
Lors de nos phases de développement, nous avons rencontré plusieurs freins, notamment lors de la phase de build pouvant demander des manipulations plus ou moins laborieuses.
Dans cet article, nous allons voir comment automatiser ces builds, directement depuis Gitlab avec EAS.
Avant de démarrer, voici quelques pré-requis :
- Un projet React Native avec Expo, dans lequel vous pouvez faire des builds avec eas.
- Ce projet doit être hébergé sur un projet Gitlab (un équivalent Github existe, mais nous ne l’aborderons pas ici)
- Un compte sur Expo.dev (Un compte gratuit suffit dans un premier temps)
- Une machine pour exécuter le runner Gitlab. Devra être allumé à chaque lancement de nos jobs. Nous vous recommandons d’utiliser un serveur, allumé 24/7. (2GB de RAM recommandé)
Les étapes d’automatisation des builds
1 – Préparation de la pipeline
Ouvrez votre projet sur votre poste de travail, créez un fichier nommé .gitlab-ci.yml et copiez le contenu suivant :
stages:
- build
variables:
NODE_VERSION: "22"
EXPO_TOKEN: $EXPO_TOKEN
# PREPROD - Android
build_preprod_android:
stage: build
image: node:${NODE_VERSION}
script:
- npm install
- npm install -g eas-cli
- eas build --platform android --profile preview --non-interactive
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
when: manual
environment:
name: preprod
# PREPROD - iOS
build_preprod_ios:
stage: build
image: node:${NODE_VERSION}
script:
- npm install
- npm install -g eas-cli
- eas build --platform ios --profile preview --non-interactive
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
when: manual
environment:
name: preprod
Description du contenu
- stages : On décrit les stages à lancer et leur ordre. Ici uniquement build, mais on aurait pu d’autres stages, comme test ou deploy, appelant des jobs qui seraient déclenchés après nos builds
- variables : On nomme les variables utilisées dans notre fichier, puis on leur donne une valeur. Le cas d’EXPO_TOKEN est particulier, nous y reviendrons dans un instant.
- build_preprod_android : Il s’agit du nom de notre premier job, destiné à produire un build Android.
- build_preprod_ios : Comme pour Android, mais destiné à produire un build iOS
- stage : Le fameux nom du stage que l’on appelle dans la premiere partie du fichier “stages”. Identique dans build_preprod_android et build_preprod_ios car ils font partie du même stage.
- image : Il s’agit de l’image docker du container qui sera utilisé pour executer les commandes de la partie scripts. Pour choisir une NODE_VERSION correcte, il convient de vérifier sur Docker Hub l’existence de cette image : node – Official Image | Docker Hub
- script : Les différentes commandes lancées pour créer le build. Grâce à eas, le nombre de commandes est fortement réduit.
- rules : On peut définir des règles pour déclencher ces jobs. Dans notre cas, on ne veut les proposer que sur la branche dev et uniquement manuellement, lorsqu’un développeur déclenchera le job. On peut bien sûr choisir de le faire de manière automatique et pour tous les commits en supprimant la ligne
if: '$CI_COMMIT_BRANCH == "dev"'et en remplaçantwhen: manualparwhen: always - environment : Pour distinguer nos builds si on decide, plus tard, d’avoir d’autres jobs. Par exemple, pour générer des builds de production. Cela peut être intéressant pour faire varier l’adresse de l’API (dev/preprod/prod), l’icone de l’application (avec un badge ou un bandeau “PREPROD” par exemple), etc …
Précisions du fichier yml
Une partie du fichier yml mérite quelques précisions, les commandes décrites dans script :
script:
- npm install # On installe nos dépendances
- npm install -g eas-cli # On installe eas-cli qui permet de communiquer avec expo.dev
- eas build --platform android --profile preview --non-interactive
La dernière ligne utilise eas-cli, installé juste avant. –platform nous permet de choisir la destination, ici un build Android. Il suffit de remplacer par iOS pour ordonner la création d’un .ipa pour appareil Apple. –profile permet d’appeler la configuration preview, définie dans eas.json, à la racine de votre projet.
–non-interactive : Désactive les questions interactives d’EAS, obligatoire en CI/CD où personne ne peut répondre aux prompts.
Le fichier eas.json et la partie preview
Voici le contenu du fichier eas.json, avec la partie preview :
{
"cli": {
"version": ">= 12.4.1",
"appVersionSource": "remote"
},
"build": {
...
"preview": {
"distribution": "internal",
"autoIncrement": true,
"credentialsSource": "remote",
"env": {
"APP_ENV": "preprod",
"EXPO_PUBLIC_API_URL": "https://ma-preprod.mon-app.up-time.fr/api",
"GOOGLE_SERVICES_JSON": "@GOOGLE_SERVICES_JSON",
"GOOGLE_SERVICES_PLIST": "@GOOGLE_SERVICES_PLIST"
},
"android": {
"buildType": "app-bundle"
}
},
...
}
Précisions de la partie preview
- distribution : Build pour distribution interne (testeurs). Pas destiné aux stores publics. Génère un fichier installable directement (APK/IPA via TestFlight/Internal Testing).
- autoIncrement : Incrémente automatiquement le
versionCode(Android) etbuildNumber(iOS) à chaque build. Plus besoin de le faire manuellement ! - credentialsSource : Les certificats et clés de signature (keystore Android, certificats iOS) sont stockés sur les serveurs Expo, pas en local. Si vous utilisez déjà eas, c’est normalement déjà indiqué ainsi dans votre eas.json.
- buildType : “app-bundle” → Génère un Android App Bundle (.aab) plutôt qu’un APK. C’est le format moderne requis par Google Play, plus optimisé (APK générés à la demande selon l’appareil).
La partie env permet de définir les variables d’environnement, habituellement dans le fichier .env sur un environnement de développement.
- APP_ENV : Variable d’environnement accessible dans votre code via
process.env.APP_ENV. Permet de différencier preprod/prod dans le code. - EXPO_PUBLIC_API_URL : URL de votre API. Le préfixe
EXPO_PUBLIC_rend cette variable accessible côté client (dans votre app React Native). - “GOOGLE_SERVICES_…” : Le JSON pour Android, le plist pour iOS. Le @ dans la valeur signifie que EAS va chercher le contenu de ces variables dans vos secrets Expo. Pour les uploader, nous avons utilisé la commande suivante (et l’équivalent pour le plist) :
eas secret:create --name GOOGLE_SERVICES_JSON --value "$(cat google-services.json)"
→ Notre pipeline est prête. Si vous commitez vos modifications, vous pourrez observer que la zone CI/CD dans votre Gitlab, contient des modifications !
2 – Configuration de EXPO_TOKEN dans Gitlab
Dans notre fichier .gitlab-ci.yml, nous avons créé une variable EXPO_TOKEN.
Jusqu’ici, celle-ci n’est toujours pas initialisée.
Pour ce faire, rendez-vous sur le site d’Expo.dev : Expo
Si vous êtes connecté, vous devriez arriver sur la page de votre compte (/accounts/nom-du-compte). Dans le menu de gauche, rendez-vous dans Access Token, dans la partie Credentials
Cliquez sur le bouton ( + Add Robot ) en haut à droite, et remplissez ces informations :
Notre token sera utilisé par un GitlabRunner que nous appellerons GitlabRunner1. Nous lui donnons le rôle de developer car ses droits seront suffisants dans le cadre d’une CI/CD. Vous pouvez lui donner le nom que vous souhaitez.
Depuis une machine où eas-cli est installé, vous pouvez aussi utiliser la commande suivante :
eas token:create --type robot --name "GitlabRunner1" --role developer
→ Dans les deux cas, une nouvelle partie avec un bouton ( + Add Token ) en haut à droite sur la page des access token d’expo.dev apparait. Cliquez sur le bouton, remplissez la modale :
Un token est généré et apparait à l’écran. Copiez le grâce au bouton prévu à cet effet, et retournez sur votre Gitlab.
Sur votre projet, rendez-vous dans Settings > CI / CD :
Sur l’écran CI/CD, dépliez la partie “Variables”, vous devriez voir cela apparaitre :
En haut à droite du tableau CI/CD Variables, cliquez sur Add variable. Remplissez ensuite la modale comme suit, sans oublier de coller le token copié quelques secondes plus tôt dans la textarea “Value” :
⚠️ Pensez bien à cocher ‘Mask variable’ et ‘Expand variable reference’
Votre pipeline est prête à communiquer avec Expo.dev. Il ne manque plus que notre runner pour executer les jobs.
3 – Le runner
Le runner, c’est une machine (serveur/container) qui exécute vos jobs CI/CD.
GitLab envoie les commandes de votre .gitlab-ci.yml au runner, qui les exécute dans l’environnement Docker spécifié (ex: node:22) et renvoie les résultats à GitLab.
Pour les besoins de cet article, nous allons lancer le runner depuis notre machine de développement, mais les commandes sont identiques sur un serveur en SSH.
# 1. Télécharger le binaire
sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# 2. Donner les permissions d'exécution
sudo chmod +x /usr/local/bin/gitlab-runner
# 3. Créer un utilisateur dédié
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
# 4. Installer et lancer le service
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
# 5. Enregistrer le runner (nécessite token de votre projet)
sudo gitlab-runner register
Lors du register, on vous demandera :
- GitLab instance URL :
https://gitlab.com(ou votre instance) - Registration token : À récupérer dans
Settings > CI/CD > Runnersde votre projet. (Vous pouvez aussi le créer depuisAdmin Area > Runnerssi vous comptez utiliser ce Runner depuis plusieurs projets hebergés sur la même instance Gitlab. - Description :
Mon runner local(ou ce que vous voulez) - Tags : Laisser vide ou
docker(optionnel)- Les tags permettent de cibler des runners spécifiques. Laisser vide pour que ce runner accepte tous les jobs.
- Executor : Tapez
docker - Default Docker image :
node:22(oualpine:latest)
⚠️ Important : Pour utiliser Docker comme executor, Docker doit déjà être installé sur la machine :
# Si Docker n'est pas installé
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
En fonction de votre choix, vous devriez voir votre Runner apparaitre dans Project Runners ou dans Instance Runners en vous rendant dans Votre Projet > CI/CD et en dépliant la partie Runners
4 – Déclenchement de la pipeline
Votre Gitlab a désormais son runner, ses jobs dans une pipeline grâce au .gitlab-ci.yml et est correctement branché à expo.dev grâce à l’EXPO_TOKEN.
Si vous avez pushé le fichier .gitlab-ci.yml sur votre repository, vous devriez voir une roue crantée avant l’id de votre dernier commit :
Soit une nouvelle entrée au status Blocked dans le menu Pipeline :
Cliquez sur l’une ou l’autre des roues crantées. Vous allez vous retrouver sur une page avec vos jobs grisés.
Vous pouvez déclencher le job de votre choix et vaquer à d’autres occupations ou vous rendre sur le job en cliquant dessus pour observer son exécution.
Au bout d’un certain temps (qui peut varier en fonction de la taille de votre projet), vous devriez voir le terme “Job succeeded”, en vert :
Rendez-vous sur expo.dev, dans le menu Deploy > Builds
Vous pouvez retrouver vos builds générés grâce à votre pipeline fraîchement installée :
En cliquant sur l’un ou l’autre on peut retrouver un bouton Download qui permet de télécharger le fichier IPA ou AAB si la publication automatique depuis expo.dev n’est pas configurée.
Et pour terminer…
Félicitations ! ✅ Vous avez mis en place un pipeline CI/CD complet qui automatise vos builds React Native. Votre équipe gagne désormais un temps précieux à chaque livraison.
Pour aller plus loin :
- Configurer des notifications Slack/Discord des résultats de build
- Automatiser la publication sur App Store Connect et Google Play Console
- Ajouter des tests automatisés avant le build