L'Architecture en Microservices : Approfondissement et Schémas
Pourquoi passer d'un monolithe à une structure en microservice ?
Mettez à jour instantanément tous vos utilisateurs sans passer par la case validation de Google et Apple
Dans cet article, je vais détailler la mise en place de la fonctionnalité Expo Update dans notre application React Native. Expo Update permet d'utiliser la mise à jour OTA (Over-The-Air) pour déployer rapidement des changements à vos utilisateurs sans avoir à passer par une re-soumission sur les stores. Tout comme un site web, cette mise à jour ne sera effective chez vos utilisateurs dès qu'ils auront "rechargé" leur app.
Je vous explique dans cet article comment détecter la présence d'une mise à jour et inciter l'utilisateur à relancer son app.
Voici ci-dessous un schema de ce qui va se dérouler
🚨 A bien noter
Vous ne pouvez utiliser Expo update que pour les mises à jour qui ne concernent que le code Javascript de votre app. Si vous touchez au code natif ou que vous ajoutez un nouveau module natif (bluetooth par exemple). Vous devrez re-soumettre un build.
npx expo install expo-updates
Le fichier eas.json est le point central de la configuration des builds et updates dans Expo. Voici un exemple de sa configuration pour différents environnements et branches de développement:
Fichier eas.json :
{
"build": {
"develop": {
"channel": "develop",
"distribution": "internal",
"env": {
"APP_NAME": "masuperapp-dev",
"API_URL": "https://masuperapp.api.dev/"
}
},
"staging": {
"channel": "staging",
"distribution": "internal",
"env": {
"APP_NAME": "masuperapp",
"API_URL": "https://masuperapp.api.staging/"
}
},
"main": {
"channel": "main",
"env": {
"APP_NAME": "masuperapp",
"API_URL": "https://masuperapp.api.prod/"
}
}
}
}
L'idée est de définir différentes configurations pour chaque environnement (développement, staging, production, etc.). Chaque configuration pointe vers un canal (ou channel) spécifique. Ces canaux permettent de gérer efficacement les mises à jour OTA.
Dans notre cas, il existe une version de build pour chaque branche principales de mon git : develop, staging et main. Tout comme pour un site web, je peux tester sur mon app "develop" les développements en cours. Sur staging, je teste les version avant leur mise en prod. La propriété "distribution": "internal" indique qu'il s'agit de build installables directement sur les devices sans passer par le store.
Nous allons implémenter un mécanisme pour vérifier et appliquer les mises à jour quand l'application passe en avant-plan.
J'ai choisi de créer un wrapper que nous importerons ensuite dans notre fichier /app/_layout.tsx où sont importés mes autres wrapper et providers.
Fichier wrappers/UpdateWrapper.tsx :
import * as Updates from 'expo-updates';
import { type PropsWithChildren, useEffect, useState } from 'react';
import { AppState } from 'react-native';
import { UpdateModal } from '@/components';
/**
* Avec l'API EAS Update, nous pouvons vérifier les mises à jour et effectuer une mise à jour silencieuse si nécessaire
* Cette fonction est appelée lorsque l'application passe en avant-plan
* https://expo.dev/changelog/2023/08-08-use-updates-api
* https://docs.expo.dev/build/updates/
*/
const useEASUpdate = () => {
const [isUpdateAvailable, setIsUpdateAvailable] = useState(false);
const downloadUpdate = async () => {
await Updates.fetchUpdateAsync().then(async () => {
await Updates.reloadAsync();
});
};
/**
* Vérifier les mises à jour lorsque l'application passe en avant-plan
*/
useEffect(() => {
AppState.addEventListener('change', (state) => {
if (state === 'active') {
void Updates.checkForUpdateAsync().then(async (update) => {
if (update.isAvailable) {
setIsUpdateAvailable(true);
}
});
}
});
}, []);
return { isUpdateAvailable, downloadUpdate };
};
/**
* Ce wrapper est responsable de vérifier l'état de l'application (maintenance, mise à jour, etc.)
* et de rediriger l'utilisateur vers la bonne page
*/
export default function UpdateWrapper(props: PropsWithChildren) {
const [hasSeenUpdateModal, setHasSeenUpdateModal] = useState(false);
const { isUpdateAvailable, downloadUpdate } = useEASUpdate();
return (
<>
{props?.children}
<UpdateModal
// Nous affichons la modal de mise à jour douce uniquement si l'utilisateur ne l'a pas encore vue. Elle s'affiche sinon à chaque écran
isVisible={Boolean(isUpdateAvailable && !hasSeenUpdateModal)}
onDismiss={() => {
// Nous ne voulons pas afficher la popup sur chaque écran ou chaque fois que l'application passe en avant-plan
setHasSeenUpdateModal(true);
}}
onPress={() => {
if (isUpdateAvailable) {
void downloadUpdate();
}
}}
/>
</>
);
}
Le composant UpdateModal est une simple modale avec un callback sur le CTA. Elle est visible si une mise à jour est disponible.
Nous voulons automatiser les mises à jour OTA lorsqu'il y a des changements dans certaines branches Git. Pour cela, nous configurons GitHub Actions comme suit :
Fichier .github/workflows/update.yml :
on:
push:
branches:
- develop
- staging
- main
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: 🏗 Setup repo
uses: actions/checkout@v3
- name: 🏗 Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: npm
- name: 🏗 Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Cache dependencies
id: cache
uses: actions/cache@v3
with:
path: ./node_modules
- name: 📦 Install dependencies
run: npm ci --ignore-scripts
# Create a new update if there are no changes in the package.json or package-lock.json
- name: ✨ Create update
run: export GIT_COMMIT_HASH=$(git rev-parse HEAD) && eas update --auto --platform all --non-interactive
Pour la configuration de secrets.EXPO_TOKEN, lisez la doc suivante :
https://docs.expo.dev/accounts/programmatic-access/#personal-access-tokens
Eh oui ! Comme nous venons d'ajouter un nouveau module implicant des modification au niveau natif, nous devons d'abord générer un nouveau build.
En réalité 3 nouveau builds. Un pour chaque branche.
git checkout develop
eas build --platform all --profile develop
git checkout staging
eas build --platform all --profile staging
git checkout main
eas build --platform all --profile main
Le profile correspond au nom donné à chaque profile de builds dans eas.json
Vous pouvez suivre l'état d'avancement de vos builds dans le dashboard Expo
Installez ensuite vos builds sur des devices. Je vous laisse fouiller la doc pour savoir comment faire. Ce n'est pas l'objet de l'article ici.
Désormais, lorsque vous pusherez sur les branches définies dans eas.json, la github action déclenchera un update sur les builds correspondants que vous venez de générer.
Vous avez une config prête pour utiliser Expo update. Il est néanmoins important de bien comprendre certaines choses avant de l'utiliser.
Je vous conseille d'ailleurs de ne pas déclencher d'update sur la branche de production dans un premier temps.
Vous avez remarque la présence de
export GIT_COMMIT_HASH=$(git rev-parse HEAD)
à la fin de ma github action ?
Cette ligne permet d'afficher le numéro de commit sur un écran côté front en utilisant process.env.GIT_COMMIT_HASH. Je vous conseille vivement de l'utiliser pour être capable d'identifier clairement quelle version du code tourne chez vos utilisateurs. C'est hyper utile également pour vos testeurs.
Je vous conseille également de bien versionner vos livraisons en utilisant par exemple release-please
https://github.com/googleapis/release-please
Lorsque vous commencerez à utiliser Expo Update, vous devez bien différencier les numéros de version identifiant vos builds des numéros de version de votre code.
En effet, Expo Update ne mettra pas à jour le numéro de version du build, puisqu'il ne peux pas agir sur le build en lui même.
Exemple :
Il est tout à fait possible de lancer un update depuis un terminal en executant
eas update --auto
Cela créera un update automatiquement sur la branche concernée.
Cela peut être très utile pour faire un rollback rapide après une mise en prod ratée par exemple.
⚠️ Mais attention !!! Lorsque vous exécutez cette commande, expo-updates va prendre en compte es variables d'environnement renseignées dans votre .env local. Faites donc très attention à ne pas pusher en prod des valeurs destinées aux environnement de dev.
Il est possible de mettre à jour qu'un certain pourcentage d'utilisateur. Cela peut être utile lors de la publication d'une feature sensible que vous aimeriez faire tester qu'à un nombre limité d'user pour éviter trop de retours négatifs en cas de bug.
Jusqu'ici, nous avons un peu considéré qu'une branche git équivalait grosso modo a un channel côté expo. Nous les avons effectivement nommées de manière identique pour pus de simplicité.
Mais vous pouvez en réalité lier plusieurs branches git à une seule channel. Cela vous permettra de gérer un update en ne mettant qu'un certain pourcentage d'utilisateur sur la nouvelle version.
Dans mon cas, lorsque je génère une nouvelle release, cela me génère une nouvelle branche avec le nom de la version. (J'ai volontairement simplifié).
Dans votre dashboard expo, allez sur déploiements puis sélectionnez votre channel de production (ici main). Vous pouvez alors sélectionner 2 branches pour un même channel et éditer le pourcentage d'utilisateur sur chaque branche.
Une fois que vous avez validé votre test auprès de ce pourcentage d'utilisateur (ceux sur la branche v1.XX), vous pouvez merger votre branche git correspondante sur main. Un update sera alors déclenché et main et v1.X.X seront alors identiques.
Vous n'avez plus qu'à redescendre le pourcentage de la branche v1.XX à 0.
Expo update est un outil très puissant pour faciliter le processus de mise à jour de vos apps. Il vous permettra de gagner un temps conséquent. Mais bien comprendre son fonctionnement nécessite du temps. Lisez bien la documentation avant de mettre en place cet outil sur vos environement de production.