Como configurar Fastlane para Firebase App Distribution (Android e iOS)
Tags: fastlane, cicd, firebase
Published: 2021-02-16 17:18:15

Neste post iremos mostrar como configurar o Fastlane para distribuir apps Android e iOS através do Firebase App Distribution. Este artigo é um complemento [deste post aqui](https://dev.to/yagoliveira92/flutter-fastlane-github-actions-45pf). Iremos tratar a execução das *lanes* em um **ambiente de CI/CD**. É bem similar à execução direto no seu computador, mas em determinados momentos pode ficar confuso. As referências estão no final do artigo.

- [Configuração no iOS](#configuração-no-ios)
  - [Instalar Fastlane](#instalar-fastlane)
  - [Escrevendo o Fastfile (iOS)](#escrevendo-o-fastfile-ios)
- [Configuração no Android](#configuração-no-android)
  - [Instalação do Fastlane](#instalação-do-fastlane)
  - [Escrevendo o Fastfile (Android)](#escrevendo-o-fastfile-android)
- [Referências](#referências)


### Configuração no iOS

#### Instalar Fastlane
Primeiro você deve [instalar o fastlane no projeto do iOS](https://docs.fastlane.tools/getting-started/ios/setup/) fora do Ambiente de CI. A melhor forma é usar o **Bundler**. 

* Instale o Ruby no seu Mac. A versão do Ruby utilizada pelo Fastlane tem que estar entre a 2.4 e a 2.7;
* Após, crie um arquivo chamado `Gemfile` sem extensão. É nesse arquivo que vamos colocar as `gems` (o Fastlane é uma Gem).
* Colocado a gem do Fastlane, instalar usando o comando `bundle` na sua pasta raiz.

```ruby
source "https://rubygems.org"

gem "fastlane", "~> 2.173"

```

Feito o `bundle` e instalando o Fastlane executamos `bundle exec fastlane init` para iniciar o Fastlane no projeto. Esse comando criará os arquivos do Fastlane dentro do projeto. Algumas perguntas podem ser feitas durante o comando, responda conforme for solicitado (Ex.: ID do Projeto).

Pronto, iniciamos o Fastlane.

#### Escrevendo o Fastfile (iOS)

O `Fastfile` é o arquivo onde será colocado toda a sua *pipeline*. É nele que configuraremos as *lanes* que serão executadas para gerenciar os certificados, os perfis e enviar para o Firebase.

Primeiro, vamos definir a *lane* que será executada dentro do CI/CD. Ela que será responsável por chamar todas as demais lanes necessárias.

```ruby
platform :ios do
    desc "Release app for App Distribution in Firebase"
    lane :send_firebase do

        build_app(
          scheme: 'Runner',
          archive_path: './build/Runner.xarchive',
          export_method: 'ad-hoc',
          output_directory: './build/Runner'
        )

        firebase_app_distribution(
          app: <FIREBASE_APP_ID>,
          groups: 'grupo-ios',
          firebase_cli_token: <FIREBASE_APP_DISTRIBUTION_TOKEN>,
          release_notes_file: 'fastlane/release-notes.txt',
          ipa_path: './build/Runner/Runner.ipa'
        )
    end
end
```

Aqui podemos definir qual a plataforma que será executada (nesse caso, o iOS), uma descrição (facilita na hora de você ver o que está sendo executado), o nome da Lane e duas *actions*: o `build_app` e o `firebase_app_distribution`. 

Como deve ter notado precisaremos de um `firebase_cli_token` para poder enviar o build para o App Distribution. Você pode criar esse token seguindo [esse tutorial aqui](https://firebase.google.com/docs/cli#cli-ci-systems).

*Nosso projeto é feito em Flutter, a configuração do `build_app` ficou conforme gerado por ele. Modifique-o conforme sua necessidade.*

Agora iremos realizar os outros passos para poder assinar o app e colocar os dispositivos cadastrados dentro do [Perfil de Provisionamento](https://docs.microsoft.com/pt-br/mem/intune/apps/app-provisioning-profile-ios). 

Como estamos em um ambiente de CI precisamos criar uma `keychain` para poder instalar todos os certificados. Para isso executamos:

```ruby
before_all do
  create_keychain(
    name: <KEYCHAIN_NAME>,
    password: <KEYCHAIN_PASSWORD>,
    default_keychain: true,
    unlock: true,
    timeout: 3600,
  )
end
```
A action `before_all` irá forçar a criação da Keychain antes de quaisquer outra action ser executada, inclusive da sua lane. 
Feito isso, podemos criar nossa Pipeline de forma que:
- Atualizaremos os dispositivos
- Criaremos ou faremos download dos certificados
- Distribuiremos o app assinado

A atualização dos dispositivos é feita com a action [register_devices](https://docs.fastlane.tools/actions/register_devices/):

```ruby
lane :update_devices do
  register_devices(
    devices_file: './fastlane/devices.txt'
  )
  profiles
end
```
Como podem ver, assim que é feito o registro dos dispositivos já chamamos a lane `profiles`. É nela que vamos criar (ou baixar) os certificados da Apple.

É recomendável que **o próprio Fastlane crie e gerencie os certificados em ambiente de CI**, porque ele fará todas as ligações entre o certificados, os dispositívos e os perfis de provisionamento. Para isso utilizaremos a action [Match](https://docs.fastlane.tools/actions/match/). 

Para iniciar o Match deve-se executar `bundle exec fastlane match init` **fora do ambiente de CI** e seguir as instruções que constam na [documentação](https://docs.fastlane.tools/actions/match/), escolhendo a melhor forma de armazenar de forma segura esses certificados. São dados três opções: Repositório GIT, Google Cloud ou Amazon S3.
As informações de acesso dos certificados devem ficar armazenadas no `Matchfile` criado no comando acima.

Iniciando o Match, vamos adicionar a criação (ou o download) dos perfis no `Fastfile`

```ruby
lane :profiles do |options|
  match(
    app_identifier: <APP_IDENTIFIER>,
    type: 'development',
    readonly: false,
    force_for_new_devices: true,
    force:options[:force],
    keychain_name: <KEYCHAIN_NAME>,
    keychain_password: <KEYCHAIN_PASSWORD>
  )

  match(
    app_identifier: <APP_IDENTIFIER>,
    type: 'adhoc',
    readonly: false,
    force_for_new_devices: true,
    force:options[:force],
    keychain_name: <KEYCHAIN_NAME>,
    keychain_password: <KEYCHAIN_PASSWORD>
  )
end
```
Cada Match vai criar o certificado e perfil dos tipos *development* e *adhoc*. Usaremos o tipo *adhoc* para o build que será enviado ao App Distribution.

Certo, estamos quase lá. Agora vamos realizar a etapa onde o Fastlane irá acessar e criar os certificados dentro da AppStore Connect. Para isso iremos criar uma API Key dentro da AppStore Connect. Siga [esse tutorial](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) para poder criar a key

Com a Key criada vamos inserir no início da nossa Lane:

```ruby
app_store_connect_api_key(
  key_id: <key_id>,
  issuer_id: <issuer_id>,
  key_filepath: <caminho_para_chave>,
  in_house: false,
)
```

Ufa! Depois disso tudo teremos nosso Fastfile completo:

```ruby
default_platform(:ios)

platform :ios do
    before_all do
      create_keychain(
        name: <KEYCHAIN_NAME>,
        password: <KEYCHAIN_PASSWORD>,
        default_keychain: true,
        unlock: true,
        timeout: 3600,
      )
    end

    lane :update_devices do
        register_devices(
          devices_file: './fastlane/devices.txt'
        )
        profiles
    end

    lane :profiles do |options|
        match(
          app_identifier: <APP_IDENTIFIER>,
          type: 'development',
          readonly: false,
          force_for_new_devices: true,
          force:options[:force],
          keychain_name: <KEYCHAIN_NAME>,
          keychain_password: <KEYCHAIN_PASSWORD>
        )

        match(
          app_identifier: <APP_IDENTIFIER>,
          type: 'adhoc',
          readonly: false,
          force_for_new_devices: true,
          force:options[:force],
          keychain_name: <KEYCHAIN_NAME>,
          keychain_password: <KEYCHAIN_PASSWORD>
        )
    end

    desc "Release app for App Distribution in Firebase"
    lane :send_firebase do
      
        app_store_connect_api_key(
          key_id: <KEY_ID>,
          issuer_id: <ISSUER_ID>,
          key_filepath: <KEY_PATH>,
          in_house: false,
        )

        update_devices

        build_app(
          scheme: 'Runner',
          archive_path: './build/Runner.xarchive',
          export_method: 'ad-hoc',
          output_directory: './build/Runner'
        )

        firebase_app_distribution(
          app: <FIREBASE_APP_ID>,
          groups: 'grupo-ios',
          firebase_cli_token: <FIREBASE_APP_DISTRIBUTION_TOKEN>,
          release_notes_file: 'fastlane/release-notes.txt',
          ipa_path: './build/Runner/Runner.ipa'
        )
    end
end

```

Podemos ver todo nosso Pipeline dentro da Lane `send_firebase`: 
- Criação da Keychain 
- Conexão com a AppStore 
- Atualização dos Dispositivos 
- Criação / Download e Instalação dos Certificados e Perfis de Provisionamento 
- Build 
- Entrega para o App Distribution.

### Configuração no Android

#### Instalação do Fastlane

Primeiro você deve [instalar o fastlane no projeto do Android](https://docs.fastlane.tools/getting-started/android/setup/) fora do Ambiente de CI. A melhor forma é usar o **Bundler**. 

* Instale o Ruby no seu PC. A versão do Ruby utilizada pelo Fastlane tem que estar entre a 2.4 e a 2.7;
* Após, crie um arquivo chamado `Gemfile` sem extensão. É nesse arquivo que vamos colocar as `gems` (o Fastlane é uma Gem).
* Colocado a gem do Fastlane, instalar usando o comando `bundle` na sua pasta raiz.

```ruby
source "https://rubygems.org"

gem "fastlane", "~> 2.173"

```

Feito o `bundle` e instalando o Fastlane damos `bundle exec fastlane init` para iniciar o Fastlane no projeto. Esse comando criará os arquivos do Fastlane dentro do projeto. Algumas perguntas podem ser feitas durante o comando, responda conforme for solicitado (Ex.: ID do Projeto).

Pronto, iniciamos o Fastlane.

#### Escrevendo o Fastfile (Android)

Diferente do [iOS](#escrevendo-o-fastfile-ios), no Android não precisamos de nenhum certificado ou coisas do aspecto para poder enviar nosso APK para o Firebase Distribution. Todo nosso Fastfile compreende:

```ruby
default_platform(:android)

platform :android do
    desc "Release app for App Distribution in Firebase"
    lane :homologacao_firebase do
        firebase_app_distribution(
            app: <FIREBASE_APP_ID_HOMOLOGACAO>,
            groups: "grupo-android",
            firebase_cli_token: <FIREBASE_APP_DISTRIBUTION_TOKEN>,
            release_notes_file: "fastlane/release-notes.txt",
            apk_path: ".path/to/app.apk"
        )
    end
end
```
Como deve ter notado precisaremos de um `firebase_cli_token` para poder enviar o build para o App Distribution. Você pode criar esse token seguindo [esse tutorial aqui](https://firebase.google.com/docs/cli#cli-ci-systems).

Pronto. Isso é tudo o que precisa para enviar o App Android ao Firebase App Distribution.

Ficamos por aqui. Quaisquer dúvida, sugestão ou erro que tenha encontrado terei total prazer em ajudar e corrigir.

### Referências

Distribuir apps para Android para testadores com o fastlane, GOOGLE. Disponível em <https://firebase.google.com/docs/app-distribution/android/distribute-fastlane#google-acc-fastlane>;

Material de consulta sobre a CLI do Firebase, GOOGLE. Disponível em <https://firebase.google.com/docs/cli#cli-ci-systems>

Getting started with fastlane for iOS, FASTLANE DOCS. Disponível em <https://docs.fastlane.tools/getting-started/ios/setup/>

Getting started with fastlane for Android, FASTLANE DOCS. Disponível em <https://docs.fastlane.tools/getting-started/android/setup/>


<span>Photo by <a href="https://unsplash.com/@johntorcasio?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">John Torcasio</a> on <a href="https://unsplash.com/s/photos/american-football?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>