Automatyzacja lokalnych buildów React Native Expo EAS dla Androida, iOS i Web

W tym artykule chcę podzielić się prostym przykładem automatyzacji lokalnych buildów React Native Expo z wykorzystaniem EAS dla Androida, iOS i Web. Przepływ pracy demonstruje, jak: automatycznie generować buildy specyficzne dla platform, instalować wygenerowany plik .apk na wszystkich podłączonych urządzeniach z Androidem, wdrażać plik .ipa na wszystkich podłączonych urządzeniach z iOS oraz uruchamiać kontener Dockera do obsługi wersji webowej.

Głównym celem tego zestawu jest zapewnienie prostej automatyzacji dla testowych buildów podglądowych. Jednak może on być łatwo dostosowany lub rozszerzony do obsługi buildów produkcyjnych, automatycznej dystrybucji oraz dodatkowych przepływów pracy.

Wszystkie skrypty, konfiguracje i pliki Dockera można znaleźć w repozytorium GitHub: https://github.com/AndreiMaksimovich/react-native-expo-eas-local-build-automation

Dlaczego warto zautomatyzować proces budowania dla React Native Expo EAS

Niezależność

Automatyzując lokalne buildy EAS, nie polegasz wyłącznie na infrastrukturze chmurowej Expo. Nawet jeśli usługa w chmurze ma przestoje, zmienia ceny lub wprowadza ograniczenia, Twój pipeline buildów nadal działa.

Bezpieczne zarządzanie certyfikatami, kluczami API i sekretami

Zautomatyzowane lokalne buildy pozwalają przechowywać klucze API, certyfikaty i produkcyjne sekrety w bezpieczny sposób poza repozytoriami projektów oraz maszynami deweloperskimi. Zamiast tego są one wstrzykiwane podczas procesu budowania, co zmniejsza ryzyko wycieku i utrzymuje poufne dane pod kontrolą.

Szybkość

Lokalne zautomatyzowane buildy mogą być znacząco szybsze. Unikasz oczekiwania w kolejkach chmurowych i zyskujesz większą kontrolę nad alokacją zasobów.

Elastyczność

Mając własny pipeline buildów, możesz rozszerzać go o niestandardowe procedury, na przykład:

  • uruchamianie testów automatycznych przed budowaniem
  • analitykę, optymalizację i kompresję zasobów
  • statyczną analizę kodu i skany bezpieczeństwa

Opis pipeline’u i wymagania

Wymagania systemowe

To środowisko zostało zaprojektowane dla macOS. Należy zainstalować i poprawnie skonfigurować następujące narzędzia:

  • Docker
  • Git
  • Android SDK
  • Xcode oraz Xcode Command Line Tools
  • cfgutil (dostarczany razem z Apple Configurator 2)

Uwaga: adb i cfgutil muszą znajdować się w systemowym PATH.

Projekt React Native Expo

Projekt React Native musi być skonfigurowany do używania buildów EAS.

Umieść lub sklonuj go do katalogu ./react-native-project/.

Kroki budowania

Poniżej znajduje się uproszczony podział kroków wykonywanych przez ten pipeline:

  1. Pobranie repozytorium projektu React Native
  2. Instalacja pakietów npm
  3. Uruchomienie expo prebuild
  4. Konfiguracja credentials
  5. Build dla Androida
  6. Build dla iOS
  7. Build dla Web
  8. Uruchomienie pliku .apk na wszystkich podłączonych urządzeniach z Androidem
  9. Uruchomienie pliku .ipa na wszystkich podłączonych urządzeniach z iOS
  10. Uruchomienie kontenera Dockera z buildem webowym

Credentials

Należy przygotować wymagane poświadczenia dla systemu buildów EAS. Szczegółowe instrukcje znajdują się w dokumentacji Expo: https://docs.expo.dev/app-signing/local-credentials/

Umieść plik z poświadczeniami w lokalizacji: ./credentials/credentials.preview.json

Uwaga: Na macOS 15+ można otworzyć Keychain Access z terminala poleceniem: open -a „Keychain Access”

Uwaga: Credentials iOS muszą zawierać profil Ad Hoc provisioning oraz certyfikat dystrybucyjny (Distribution certificate).

Sekrety i klucze

W tym przykładzie nie wstrzykuję sekretów ani kluczy bezpośrednio do procesu budowania. Jednak można to łatwo zrobić poprzez wprowadzenie zmiennych środowiskowych.

