GitHub Actions

Собираем пример CI/CD пайплайна в Github.

Время чтения: 9 мин

Как понять

Секция статьи "Как понять"

Существует большое количество инструментов для сборки кода и публикации его для пользователей. У каждого свои особенности и тонкости использования. Но есть и конкурирующие между собой инструменты. Среди конкурирующих платформ стоит говорить о GitLab CI/CD, Bitbucket Pipelines, Jenkins, Netlify, JetBrains TeamCity, GitHub Actions и прочие.

GitHub Actions — это инструмент для автоматизации рутины в области разработки программного обеспечения, автоматического тестирования, сборки и публикации приложений, который глубоко интегрирован в экосистему инструментов GitHub.

Например, нужна проверка кодовой базы проекта на Node.js или контента на соответствие правилам линтера EditorConfig, установленного в проекте как дев-зависимость. Разработчики, открывая проект локально с помощью IDE или запуская специальный скрипт, смогут проверить код, поскольку в проекте уже присутствует файл .editorconfig. А что делать в том случае, если разработчик открыл файл в блокноте или в веб-интерфейсе? Вот тут можно использовать экшен, который, например, будет срабатывать всякий раз, когда файлы попадают в репозиторий с помощью git push:

        
          
          name: EditorConfigon:  pushjobs:  lint:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v2      - uses: actions/setup-node@v2        with:          node-version: '14'      - run: npm ci      - run: npm run editorconfig
          name: EditorConfig

on:
  push

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
      - run: npm ci
      - run: npm run editorconfig

        
        
          
        
      

Файл можно назвать произвольно, указать расширение .yml и поместить в папку .github/workflows. Экшен автоматически запустится и выдаст результаты проверки на отдельной вкладке «Actions».

Как пользоваться

Секция статьи "Как пользоваться"

GitHub Actions — средство автоматизации, конфигурация которого описывается в формате YAML.

В первой секции name устанавливается название экшена, которое будет использоваться в интерфейсе GitHub. Для названия можно использовать как английский язык, так и, например, русский. Вообще эта секция не является обязательной. Давайте создадим экшен для тестирования приложения на Node.js и назовём его так:

        
          
          name: Тестирование
          name: Тестирование

        
        
          
        
      

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

        
          
          on:  push:    branches: [ main ]  pull_request:    branches: [ main ]
          on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

        
        
          
        
      

В качестве событий можно использовать разные события в репозитории в формате on.<event_name>.types. Подробно перечень событий и их типы описаны в соответствующем разделе официальной документации. Самые распространённые:

  • push;
  • pull_request;
  • schedule.

В случае с push и pull_request можно указать ветку или тег, тем самым отфильтровав нужное событие для запуска экшена (формат — on.<push|pull_request>.<branches|tags>). Есть возможность исключить какие-то ветки, а для всех остальных запускать экшен.

В секции schedule используется формат записи времени, аналогичный cron:

        
          
          on:  schedule:    - cron: '20/15 9-18 1,10,20 * *'
          on:
  schedule:
    - cron: '20/15 9-18 1,10,20 * *'

        
        
          
        
      

Позиции в строке очень важны: cron: '* * * * *'

  1. * минуты (0—59)
  2. * часы (0—23)
  3. * номер дня в месяце (1—31)
  4. * номер месяца или сокращение на английском языке (1—12 или JAN—DEC)
  5. * номер дня недели или сокращение на английском языке (0—6 или SUN—SAT)

Если значений несколько, они могут быть перечислены через запятую ,. Знак дефиса - можно использовать для указания диапазона. Если нужно начать, например, с какой-то определённой минуты, часа, дня, месяца, дня недели и запускать что-то периодически можно использовать символ /. Например, запись '20/15 9-18 1,10,20 * *' означает, что событие будет запускаться 1-го, 10-го и 20-го числа каждого месяца с 9 часов утра до 18 часов вечера на 20-й, 35-й и 50-й минуте каждого часа.

Секция jobs (джобы) предназначена для описания действий, которые должен произвести экшен.

        
          
          jobs:
          jobs:

        
        
          
        
      

Каждый джоб имеет свой вывод, которым можно управлять. Вывод той или иной информации можно использовать в зависимых джобах, которые указываются в секции needs. Вот пример зависимых джобов:

        
          
          jobs:  job1:  job2:    needs: job1  job3:    needs: [job1, job2]
          jobs:
  job1:
  job2:
    needs: job1
  job3:
    needs: [job1, job2]

        
        
          
        
      

Джоб job2 зависит от job1, а джоб job3 ещё и от job2. Название джоба — это фактически название последовательности действий. Название может содержать буквы латинского алфавита, цифры и дефисы. В джобе необходимо прописать, на какой операционной системе экшен должен запускаться в секции runs-on:

        
          
          first-test:  runs-on: ubuntu-latest
          first-test:
  runs-on: ubuntu-latest

        
        
          
        
      

Доступны следующие операционные системы:

  • Windows Server 2019 — для этого нужно использовать windows-latest или windows-2019;
  • Windows Server 2016 — для этого нужно использовать windows-2016;
  • Ubuntu 20.04 — для этого нужно использовать ubuntu-latest или ubuntu-20.04;
  • Ubuntu 18.04 — для этого нужно использовать ubuntu-18.04;
  • macOS Big Sur 11 — для этого нужно использовать macos-11;
  • macOS Catalina 10.15 — для этого нужно использовать macos-latest или macos-10.15.

Для проведения тестирования или сборки приложения иногда важно использовать разные версии платформ или операционных систем. Например, в случае с Node.js можно использовать несколько версий для тестирования веб-приложения, библиотеки или фреймворка. В экшенах это возможно и реализовано как стратегия запуска на списке (матрице) образов. Слово «образ» употреблено не случайно, поскольку используется Docker-образ для запуска той или иной платформы на базе нужной операционной системы. Кроме Node.js в экшенах поддерживается большой список других платформ, инструкции по настройке которых можно посмотреть в соответствующем разделе официальной документации. В примере используется две операционных системы, на каждой из которых запускается три версии Node.js:

        
          
          runs-on: ${{ matrix.os }}strategy:  matrix:    os: [ubuntu-18.04, ubuntu-20.04]    node: [10, 12, 14]
          runs-on: ${{ matrix.os }}
strategy:
  matrix:
    os: [ubuntu-18.04, ubuntu-20.04]
    node: [10, 12, 14]

        
        
          
        
      

Если вам не очень понятно, зачем Docker нужен и как работает, прочитайте статью «Что такое Docker».

Чтобы можно было запустить экшен на нескольких операционных системах, в секции runs-on необходимо использовать переменную ${{ matrix.os }}. Существует довольно много переменных, которые можно использовать в экшенах. Например, чтобы получить имя экшена нужно использовать переменную github.action, а чтобы получить хэш коммита — github.sha. Список всех доступных для использования переменных в контексте github.* есть в официальной документации. В экшенах есть и другие контексты:

  • env — содержит переменные окружения;
  • job — хранит информацию о текущем джобе, которую можно использовать внутри джоба;
  • steps — хранит информацию о последовательности действий в джобе;
  • runner — хранит информацию о контейнере, в котором запущен текущий джоб;
  • needs — хранит вывод всех джобов, зависимых от текущего джоба.

Кроме переменных в экшенах можно пользоваться переменными окружения в операционной системе напрямую для работы команд, скриптов и программ. Иногда это удобнее, чем контексты и переменные. Список переменных окружения находится на соответствующей странице документации.

