Как стать автором
Обновить

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

Время на прочтение 4 мин
Количество просмотров 11K
Привет!

С появлением 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.

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

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн