Configurando CI/CD com Github Actions e Firebase App Distribution para projetos Android
Tags: android, github, firebase
Published: 2020-05-12 16:05:36

[English Version](https://dev.to/denisvieira05/setting-up-ci-cd-with-github-actions-and-firebase-app-distribution-for-android-projects-4en5)

A integração contínua (CI) e a implantação contínua (CD) são os primeiros passos para começarmos a automatizar e agilizar o nosso processo de desenvolvimento. Dessa forma, temos certeza de que nosso código é sempre verificado, testado e atende aos nossos padrões de qualidade antes de ser implantado.

Atualmente, existem várias ferramentas que podem nos ajudar a automatizar tudo isso. Um dos mais populares são Jenkins, GitLab, Travis CI, CircleCI ou Fastlane. Mas agora para nossa felicidade o queridinho do open source, o Github, entrou no jogo com o [GitHub Actions](https://github.com/features/actions).

No Github existe um marketplace que contém varios Apps e Actions disponíveis para adicionarmos grandes poderes em nossos projetos e o melhor é que são facilmente acopláveis e desacopláveis com simples configurações. Estes itens funcionam como extensões ou plugins que automatizam varias tarefas importantes para nosso projeto e também são open-source e desenvolvidos por vários desenvolvedores. Para nosso setup utilizaremos as actions, [Checkout](https://github.com/marketplace/actions/checkout) para realizar o checkout do nosso repositório, [setup-java](https://github.com/marketplace/actions/setup-java-jdk) para configurar o Java JDK e [Firebase App Distribution](https://github.com/marketplace/actions/firebase-app-distribution) para finalmente enviarmos nossa APK para o Firebase.

Então, vamos lá.

Primeiramente, vá na aba "Actions" no repositorio do projeto android que desejamos automatizar, após iremos no link "setup a workflow yourself" para criar a nossa própria configuração através do proprio editor do Github, lembrando que após finalizar o arquivo de todos os workflows é preciso realizar o commit dele, mas se desejar você também pode fazer manualmente no seu editor de código pessoal, para isso precisamos apenas criar uma pasta na raiz do seu projeto com o nome ".github" e dentro dela criar outra pasta com nome "workflows" e nela adicionaremos todos os nossos arquivos de workflow.

Baseando na necessidade de criar um setup para aplicar um workflow baseado em no [Git Feature Branch](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow), iremos basicamente criar 2 workflows, o "Pull Requests CI" que irá iniciar quando houver um novo pull request para Master ou branch de release, verificando se os testes passam, rodando build e gerando uma APK que será armazenada no Github Artifacts para testes funcionais de uma nova feature ou bug desenvolvido e o "Master Release CI" que irá rodar quando houver alguma atualização na Master ou branch de release, fazendo as mesmas verificações mas dessa vez enviando a APK de release para o Firebase App Distribution. 

Obs: Fique a vontade para utilizar qualquer nome para seus workflows, Jobs e Steps deste post, os utilizados são só sugestões baseadas na minha necessidade atual.

Vamos iniciar então criando o Pull Requests CI, primeiramente iremos configurar em que caso iremos rodar esse workflow, nesse caso em todos os pull requests criados para as branchs "master" ou "release..".

```
name: Pull Requests CI

on:
  pull_request:
    branches:
      - 'master'
      - 'release*'
```

Agora iremos configurar os Jobs que irão ser executados. Cada Job pode ter vários Steps, e é nesses Steps que iremos configurar o que precisamos para chegar a cada resultado.

Então, partindo do principio que se os testes não passarem não precisar rodar mais nada. O Primeiro Job que iremos configurar será o de testes. Neste Job basicamente precisamos de um ambiente Ubuntu e configurar o JDK 1.8 como pré-requisitos, então para esse Job temos esses dois Steps definidos pelo nome "set up JDK 1.8" e "Unit tests" e em "runs-on" definimos o ambiente "Ubuntu-latest". Tendo nosso primeiro Job Ficando dessa forma:
```
# Um workflow é composto de um ou mais Jobs que podem ser executados sequencialmente ou em paralelo
jobs:
  # Este Workflow contém dois Jobs chamados "test" e "build-and-deploy"
  test:
    name: Run Unit Tests
    # O tipo de runner em que o job será executado
    runs-on: ubuntu-latest
    # Steps representam a sequencia de tarefas usando shell runners que serão executadas como parte do Job
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Unit tests
      run: bash ./gradlew test --stacktrace
```

Agora iremos configurar nosso Job principal o "build-and-generate-apk", nesse Job iremos configurar os Steps "set up JDK 1.8", "Install NDK", "Build Debug" e "Upload APK on Github Artifact" onde disponibilizaremos para um Q.A, P.O ou por você mesmo a APK para teste da Feature ou Bug antes de ser mergeado. Tendo nosso Job ficando dessa forma:
```
  build-and-generate-apk:
    # O tipo de runner em que o job será executado
    runs-on: ubuntu-latest
    # Steps representam a sequencia de tarefas usando o shell runners que serão executadas no como parte do Job
    steps:
    - uses: actions/checkout@v1
    # Step para Configurar o JDK
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8       
    #  Step para instalar o NDK
    - name: Install NDK
      run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;20.0.5594570"
    # Step para Buildar o Projeto e gerar a APK
    - name: Build Debug 
      run: ./gradlew assembleDebug
    # Step para salvar a APK como Artifacts
    - name: Upload APK on Build Artifacts
      uses: actions/upload-artifact@v1
      with:
        name: app
        path: app/build/outputs/apk/debug/app-debug.apk 
```
Pronto, agora toda vez que criamos um Pull Request, a Pipeline é iniciada com esse Workflow.
![Pipeline sendo iniciada ](https://miro.medium.com/max/1400/1*cHFmyFFpwWqUh9i3sU3D8w.gif)

E ao final dela temos uma apk para teste gerada e disponível para realizar um teste funcional de uma feature para um testar uma nova funcionalidade.
![Apk Gerada no Github Artifacts](https://miro.medium.com/max/1400/1*S1LteudpMHmkA5zUGpgeVw.png)

Segue o link do arquivo final deste workflow: 
- [Link do arquivo do workflow "Pull Request CI" no Github](https://github.com/denisvieira05/themoviesdb-android-app/blob/master/.github/workflows/pull-request-ci.yml)

Tendo pronto nosso primeiro workflow que tratará nosso primeiro processo de desenvolvimento, o Pull Request. Agora iremos configurar o "Master Release CI" que irá iniciar sempre que ocorrer uma atualização em nossa branch principal, no caso a "master" ou alguma outra branch com iniciais "release".

Então primeiramente iremos criar um novo arquivo .yml de para esse nosso novo workflow, decidi batizar de "master-release-ci.yml". Iniciando configurando o caso em que iremos iniciar esse workflow, adicionamos o código abaixo para que a pipeline só inicie apenas para os casos de quando ocorrer uma atualização nessas branchs.
```
name: Master Release CI

on:
  push:
    branches:
      - 'master'
      - 'release*'
```

Neste workflow na parte configuração dos Jobs e Steps apenas o que difere seria adicionar o Step de "upload artifact to Firebase App Distribution", onde iremos fazer o upload do artefato gerado no build para o Firebase Distribuition, e para isso precisaremos realizar algumas configurações com o Firebase, se caso você ainda não tiver configurado o Firebase em seu projeto Android siga as instruções do [Link da Documentação Oficial](https://firebase.google.com/docs/android/setup?hl=pt-br). 

Com seu aplicativo configurado, iremos entrar em nosso projeto no [console do firebase](https://console.firebase.google.com/u/0/) e acessar a sessão de "App Distribution", lá adicionaremos nosso Android app e ele já estará disponível para enviarmos uma apk e distribuir para os testers e grupos que quisermos. Com isso você já pode fazer upload de seus apks manualmente e distribuir para grupos facilmente, também é possível fazer upload localmente de qualquer apk através do FIREBASE CLI seguindo as [instruções da Documentação Oficial](https://firebase.google.com/docs/app-distribution/android/distribute-cli).

![Firebase Distribution ](
https://www.androidpolice.com/wp-content/uploads/2019/09/image1.png)

Para realizarmos este procedimento automaticamente via Github Actions e em uma pipeline confiável, precisamos gerar algumas credenciais antes. O "FIREBASE APP ID" e "FIREBASE TOKEN". 

O Firebase App ID é facilmente encontrado na Aba de indo em Project Overview -> Selecionar Settings do App Android já Configurado -> E lá embaixo em "Your apps" copiamos a hash do App Id. Ex.:"1:1234567890:android:0a1b2c3d4e5f67890"

![App Settings Firebase](https://docs.monaca.io/images/tutorials/10.png)

Para adquirir o Firebase Token é necessário utilizar o Firebase CLI para realizar algumas actions locais em sua máquina, se você ainda não tem configurado, veja como instalar no seguinte [Link da Documentação Oficial](https://firebase.google.com/docs/cli#install_the_firebase_cli). 

Com o Firebase CLI instalado, iremos configura-lo para utilizar nossa conta do Firebase utilizando o comando: 
```
firebase login:ci
```
Este comando irá abrir uma nova janela de autenticação do google no browser (caso não abra automaticamente, haverá um link que você poderá ver na linha comando), após autenticar com sua conta com sucesso, você verá a mensagem "✔  Success! Use this token to login on a CI server:" e logo abaixo verá uma hash que é o nosso "Firebase Token" e que deverá ser usado em nosso arquivo de workflow do github actions.

![Firebase Token](https://loiane.com/assets/images/2017/angular-travis-firebase-10.jpg)

Como o Firebase App Id e Firebase Token são credenciais é importante que não a deixemos expostas diretamente no código, então iremos configura-lá como "Secrets" do projeto e assim deixando-os dinâmicos e realmente ofuscados. 
Para configurar os "Secrets" de um projeto vá até a parte de Settings do seu repositório e haverá uma aba de "Secrets". Seguindo simplesmente vamos em "Add a new secret" e adicionamos as duas que precisamos, o FIREBASE_APP_ID e FIREBASE_TOKEN, com seus respectivos valores que adquirimos nas etapas anteriores.

![Github Secrets](https://www.edwardthomson.com/blog/images/githubactions/11-addingsecret.png)

Agora com as credenciais que precisamos em mãos e configuradas no projeto, vamos voltar para nosso arquivo de configuração do workflow. Nele agora iremos adicionar nosso único Job com os steps necessários para build e geração da apk, acrescentando o novo Step que servirá para enviar a Apk para o Firebase Distribuition automaticamente. Sendo assim nosso Job fica da exata maneira abaixo: 

```
# Um workflow é composto de um ou mais Jobs que podem ser executados sequencialmente ou em paralelo
jobs:
  # Este Workflow contém um único Job chamado "build-and-deploy"
  build-and-deploy:
    # O tipo de runner em que o job será executado
    runs-on: ubuntu-latest
    # Steps representam a sequencia de tarefas usando o shell runners que serão executadas no como parte do Job
    steps:
    - uses: actions/checkout@v1
    - name: set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8       
    # Step para Instalar o NDK
    - name: Install NDK
      run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;20.0.5594570"
    # Step para Buildar o Projeto e gerar a APK
    - name: build debug apk
      run: ./gradlew assembleDebug
    # Step para Enviar a APK gerada para o Firebase App Distribution
    - name: upload artifact to Firebase App Distribution
      uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1
      with:
        appId: ${{ secrets.FIREBASE_APP_ID }}
        token: ${{ secrets.FIREBASE_TOKEN }}
        groups: testers
        file: app/build/outputs/apk/debug/app-debug.apk

```

Segue o link do arquivo final deste workflow: 
- [Link do arquivo do workflow "Master Release CI" no Github](https://github.com/denisvieira05/themoviesdb-android-app/blob/master/.github/workflows/master-release-ci.yml)


E pronto ! Agora temos o que precisamos para realizar uma entrega integrada, continua e centralizada para todas as partes interessadas apenas seguindo um fluxo de desenvolvimento simples. Por favor comente suas dúvidas, sugestões e compartilhe sua experiência.

Segue o link de um projeto antigo open source que apliquei as configurações deste artigo.
- https://github.com/denisvieira05/themoviesdb-android-app

Próximo Passo:
- Configurar para gerar apk de release com a keystore

Estes workflows ainda são muito simples, mas pretendo criar novos posts adicionando alguns outros jobs ou steps também muito importantes, como linters, sonarqube, relatórios de testes e rodar instrumented tests com Firebase Test Lab. Segue ai e fica ligado !!


Observações: 
- Mesmo enviando o apk automaticamente para o Firebase Distribuition através do Actions ainda é necessário que você vá até o console do Firebase na sessão de App Distribuition e realize a distribuição para o grupo que desejar.
- Por padrão as release notes são preenchidas com a descrição do último commit, mas é possível modifica-lá antes de realizar a sua distribuição.


Referências: 
- https://coletiv.com/blog/android-github-actions-setup/
- https://medium.com/@wkrzywiec/github-actions-for-android-first-approach-f616c24aa0f9