Поднимаем CI на github для Android за день

от автора

Привет!

С появлением Github Actions проявил инициативу и интегрировал простенький (но вполне эффективный) CI/CD в наш небольшой, но уже как 2 года живой проект Flowwow.

Зачем?

Возможно, есть такие разработчики, которые не совершают ошибок, но вот я — не из таких, поэтому изредка, но случаются вот такие небольшие всплески крашей и приходится в срочном порядке выпускать новую версию с правкой либо откатом к предыдущей версии. Но те часы-дни, в которые пользователи натыкаются на вылеты приложения, без следов не остаются как у клиентов, так и в настроении ответственного разработчика.

Как минимизировать факапы на продакшене, расскажу ниже.

Отчего же лично у меня бывают такие факапы?

  1. Ненадежный участок кода
  2. Завезли какую-то библиотеку и она ситуативно крашит
  3. Обновили какую-то библиотеку (обычно, это аналитика) на нестабильную версию

С 1 пунктом нам помогут code review, Unit тесты, статический анализ кода, UI тесты, ручное тестирование.

Со 2-3 пунктами — только UI тесты и ручное тестирование.

Остается только это автоматизировать. На этом этапе выбор пал на тогда еще только появившийся Github Actions, благо и код проекта находится на Github. Сразу скажу, для free аккаунта github дается бесплатных 2,000 Action минут в месяц.

C чего начать?

Здесь полно готовых примеров для различных языков и фреймворков. Настраивается эта штука через конфигурационный файлик YAML, который находится в репозитории проекта.

Минимальный пример для Android:

name: Android CI  on: [push]  jobs:   build:     runs-on: ubuntu-latest     steps:       - uses: actions/checkout@v1       - name: set up JDK 1.8         uses: actions/setup-java@v1         with:           java-version: 1.8       - name: Build with Gradle         run: ./gradlew assembleDebug 

Описание: на каждый push в любую ветку запускается задача (job) на виртуальной машине github с ОС ubuntu. Шаги задачи: checkout нашего кода, настройка jdk, запуск gradle задачи для сборки.

В случае неуспешного прохождения какого либо шага увидим такую картину

там же можно посмотреть логи.

Удобно, что при Pull Request нам сразу покажут, что наша проверочная последовательность зафейлилась

А если у вас есть интеграция github со Slack, то еще и так

А теперь по пунктам

1. Unit tests

Вы написали Unit тесты используя junit, mockito, etc.

Теперь ваши тесты включаются в последовательность проверки добавлением соответствующей gradle задачи.

- name: Run some unit tests   run: ./gradlew testStageDebugUnitTest 

2. Статический анализ кода

Вы можете использовать простые линтеры (detekt — для kotlin, pmd — для java).
Или же более сложный вариант — sonarqube.

В случае простых линтеров (например, у нас и java, и kotlin):

task("checkAll") {     group "Verify"     description "Runs all static checks on the build"     dependsOn "pmd", "detekt" } 

- name: Run some unit tests   run: ./gradlew checkAll 

в случае sonarqube — подробнее про настройку — здесь

- uses: actions/checkout@v1 - name: SonarCloud Scan    run: ./gradlew jacocoUnitTestReport sonarqube -Dsonar.login=${{ secrets.SONAR_TOKEN }} --stacktrace    env:      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Link to SonarCloud Report    run: echo "https://sonarcloud.io/dashboard?id=.." 

3. UI тесты

Написание UI теста — плод вашей фантазии, мой подход — один «Smoke» тест, имитирующий стандартные действия пользователя в приложении — зайти, выбрать товар, сделать заказ, отследить заказ. Вы можете использовать UIAutomator, Espresso, Kaspresso.

Для запуска здесь тоже 2 варианта — эмулятор на виртуальной машине github или облачные сервисы, такие как Firebase Test Lab
Для использования эмулятора внутри github есть готовые реализации: раз и два.

В случае с Firebase Test Lab, необходимо работать с Google Cloud Platform через gcloud CLI

- name: prepare gcloud   uses: GoogleCloudPlatform/github-actions/setup-gcloud@master   with:     version: latest     service_account_email:  ${{ secrets.SA_EMAIL }}     service_account_key: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - name: gcloud Set up project   run: |     gcloud config set project ${{ secrets.PROJECT_ID }} - name: Assemble apks for smoke test   run: ./gradlew Smoke - name: Run tests in test lab   run: |      gcloud firebase test android run \        --app app/build/outputs/apk/production/debug/app.apk \        --test app/build/outputs/apk/androidTest/production/debug/appTest.apk \        --device model=Nexus6P,version=25,orientation=portrait,locale=en_US \        --device model=athene,version=23,orientation=portrait,locale=en_US \        --device model=sailfish,version=26,orientation=portrait,locale=en_US 

Чтобы это работало, необходимо создать проект в Firebase, создать в Google Cloud Сonsole сервисный аккаунт с админ правами и полученный ключ json загрузить в base64 в github secrets для авторизации в gcloud.

Общий конфиг в моем случае выглядел так. Задача запускается по событию PR в master

name: Android CI  on:   pull_request:     branches:       - 'master'  jobs:   build:      runs-on: ubuntu-latest      steps:       - uses: actions/checkout@v1       - name: set up JDK 1.8         uses: actions/setup-java@v1         with:           java-version: 1.8       - name: Run static checks         run: ./gradlew checkAll       - name: Run some unit tests         run: ./gradlew testStageDebugUnitTest        - name: prepare gcloud         uses: GoogleCloudPlatform/github-actions/setup-gcloud@master         with:           version: latest           service_account_email:  ${{ secrets.SA_EMAIL }}           service_account_key: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}       - name: gcloud Set up project         run: |           gcloud config set project ${{ secrets.PROJECT_ID }}       - name: Assemble apks for smoke test         run: ./gradlew Smoke       - name: Run tests in test lab         run: |           gcloud firebase test android run \             --app app/build/outputs/apk/production/debug/app.apk \             --test app/build/outputs/apk/androidTest/production/debug/appTest.apk \             --device model=Nexus6P,version=25,orientation=portrait,locale=en_US \             --device model=athene,version=23,orientation=portrait,locale=en_US \             --device model=sailfish,version=26,orientation=portrait,locale=en_US 

Вроде, просто. Эффективность зависит от написанных тестов и выбранных правил код-анализа. Можно писать несколько независимых задач (job) для их параллельного запуска. В моем случае все проходит последовательно. Процесс проверки занимает примерно 15 минут на нашем проекте (виртуальная машина github 2-core CPU, 7 GB RAM, 14 GB of SSD), но на самом деле не критично, тк пока «кодревьюишь» глазами, подъезжают результаты и этих тестов.

Больше всего выручают UI тесты — бывает, что во время их прохождения крашится аналитика после обновления библиотеки и ты просто понимаешь, что не стоит её обновлять.

Через gcloud также можно доставлять билды в Firebase App Distribution, релизить в Google Play, etc.

Много полезных примеров можно посмотреть тут и тут.
Надеюсь, статья кому-нибудь будет полезна. Удачи и меньше крашей на продакшене!

ссылка на оригинал статьи https://habr.com/ru/post/488134/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *