Como um script fez o deploy do nosso app ficar até 400% mais rápido
Para quem já conhece o React Native, deve conhecer o conceito de deploy over the air ou até mesmo pelo serviço mais conhecido desse conceito que é o CodePush da Microsoft.
Quando temos um aplicativo em React Native existem duas partes de código, o código nativo escrito em Java/Kotlin e Swift/Objective-C, que geralmente são pouco alterados e a segunda parte é o código JavaScript que contém a lógica de componentes, bibliotecas e tudo que é utilizado nessa camada JavaScript. Como comentei, as alterações no código nativo são raras, com isso o conceito de over the air é o aplicativo conferir se existe uma nova versão desse código JavaScript e, caso tenha, baixar para o aplicativo local da pessoa, ou seja, conseguimos criar novas features que alteram só o JavaScript, enviar essa atualização com algum serviço como o CodePush, e então a pessoa terá o aplicativo atualizado sem precisar atualizar na loja de aplicativo do seu celular.
Pipeline e a lentidão
Utilizamos Github Actions em nossa pipeline, com isso geramos uma versão do aplicativo com as alterações da branch na pull request, e em seguida com essa versão conseguimos rodar nossos testes e2e para validar se as alterações não afetam nossos fluxos principais.
O tempo de build do Android é relativamente rápido, chegando a no máximo 20 minutos. Comparado a gerar um ambiente de testes da web é lento, mas geralmente fazemos esse build após as aprovações na pull request e testes de QA então se torna aceitável.
Já o build do iOS demorava de 40 minutos até 1 hora, algo totalmente fora do aceitável, e verificando a etapa que mais demora é criar um novo arquivo .app
(um formato da Apple), o arquivo nesse formato se assemelha a uma pasta então podemos mover arquivos para dentro dele, isso já nos dá um ideia de otimização.
Github Actions e o bom e velho script
Para quem não conhece o Github Actions, é uma ferramenta de CI/CD em que os usuários podem criar actions e publicar na loja da ferramenta.
A action que mais facilitou meu trabalho com certeza foi a action de cache, com ela podemos armazenar um artefato qualquer e recuperar escolhendo uma chave única para vários caches específicos.
Na pipeline temos a informação se um cache foi recuperado, então se torna simples gerar o .app
ou não, caso exista um cache.
No pipeline do Github Actions também podemos criar scripts em shell, e foi o que fiz para gerar um novo bundle com as informações do JavaScript e mover para dentro do .app
com as mudanças, também podemos adicionar novas imagens para utilizar no JavaScript então também adicionei no script para mover as imagens para a pasta assets
dentro do .app
.
Um exemplo genérico do script:
#!/bin/bashset -e# use version as cache key, for this command to work keep the package key version at the top of the file (usually second after the name)
pkgVersion=$(cat package.json | grep version | head -1 | awk -F: ‘{ print $2 }’ | sed ‘s/[“,]//g’ | tr -d ‘[[:space:]]’)\# directory when your CI generate and read .app file
releaseDir=”ios/build/Build/Products/Release-iphonesimulator”appDir=$(pwd)# generate new JS bundle, change index.js if your initial file have another name
yarn react-native bundle \
— entry-file index.js \
— platform ios \
— dev false \
— bundle-output ${releaseDir}/main.jsbundleecho “## Unzip cached ios ##”
cd ${releaseDir}
unzip ios-${pkgVersion}.zipecho “## Remove cached zip ##”
rm -rf ios-${pkgVersion}.zipecho “## Update main.jsbundle and assets on YouApp.app ##”
mkdir assets
cp -R ${appDir}/src/assets/png assets
cp -R ${appDir}/src/assets/json assets
cp -R ${appDir}/src/assets/gif assets
mv main.jsbundle YourApp.app
cp -R assets/* YourApp.app/assets/src/assets/
rm -rf assets
# the directories should change depending on your folder structure, check the assets folder inside your .app and you will know how to replaceecho “### Zip .app with new bundle ##”
zip -rm ios-${pkgVersion}.zip YourApp.appcd ${appDir}
E agora um exemplo simples de como ficou o nosso fluxo da pipeline de release do iOS.
Criei um gráfico simples para exemplificar e ficar mais visual a melhoria no tempo de build do iOS. Podemos ver que antes do cache a média de build ultrapassava os 50 minutos, diminuindo com a implementação do cache para uma média de 15 minutos.
Tentei fazer algo parecido com o Android, mas infelizmente quando descompactamos o arquivo .apk
perdemos a assinatura do build gerado e não conseguimos instalar nos celulares/emuladores.
Finalização e comemoração
Após essa implementação o tempo do build no iOS ficou entre 10–18min, uma melhora absurda comparado ao tempo anterior. E não devemos parar por aí, pois para gerar o bundle JS localmente é muito rápido não chegando a 2 minutos, enquanto no CI acaba demorando 5–8 minutos, então melhorar as máquinas com certeza é um próximo passo para evoluir esse tempo tanto no Android, quanto no iOS.
Agora é só comemorar e rodar o build completo somente quando planejarmos uma nova release com alterações nativas.