Список конкретных действий указывается в секции steps. Такое название используется не случайно, подчёркивается, что это не просто список, это — строгая последовательность действий. Можно использовать заранее готовые экшены с помощью секции uses, которые находятся в публичном или приватном репозитории. На GitHub есть даже специальный раздел для тех экшенов, о которых авторы решили заявить максимально широкой аудитории. Процесс создания и публикации экшенов описан в официальной документации. В качестве очередного действия можно пользоваться и обычными командами терминала с помощью секции run. Если необходимо выполнить несколько команд подряд, их можно расположить построчно, используя вертикальную черту |.

        
          
          steps:  - uses: actions/checkout@v2  - name: Использование Node.js ${{ matrix.node-version }}    uses: actions/setup-node@v1    with:      node-version: ${{ matrix.node-version }}  - run: npm ci  - run: |      npm run build      npm test
          steps:
  - uses: actions/checkout@v2
  - name: Использование Node.js ${{ matrix.node-version }}
    uses: actions/setup-node@v1
    with:
      node-version: ${{ matrix.node-version }}
  - run: npm ci
  - run: |
      npm run build
      npm test

        
        
          
        
      

Подробнее об использовании терминала можно почитать в статье «Интерфейс командной строки». Полностью пример конфигурации для тестирования приложения Node.js будет выглядеть так:

        
          
          name: Тестированиеon:  push:    branches: [ main ]  pull_request:    branches: [ main ]jobs:  test:    runs-on: ${{ matrix.os }}    strategy:      matrix:        os: [ubuntu-18.04, ubuntu-20.04]        node: [10, 12, 14]    steps:      - uses: actions/checkout@v2      - name: Использование Node.js ${{ matrix.node-version }}        uses: actions/setup-node@v1        with:          node-version: ${{ matrix.node-version }}      - run: npm ci      - run: |          npm run build          npm test
          name: Тестирование

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-18.04, ubuntu-20.04]
        node: [10, 12, 14]
    steps:
      - uses: actions/checkout@v2
      - name: Использование Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: |
          npm run build
          npm test

        
        
          
        
      

На практике

Секция статьи "На практике"

Игорь Коровченко советует

Секция статьи "Игорь Коровченко советует"

Публикация для сайта на 11ty

Секция статьи "Публикация для сайта на 11ty"

Попробуем сделать экшен для своего репозитория. Например, мы работаем с движком генератора статики 11ty, и в проекте поддерживается проверка линтерами EditorConfig, stylelint и ESLint, которые выполняют функцию тестирования текстов и кода. Список скриптов в файле конфигурации package.json будет примерно таким:

        
          
          {  "scripts": {    "start": "eleventy --serve --quiet",    "test": "editorconfig-checker && stylelint \"src/styles/**/*.css\" && eslint src/**/*.js",    "build": "eleventy",    "deploy": "cd dist && rsync --archive --compress --delete . user@example.com:/var/www/example.com/html/"  }}
          {
  "scripts": {
    "start": "eleventy --serve --quiet",
    "test": "editorconfig-checker && stylelint \"src/styles/**/*.css\" && eslint src/**/*.js",
    "build": "eleventy",
    "deploy": "cd dist && rsync --archive --compress --delete . user@example.com:/var/www/example.com/html/"
  }
}

        
        
          
        
      

Тогда можно реализовать такую схему работы:

  1. Загрузка репозитория;
  2. Установка Node.js нужных версий;
  3. Установка зависимостей;
  4. Тестирование кодовой базы и контента на соответствие правилам линтеров;
  5. Сборка приложения;
  6. Публикация приложения.

Наложим дополнительные условия: будет отслеживаться пуш только в ветку main и все ветки, названия которых будет начинаться с releases/.

Конфигурация для экшена будет выглядеть так:

        
          
          name: Публикация сайтаon:  push:    branches:      - main      - 'releases/**'  pull_request:    branches:      - mainjobs:  deploy:    runs-on: ubuntu-latest    strategy:      matrix:        node-version: [ 12.x ]    steps:      - uses: actions/checkout@v2      - name: Use Node.js ${{ matrix.node-version }}        uses: actions/setup-node@v1        with:          node-version: ${{ matrix.node-version }}      - run: |        npm ci        npm run test        npm run build        npm run deploy
          name: Публикация сайта

on:
  push:
    branches:
      - main
      - 'releases/**'
  pull_request:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [ 12.x ]
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - run: |
        npm ci
        npm run test
        npm run build
        npm run deploy