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, и в проекте поддерживается проверка линтерами 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