Weźmy jako przykład pakiet react-native-maps i klucze API Google Maps:

  • Na iOS klucz API jest zahardkodowany w kodzie podczas procesu prebuild i musi być dostępny w konfiguracji aplikacji.
  • Na Androidzie klucz API znajduje się w pliku AndroidManifest.xml.

Możliwe podejście:

  1. Przełącz projekt na korzystanie z dynamicznego pliku app.config.ts.
  2. Zdefiniuj klucze API jako zmienne środowiskowe w skryptach budowania (np. export GOOGLE_MAPS_API_KEY_ANDROID=...).
  3. W pliku app.config.ts odczytuj te klucze API ze środowiska i dynamicznie wstrzykuj je do konfiguracji aplikacji.
  4. Dla Androida zaimplementuj własną wtyczkę konfiguracyjną, która pobierze zmienną środowiskową i wstrzyknie ją do manifestu (za pomocą AndroidConfig.Manifest.addMetaDataItemToMainApplication).

Skrypt budowania

W tym przykładzie używam najprostszego możliwego podejścia do automatyzacji – skryptu shellowego, który działa jak pipeline buildów, wykonując wymagane kroki jeden po drugim.

Skrypt powinien być dość oczywisty w działaniu build.preview.sh:

#!/bin/bash

# Switch to the script folder
cd "$(dirname "$0")"

export EXPO_NO_GIT_STATUS=1

# Paths
PROJECT_DIR="react-native-project"
APK_PATH="../builds/android.apk"
IPA_PATH="../builds/ios.ipa"
WEB_PATH="../builds/web"

# Git configuration
USE_GIT="true"
GIT_BRANCH="development"

# Switch to the project dir
cd "$PROJECT_DIR"

# Git checkout
if [ "$USE_GIT" == "true" ]; then
    git restore . --staged --worktree
    git pull
    git checkout $GIT_BRANCH
fi

# Clean
rm -rf android/*
rm -rf ios/*
rm -rf dist/*
rm -rf ../builds/*

# Create buids/web dir
mkdir $WEB_PATH

# Install Node packages and prebuild android/ios projects
npm install
npx expo prebuild --clean

# Copy credentials
cp ../credentials/credentials.preview.json ./credentials.json

# Build Android
eas build --platform android --local --profile preview --output "$APK_PATH"

# Build iOS
eas build --platform ios --local --profile preview --output "$IPA_PATH"

# Build Web
npx expo export -p web

# Move web build to buids/web
mv dist/* "$WEB_PATH"

# Remove changes
if [ "$USE_GIT" == "true" ]; then
    git restore . --staged --worktree
fi

# Install .apk on all connected Android devices
adb devices | grep "device$" | while read -r line; do
    DEVICE_ID=$(echo "$line" | awk '{print $1}')
    if [ "$DEVICE_ID" != "List" ]; then
        adb -s "$DEVICE_ID" install "$APK_PATH"
    fi
done

# Install .ipa on all connected iOS devices
cfgutil --foreach install-app "$IPA_PATH"

# Launch docker container with web build
sh ../docker/Up.sh

Co dalej

Ten przykład można rozbudować do pełnego pipeline’u CI/CD. Niektóre możliwe kierunki to:

Integracja z CI/CD

Części skryptu buildówania mogą być wykorzystane w CI/CD pipeline’ach obsługiwanych przez Jenkins, lokalny GitLab CI/CD lub inne systemy automatyzacji.

Testowanie i zapewnianie jakości

Pipeline produkcyjny powinien integrować testy automatyczne na wielu poziomach (jednostkowe, integracyjne, end-to-end). Powinien również uwzględniać statyczną analizę kodu, linting oraz inne mechanizmy kontroli jakości jako część procesu budowania.

Bezpieczne zarządzanie sekretami

Pipeline powinien być jedynym miejscem, w którym wstrzykiwane/używane są produkcyjne poświadczenia, certyfikaty podpisywania i klucze API. Sekrety nigdy nie mogą być przechowywane w repozytoriach ani na maszynach deweloperskich.

Automatyczna dystrybucja
  • iOS: Automatyzacja dystrybucji plików .ipa przez OTA wraz ze stroną WWW i powiadomieniami e-mail lub Slack, albo bezpośrednie wypychanie buildów do TestFlight.
  • Android: Wgrywanie plików .apk/.aab do kanałów Google Play Internal Testing.
  • Web: Wypychanie zdockerowanego buildu webowego na serwery beta do testów w przeglądarce.