---
# System prepended metadata

title: Как быстро настроить GitHub Actions

---

# Как быстро настроить GitHub Actions

# Как переехали с тревиса (?)

- сложности (релизы, ...)
- больше уникальности

Привет, меня зовут Игорь, я разработчик решений на Tarantool в Mail.ru Group. Я работаю над витринами маркетинга в реальном времени для Мегафона, а также мейнтейню [tarantool/metrics](https://github.com/tarantool/metrics).
Недавно мы переехали с Travic CI на GitHub Actions. Хочу поделиться опытом его настройки в `tarantool/metrics`. Статья будет полезна тем, кто переезжает на Actions с другого CI и тем, кто настраивает его впервые. Далее я расскажу о типах событий в GitHub, о синтаксисе Actions и покажу, как запускать тесты и собирать приложение для доставки пользователям.

## GitHub Actions

### Workflow и job
**Workflow** - это процедура, которая выполняется после возникновения определенного события на GH.
Каждый workflow состоит из одной или нескольких job. **Job** - это набор действий, которые выполняются на одном раннере. 
**Раннер** - это сервер, на котором запускаются команды. Можно воспользоваться гитхабовскими раннерами или поднять свои. Для простых задач гитхабовских раннеров будет достаточно.
**Шаги** job'ы могут быть консольными командами или action'ами.
**Action** - набор команд, который вынесен в отдельный репозиторий и предназначенный для выполнения определенного действия.


### Кратко про типы событий
Обо всех событиях можно прочитать [здесь](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#webhook-events).
Для повседневных задач (запуска тестов, деплоя) используются обычно следующие события:

- `push` - пуш в ветку или мердж в мастер
    - `branches` - вводит ограничения на ветки
    - `tags` - ограничения на теги
- `release` - вызывается при релизе через GitHub
- `pull_request` - создание пулл реквеста
- `workflow_run` - событие о запуске workflow
- `schedule` - вызывается по крону 


### Основные синтаксические конструкции
Workflow состоит из нескольких основных секций (?):
- `name` - имя workflow, оно используется в других workflow и отображается на гитхабе
- `on` - здесь определяются типы событий, которые запускают workflow
- `jobs` - это список job, у каждой из которых есть список "шагов" (они прописываются в `steps`), которые выполняются в рамках этой job'ы
    - `runs-on` - определяет ОС, на которой будет выполняться шаги job'ы
    - `steps` - набор шагов, которые выполняются в рамках этой job'ы

```yaml=
name: Workflow name
on:
  event_name:
    event_params

jobs:
  job_name:
    runs-on: ubuntu-latest
    #job_params
    steps:
    - name: Step 1 name
      uses: actions/checkout@v2
    - name: Step 2 name
      run: some shell commands
    ...
```

### Действия на шаге job'ы

Шагом job'ы может быть запуск action'a или консольной команды.

`run` - запускает консольную команду. Перед вызовом `run` необходимо убедиться, что установлены необходимые пакеты и настроено окружение.

`uses` - запускает action. Action'ы можно написать самостоятельно или воспользоваться уже имеющимися.

Первым шагом каждой job'ы обычно является `actions/checkout`. Этот action копирует код из репозитория на раннер.

### Настройка окружения
~~Настройка окружения для разных языков производится с помощью конструкции `uses`.~~ Для большинства популярных языков уже написаны готовые экшены которые настраивают окружение, и не нужно каждый раз прописывать шаги по настройке и установке пакетов. Необходимые параметры для настройки (они прописываются с помощью конструкции `with`) можно посмотреть в соответствующем GitHub-репозитории.

Вот так, например, можно настроить окружение для Go:
```yaml=
jobs:
  job_name:
    steps:
      - name: Setup go
        uses: actions/setup-go@v2
        with:
          go-version: '1.15'
```

### Как подключить GH Actions

1. Создаем в репозитории папку `.github/workflows/`.
2. В папке создаем файл с расширением `.yml`.
3. Описываем в этом файле workflow
4. После этого workflow запустится автоматически при возникновении нужного события

## Тесты

### Матричное тестирование
Для тестов часто требуется использовать различные версии языков, приложений или ос. Actions позволяет составить матрицу для тестов.
Вот так, например, мы настроили матричные тесты для разных версии Tarantool и Cartridge:
Файл `.github/workflows/tests.yaml`:
```yaml=
name: Tests
on:
  push:
    branches: 
     - '*'
     # вызывается при пуше коммитов в любую ветку
test:
  strategy:
    matrix:
      tarantool:
        - "1.10.8"
        ...
        - "2.7.0"
      cartridge: [ "", "1.2.0", "2.1.2" ]
    runs-on: ubuntu-latest
  container:
    image: tarantool/tarantool:${{ matrix.tarantool }}
    env:
      CARTRIDGE_VERSION: ${{ matrix.cartridge }}
  steps:
    ...
```
`matrix` также позволяет добавлять и исключать определенные тесты из матрицы. Например, если бы мы хотели пропускать тест для связки Tarantool 1.10 и Cartridge 2.1.2, и дополнительно проверять работу метрик на версии Tarantool 2.7 и Cartridge 2.4.0, следовало дописать:
```yaml=
matrix:
  tarantool:
    - "1.10.8"
    ...
    - "2.7.0"
  cartridge: [ "", "1.2.0", "2.1.2" ]
  include:
    - tarantool: "2.7.0"
      cartridge: "2.4.0"
  exclude:
    - tarantool: "1.10.8"
      cartridge: "2.1.2"
```

На каждую пару `matrix.tarantool` и `matrix.cartridge` создается одна job'a, а также добавляются те, которые указаны в `include` и исключаются указанные в `exclude`. В итоге получаем `7х3 + 1 - 1 = 21` job. Вместо `tarantool` и `cartridge` можно использовать вообще любые параметры и обращаться к ним в любом месте тестов с помощью конструкции `${{ matrix.tarantool }}`

### Тело теста (wtf?)

Сюда можно перенести команды, которые выполнялись в другом CI, например, Тревисе. 

```yaml=
# копируем код на раннер
- uses: actions/checkout@v2

# устанавливаем зависимости
- name: deps
run: apk add --update alpine-sdk make curl git cmake gcc

# запускаем команды из мейкфайла
- name: lint
run: make lint

- name: test
run: make test_with_coverage_report
```

### Зависимости между тестами
По умолчанию тесты отменяются, если хотя бы один из них упал. Чтобы изменить это поведение, можно прописать в `strategy` параметр `fail-fast: false`. (это про матрицу, остальное все равно упадет)
Для запуска определенного теста после предыдущего можно прописать `needs: job_name` в параметрах job'ы. Это будет полезно, например, для разграничения запуска "тяжелых" тестов.
В гитхабе можно посмотреть на визуализированные зависимости шагов:

![](https://i.imgur.com/rIJmBB7.png)

## Деплой

В `tarantool/metrics` два типа релизов: релиз latest-версии по пушу в мастер и релиз тегированной версии по пушу тегов. Мы не хотим, чтобы деплои запускались после упавших тестов, поэтому в релизные джобы добавляем проверку на то, что предыдущий workflow завершился успешно.
~~Следует также иметь в виду, что при релизах через UI гитхаба, создаются только неаннотированные теги, с которыми есть **проблемы** (вспомнить бы какие)~~
```yaml=
on:
  workflow_run:
    workflows: ["Tests"]
    branches: [master]
    types:
      - completed 
      # Запускаем только после завершения тестов в мастере
  push:
    tags:
       - '*'
       # И по пушу тегов
...
jobs:
  push-scm-rockspec:
    runs-on: [ ubuntu-latest ]
    # Запускаем только если предыдущий workflow был успешным
    if: github.ref == 'refs/heads/master' && github.event.workflow_run.conclusion == 'success'
    steps:
    # заливаем рокспеку на рок-сервер
    ...
  push-tagged-rockspec:
    runs-on: [ ubuntu-latest ]
    if: startsWith(github.ref, 'refs/tags')
    # Запускаем когда запушили теги
    steps:
      ...
      - name: Set env
        run: echo "GIT_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
        # получаем название тега
      ...
      # заливаем рокспеку на рок-сервер
```

Для деплоя пакетов также можно использовать матрицы, вот так, например, мы выбираем разные дистрибутивы ОС:

```yaml=
jobs:
  deploy:
    strategy:
      matrix:
        os:
          - name: el
            dist: 7
          ...
          - name: fedora
            dist: 29
        repository:
          - "1_10"
          ...
          - "2_4"
    runs-on: ubuntu-latest
    env:
      PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
      OS: ${{ matrix.os.name }}
      DIST: ${{ matrix.os.dist }}
```

## Дополнительно

Вот так можно добавить в README бейдж о прохождении тестов (в ссылке `Tests` - название нужного workflow):

![Build Status](https://github.com/tarantool/metrics/workflows/Tests/badge.svg?branch=master)
```markdown 
![Build Status](https://github.com/tarantool/metrics/workflows/Tests/badge.svg?branch=master)
```

Еще мы настроили автоматическое [закрытие тикетов по крону](https://github.com/actions/stale). Посмотреть можно [здесь](https://github.com/tarantool/metrics/blob/master/.github/workflows/stale.yml).

## Итоги
В статье были рассмотрены основные термины и типы событий в GitHub, синтаксис Actions и показано, как мы описывали workflow для тестов сборки приложение для доставки пользователям.

## Ссылки

- Скачать Tarantool можно на официальном сайте
- Получить помощь можно в Telegram-чате.
- [Введение в GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/introduction-to-github-actions)
- [Синтаксис workflow](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions)
- [События в GitHub](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#webhook-events)