Se você ainda não conhece as motivações deste projeto, leia o primeiro artigo aqui.
Olá pessoal! No último post configuramos as chaves de assinatura do nosso app de maneira segura, garantindo que os dados não estejam expostos no repositório e viabilizando que o CI consiga buildar e assinar os APKs. No post de hoje, vamos fazer a configuração do projeto no Firebase.
O primeiro passo é criarmos os projetos em nossa conta Google. Por que escrevi no plural? Porque faz todo sentido separarmos os ambientes, tendo um projeto para desenvolvimento (onde poderemos testar das mais diversas formas desde formato de dados, notificações, configurações e outros) e outro para produção, podendo assim realizar diversos experimentos sem impactar o app já publicado.
No meu caso, criei dois projetos no painel do Firebase, dando o nome de Social App Development e Social App Production.
Com os dois projetos criados, vamos configurar o nosso app. Primeiramente o build.gradle principal do projeto:
// build.gradle
buildscript {
ext.versions = [
'kotlin': '1.2.30',
'supportLibrary': '27.1.0',
'googleServices': '12.0.0'
]
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath 'com.google.gms:google-services:3.2.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
configurations.all {
resolutionStrategy {
eachDependency { details ->
if (details.requested.group == 'com.android.support'
&& details.requested.name != 'multidex'
&& details.requested.name != 'multidex-instrumentation') {
details.useVersion versions.supportLibrary
}
}
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Fizemos algumas boas mudanças no arquivo. Primeiramente criei um bloco para padronizar a configuração de versões que se repetem em múltiplos locais do projeto – a versão do Kotlin, da Support Library e das bibliotecas Google (Firebase e Play Services). Em seguida, adicionamos o plugin google-services em sua última versão, que se encarregará de configurar o nosso projeto.
Nesse arquivo, também, coloquei uma pequena configuração para forçar todas as Support Libraries a compartilharem da mesma versão. Por quê? Muitas vezes, ao adicionar uma outra dependência – por exemplo, o próprio Firebase – ele depende de uma versão de alguma Support Library mais antiga que a utilizada no projeto. Uma das formas de se resolver isso é dando um exclude na dependência e adicionando manualmente a versão mais recente. Particularmente não gosto dessa abordagem por poluir muito o bloco de dependências. Dessa forma, temos esse bloco de configuração que vai garantir que as Support Libraries estejam todas na versão 27.1.0 (exceto as bibliotecas de Multidex, que possuem um versionamento próprio).
Em seguida, vamos configurar o build.gradle do projeto app.
// app/build.gradle
android {
...
buildTypes {
debug {
applicationIdSuffix '.dev'
signingConfig signingConfigs.debug
}
...
}
}
dependencies {
implementation "com.android.support:appcompat-v7:$versions.supportLibrary"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$versions.kotlin"
implementation "com.google.firebase:firebase-core:$versions.googleServices"
...
}
apply plugin: 'com.google.gms.google-services'
Aqui, primeiramente, adicionamos um sufixo ao applicationId para o build type debug. Isso vai facilitar mantermos tanto a versão de produção quanto a de desenvolvimento no mesmo dispositivo. Em seguida, configurei as dependências para utilizar as versões definidas globalmente e adicionei a dependência firebase-core, que já nos adiciona o analytics, por exemplo. Por fim, aplicamos o plugin google-services no final do arquivo, conforme a documentação.
Bom, feito isso, vamos configurar os aplicativos no painel do Firebase.
Aqui, vamos inserir o nome do pacote (net.rafaeltoledo.social para o app de produção, net.rafaeltoledo.social.dev para o app de desenvolvimento), opcionalmente um apelido e o hash SHA-1 de assinatura. Podemos facilmente conseguir esse hash através da task signingReport do Gradle. Para executá-la, certifique-se de que a chave de produção esteja descriptografada e que as variáveis de ambiente com as senhas estejam exportadas.
Com o resultado dessa task, copie o hash SHA-1 de debug e release e coloque em cada um dos projetos. Com isso, teremos dois arquivos google-services.json, um para cada projeto.
Pronto, agora é só commitar e seguir o jogo? Não necessariamente…
Quando você está desenvolvendo um projeto, idealmente você não deveria deixar estes arquivos no seu controle de versão. Primeiramente, porque alguém poderia criar um app com o mesmo applicationId e utilizá-lo para acessar o seu projeto. Segundo, que poderia com isso também comprometer a sua cota de serviços e, caso você utilize um dos serviços pagos do Firebase, ocasionar em gastos para você. Portanto, trate o arquivo google-services.json também como uma chave do app.
Para resolver esse problema, primeiramente coloquei o arquivo no .gitignore, para que ele não seja adicionado ao Git. Em seguida, vamos colocar o conteúdo do arquivo no CI, para que sejam colocados no projeto na hora do build. Farei isso colocando o conteúdo em uma variável de ambiente e, através do comando echo, mandando o conteúdo pra um arquivo.
O conteúdo desse arquivo é simplesmente um JSON, então a minha recomendação é, antes de criar as variáveis, utilizar algum serviço de minificação (ou minify) para remover espaços e quebras de linha desnecessárias.
Por fim, basta adicionar uma etapa no arquivo de configuração do CI para obter o valor da variável e jogar no arquivo corretamente dentro de cada uma das pastas (app/src/release e app/src/debug).
- run:
name: Setup Google Services JSON
command: |
mkdir -p app/src/debug/ && touch app/src/debug/google-services.json
echo "${JSON_FIREBASE_DEVELOPMENT}" >> "app/src/debug/google-services.json"
mkdir -p app/src/release/ && touch app/src/release/google-services.json
echo "${JSON_FIREBASE_RELEASE}" >> "app/src/release/google-services.json"
E é isso. Configuramos corretamente o projeto e mantivemos nossos segredos em segurança :)
O código já foi mergeado no repositório após um PR e a build no CI :)