Перейти к основному содержимому

Мастер-класс по CI/CD

Часть 1. Основы CI/CD

Что такое методология CI/CD?

Материал основан на Что такое CI/CD | Дока.

Простыми словами, CI/CD (Continuous Integration, Continuous Delivery — непрерывная интеграция и доставка) — это технология автоматизации тестирования и доставки новых модулей разрабатываемого проекта заинтересованным сторонам (разработчики, аналитики, инженеры качества, конечные пользователи и др.).

Основные принципы CI/CD

  • Сегрегация ответственности заинтересованных сторон. Участники процесса разработки и потребители готового проекта разделяют между собой ответственность за ту или иную стадию жизненного цикла продукта. Разработчики и дизайнеры проектируют бизнес-логику, а также обеспечивают положительный пользовательский опыт взаимодействия с готовой системой. Инженеры по качеству вводят сквозные функции и приемочные тесты, DevOps-инженеры организуют логистику кода, а пользователи — дают обратную связь по результатам использования системы.
  • Снижение риска. Каждая группа участников процесса разработки минимизирует возможные риски при прохождении продукта через стадии жизненного цикла (контроль целостности бизнес-логики, пользовательского опыта, оптимизация хранения и обработки данных, миграции и пр.).
  • Короткий цикл обратной связи. В коммерческой разработке скорость реакции на возникновение ошибок, либо запросов нового функционала закладывает основу конкурентоспособности будущей системы. Чтобы добавлять в продукт новый функционал быстрее конкурентов, необходимо стремиться к автоматизации сборки и тестирования кода. Однако, в ситуациях, когда для решения необходимо участие человека, автоматизация может только навредить. Для таких ситуаций рекомендуется сокращать число информационных посредников, обеспечивая короткий цикл обратной связи.
  • Реализация среды. Команде разработки требуется единое рабочее окружение для контроля версий и построения вспомогательных веток для целей контроля качества, приемлемости, масштабируемости и отказоустойчивости производимого кода. По мере контроля протестированные модули должны перемещаться в основную ветку проекта и поступать на тестирование и сборку в составе единого решения. На этапе финального тестирования код также оценивается с позиций безопасности.

Этапы CI/CD

Типичную итерацию процесса разработки с применением CI/CD (источник doka.guide/tools/ci-cd):

CI/CD flow

Детализированное описание этапов:

  • Написание кода. Каждый из разработчиков пишет код своего модуля, проводит ручное тестирование, а затем соединяет результат работы с текущей версией проекта в основной ветке. Для контроля версий используется система Git, либо аналогичные решения. Когда участники команды опубликуют код своих модулей в основной ветке, начнется следующий этап.
  • Сборка. Система контроля версий запускает автоматическую сборку и тестирование проекта. Триггеры для начала сборки настраиваются командой индивидуально — фиксация изменений в основной ветке проекта, сборка по расписанию, по запросу и т.д. Для автоматизации сборки используется Jenkins, либо аналогичный продукт.
  • Ручное тестирование. Когда CI система успешно проверила работоспособность тестовой версии, то код отправляется тестировщикам для ручного обследования. При этом тестовая сборка получает номер кандидата для дальнейшего релиза продукта (например, v.1.0.0-1).
  • Релиз. По итогам ручного тестирования сборка получает исправления, а итоговый номер версии кандидата повышается (например, после первого исправления версия v.1.0.0-1 становится v.1.0.0-2). После этого выпускается версия кода для клиента (например, v.1.0.0) и начинается следующий этап цикла.
  • Развертывание. На этом этапе рабочая версия продукта для клиентов автоматически публикуется на production серверах разработчика. После этого клиент может взаимодействовать с программой и ознакомиться с ее функционалом как непосредственно через готовый интерфейс, так и через облачные сервисы.
  • Поддержка и мониторинг. Конечные пользователи начинают работать с продуктом. Команда разработки поддерживает его и анализирует пользовательский опыт.
  • Планирование. На основе пользовательского опыта формируются запросы на новый функционал для продукта, готовится план доработок. После этого цикл замыкается и переходит в начальную стадию — написание кода. Далее начинается новая итерация CI/CD разработки.

Да благословят боги сей расчудесный мир!

Пару слов про DevOps

Материал основан на Что такое DevOps: зачем он нужен, где применяется и в чём его плюсы и минусы | Блог Яндекс.Практикума.

В любом процессе разработки участвует три команды:

  • Dev — программисты, которые непосредственно пишут код;
  • QA/QC — тестировщики, которые выявляют ошибки в коде, вручную или автоматически;
  • Ops — инженеры, которые поддерживают инфраструктуру для написания кода, например сервера, а также берут уже рабочий код и запускают на реальные сервера, чтобы клиент получил доступ к сайту, сервису или приложению.

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

Чтобы избавиться от этих задержек и недопонимания, была придумана методология DevOps — особый подход к организации команд разработки. Он позволяет выстроить конвейер, на котором разработчики, тестировщики и сисадмины работают в едином потоке и вместе отвечают за результат — код, выпущенный в релиз для пользователей.

Методология DevOps — это комбинация из двух факторов:

  • особой философии, принципов работы DevOps: налаженного общения между командами, совместных KPI, общей ответственности за результат;
  • набора инструментов, который позволяет создать этот самый конвейер: автоматических систем тестирования, инфраструктуры для написания и развёртывания кода, программ для передачи кода между разными командами.

Ключевые инструменты, без которых внедрить подход DevOps нельзя:

  • Системы контроля версий. Обычно используют Git. Это среда, в которую вносят код в процессе написания и разработки. В ней можно видеть все внесённые изменения, быстро откатить приложение к предыдущей конфигурации и сливать вместе разные версии кода, если над одной и той же функцией работает несколько программистов.
  • CI/CD-системы. CI/CD — это комбинация непрерывной интеграции и непрерывного развёртывания контента. В этом и есть суть подхода DevOps, при котором все процессы происходят параллельно. Чтобы обеспечить автоматическую передачу кода, нужны CI/CD-системы, например GitLab или Jenkins. Они позволяют настраивать скрипты и сразу получать обратную связь на переданный дальше по конвейеру код.
  • Системы мониторинга. Они автоматически проверяют сервера и приложения, собирают логи с ошибками, отправляют сообщения о проблемах. Обычно это комплексные приложения типа Grafana и Prometheus.

Инструменты для CI/CD

Материал основан на Что такое методология разработки CI/CD | Академия Selectel.

Разработчики программного обеспечения используют различные инструменты для автоматизации тестирования и доставки кода своих проектов конечным пользователям.

  • GitLab. Среда позволяет управлять репозиториями проекта, документировать функциональность и результаты доработок и тестов, а также отслеживать ошибки и работать с CI/CD pipeline.
  • GitHub. Среда позволяет управлять репозиториями проекта, документировать функциональность и результаты доработок и тестов, а также отслеживать ошибки и работать с CI/CD с помощью GitHub Actions.
  • Travis-CI. Инструмент подключается к репозиториям в GitHub при минимуме настроек. Решение облачное и не требует локальной установки. Кроме того, оно бесплатно для open-source проектов.
  • Circle-CI. Продукт также использует бесшовную интеграцию с GitHub. Предлагается веб-интерфейс для отслеживания версий сборок и ведения задач. Также решение поддерживает отправку оповещений по почте, slack и другим каналам связи.
  • Jenkins. Довольно популярный инструмент в DevOps среде. Заслужил свою репутацию благодаря работе с различными плагинами, позволяющими гибко настраивать CI/CD процессы под требования разработки конкретного продукта.
  • TeamCity. В бесплатном режиме позволяет работать только с 3 агентами сборки, техническая поддержка предоставляется по подписке.
  • PHP Censor. CI-сервер для автоматизации сборки PHP-проектов, работающий с репозиториями GitLab, GitHub, Mercurial и др. Для тестирования используются библиотеки Atoum, PHP Spec, Behat и др. Проект хорошо документирован, но предполагает самостоятельную настройку и хостинг.
  • Rex. Предназначен для автоматизации CI процессов в дата-центрах. Функционирует на Perl-скриптах.
  • Open Build Service (OBS). Предназначен для автоматизации CI/CD в разработке дистрибутивов приложений.
  • Buildbot. CI-система, позволяющая автоматизировать сборку и тестирование приложений. Поддерживает современные VCS и позволяет гибко настраивать сборку за счет Python-компонентов.

И это лишь малая часть инструментов для CI/CD.

Инструменты для CI/CD (источник harness.io/blog):

CI/CD Tools

Преимущества и недостатки

Преимущества:

  • Инструменты CI/CD позволяют быстро выводить программный продукт на рынок, исправлять ошибки, внедрять новую функциональность.
  • Автоматизация позволяет снизить нагрузку на членов команды, облегчает тестирование и уменьшает количество ошибок в каждой итерации. Главный плюс — это быстрая обратная связь.
  • Гибкий план позволяет быстро перераспределять ресурсы команды на решение действительно важных задач.

Недостатки:

  • Часто инструменты CI/CD воспринимаются как швейцарский нож, который может справиться с любыми задачами. При недостатке опыта это может приводить к существенному усложнению процессов в команде.
  • Инструменты CI/CD не могут решить проблемы взаимодействия внутри команды, обо всём придётся договариваться.
  • Все члены команды должны работать в единой экосистеме.

Подробнее о CI/CD:

Часть 2. GitLab CI/CD

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

GitLab CI — инструмент, встроенный в GitLab для автоматизации рутинных задач, возникающих в процессе разработки программного обеспечения. Спектр таких задач огромен и отличается от проекта к проекту, но основные — это тестирование, статический анализ, проверка стиля написания кода и деплой (выпуск) приложения. GitLab CI — конкурент другого популярного инструмента, GitHub Actions. Эти два сервиса во многом похожи, но есть некоторые отличия.

Пример

Допустим, мы договорились в команде об особых правилах оформления кода при помощи EditorConfig, установили его как дев-зависимость и сделали его доступным с помощью команды npm run editorconfig. Можно запускать проверку каждый раз перед коммитом, но всегда будут ситуации, когда это забудут сделать, и код, оформленный неправильно, попадёт в репозиторий. Здесь приходит на помощь GitLab CI/CD — достаточно создать в корне проекта файл .gitlab-ci.yml со следующим содержанием:

EditorConfig:
image: node:lts
script:
- npm ci
- npm run editorconfig

И теперь каждый раз, когда в репозиторий попадает новый код, он будет проверяться на соответствие правилам, а ошибки будут видны в интерфейсе GitLab.

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

Основные понятия

Основной сущностью в GitLab CI/CD является конвейер (pipeline) — конвейер, который может состоять из:

  • джобов (jobs), описывающих что нужно выполнить;
  • этапов (stages), указывающих когда или в какой последовательности нужно выполнить джобы.

Джобы в одном этапе обычно выполняются параллельно. Если все джобы завершились успешно, выполнение переходит к следующему этапу и так далее. Если любой из джобов завершился ошибкой, то выполнение останавливается, и весь конвейер (обычно) считается проваленным.

Создаём .gitlab-ci.yml

GitLab CI полностью конфигурируется с помощью одного файла в формате YAML, который нужно создать в корне проекта — .gitlab-ci.yml.

Джобы часто могут иметь одинаковые свойства, например, образ среды, в которой выполняются действия, предварительные команды и т. д. Чтобы не повторять их каждый раз, нужно объявить их в секции default. Если какому-то джобу нужны другие параметры, можно указать их внутри этого джоба, и они перезапишут глобальные параметры.

В первую очередь нужно указать Docker-образ, в котором будут выполняться джобы. Для Node.js, например, это node:lts — это означает, что наши команды будут выполняться внутри операционной системы Linux с установленными Node.js, npm и даже Yarn. Про буквы lts можно почитать в разделе про версионирование Node.js.

default:
image: node:lts

Задаём подготовительные команды

При работе с CI/CD во фронтенд-проектах чаще всего перед выполнением основного действия необходимо установить зависимости. Для этого мы можем указать их в секции before_script — эти команды будут выполняться в каждом джобе перед основным действием.

default:
image: node:lts
before_script:
- npm -v
- npm install

Указываем этапы

Предположим, что мы хотим запускать сначала проверку кодовой базы с помощью EditorConfig и Stylelint, а потом, если они обе завершатся успешно, запустить тесты. В этом примере можно выделить два этапа: стиль кода и тесты. Определить этапы можно при помощи ключевого слова stages:

stages:
- Стиль кода
- Тесты

Описываем джобы и задаём команду

Теперь укажем все три джоба. Для этого мы вначале указываем название джоба, указываем его этап при помощи ключевого слова stage и передаём список команд в script. В нашем примере каждый джоб будет запускать по одному npm-скрипту.

default:
image: node:lts
before_script:
- npm -v
- npm ci

stages:
- Стиль кода
- Тесты

EditorConfig:
stage: Стиль кода
script:
- npm run editorconfig

Stylelint:
stage: Стиль кода
script:
- npm run stylelint

Автотесты:
stage: Тесты
script:
- npm run test

А вот схематичное представление конфигурации выше:

Схема конвейера (источник: doka.guide):

Схема конвейера

Конфигурация сначала проверяет стиль кода и, если он верный, запускает тесты.

к сведению

Джобы должны иметь уникальные имена — если указать два джоба с одинаковым именем, то из них выполнится только последний — он перезапишет все предыдущие джобы с таким именем!

Продвинутое использование

Если мы хотим запускать определённый джоб вручную, то нужно добавить when: manual:

job:
script: npm run deploy
when: manual

Продолжение при провале

По умолчанию при провале любого джоба весь конвейер отмечается как проваленный, и оставшиеся джобы не выполнятся. Однако бывают ситуации, когда этого поведения хочется избежать. Например, мы добавили джоб с тестами в только что появившейся версии Node.js и просто хотим видеть проблемы, которые потенциально нужно исправить в будущем. Здесь придёт на помощь allow_failure: true:

job:
image: node:latest
script: npm run test
allow_failure: true

Выполнение джобов по условию

GitLab даёт доступ к большому количеству переменных окружения с полезной информацией. Например, $CI_COMMIT_BRANCH содержит текущую ветку, $CI_COMMIT_SHORT_SHA — короткий хеш коммита, $CI_PIPELINE_SOURCE — источник вызова текущего конвейера и так далее. С их помощью мы можем запускать определённые джобы при соблюдении заданных условий. Для этого нужно объявить одну или несколько секций rules.

Вот такой джоб будет выполняться только для коммитов в ветку main:

job:
script: npm run deploy-to-production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'

Запуск по расписанию

В отличие от GitHub Actions, в GitLab CI/CD запуск конвейеров по расписанию настраивается только в веб-интерфейсе. Для этого нужно открыть страницу репозитория и выбрать CI/CD → Schedules. Перед нами откроется список уже существующих правил и кнопка добавления нового. В форме добавления можно указать название правила, выбрать интервал из списка или указать свой в синтаксисе Cron. Последним важным полем является ветка — при срабатывании правила конвейер запустится, как будто был запушен код в этой ветке. Отличие в том, что переменная $CI_PIPELINE_SOURCE будет содержать значение schedule.

Серия джобов

Ещё одна типичная задача — прогнать тесты в разных версиях Node.js. Можно для каждой версии создать вручную джоб, а можно указать список переменных:

Unit Tests:
script: node -v
image: ${NODE_VERSION}
parallel:
matrix:
- NODE_VERSION: ["node:14", "node:16", "node:17"]

В примере выше мы объявили список NODE_VERSION из трёх элементов. GitLab создаст три джоба с именами: Unit Tests [node:14], Unit Tests [node:16] и Unit Tests [node:17], а потом в каждом джобе заменит все места использования переменной NODE_VERSION. Поэтому image в каждом джобе будет разный.

Часть 3. Закрепление основных концепций GitLab CI/CD

Основано на материале Get started with GitLab CI/CD | GitLab

CI/CD - это непрерывный метод разработки программного обеспечения, при котором вы непрерывно создаете, тестируете, развертываете и отслеживаете итеративные изменения кода.

Этот итеративный процесс помогает снизить вероятность того, что вы разработаете новый код на основе баг или неудачных предыдущих версий. GitLab CI/CD может выявлять ошибки на ранних стадиях цикла разработки и помогает гарантировать, что весь код, развертываемый в рабочей среде, соответствует установленным вами стандартам кода.

Файл .gitlab-ci.yml

Чтобы использовать GitLab CI/CD, вы начинаете с .gitlab-ci.yml файла в корневом каталоге вашего проекта, который содержит конфигурацию для вашего конвейера CI/CD. Этот файл соответствует формату YAML и имеет свой особый синтаксис.

Вы можете назвать этот файл как угодно, но .gitlab-ci.yml это наиболее распространенное имя.

В .gitlab-ci.yml файле вы можете определить:

  • Задачи, которые вы хотите выполнить, например, протестировать и развернуть ваше приложение.
  • Другие файлы конфигурации и шаблоны, которые вы хотите включить.
  • Зависимости и кэши.
  • Команды, которые вы хотите запускать последовательно, и те, которые вы хотите запускать параллельно.
  • Местоположение для развертывания вашего приложения.
  • Независимо от того, хотите ли вы запускать скрипты автоматически или запускать любой из них вручную.

Раннеры

Раннеры - это агенты, которые выполняют ваши задания. Эти агенты могут выполняться на физических машинах или виртуальных экземплярах. В вашем .gitlab-ci.yml файле вы можете указать образ контейнера, который хотите использовать при выполнении задания. Программа-исполнитель загружает образ, клонирует ваш проект и запускает задание либо локально, либо в контейнере.

Если вы используете GitLab.com, раннеры SaaS в Linux, Windows и macOS уже доступны для использования. И вы можете зарегистрировать свои собственные раннеры на GitLab.com, если хотите.

Если вы не используете GitLab.com, вы можете:

  • Зарегистрируйте раннеры или используйте уже зарегистрированные раннеры для вашего самоуправляемого экземпляра.
  • Создать раннер на своем локальном компьютере.

Конвейеры

Конвейеры (Pipelines) состоят из заданий и этапов:

  • Задания определяют, что вы хотите сделать. Например, протестировать изменения кода или выполнить развертывание в промежуточной среде.
  • Задания сгруппированы по этапам. Каждый этап содержит по крайней мере одно задание. Типичными этапами могут быть build, test и deploy.

Переменные CI/CD

Переменные CI/CD помогают настраивать задания, делая доступными для них значения, определенные в другом месте. Они могут быть жестко запрограммированы в вашем .gitlab-ci.yml файле, настройках проекта или динамически сгенерированы.

Компоненты CI/CD

Компоненты CI/CD - это повторно используемые единицы конфигурации отдельного конвейера. Используйте их для создания конфигурации всего конвейера или небольшой части более крупного конвейера.

Часть 4. Создание и запуск конвейера GitLab CI/CD

Основано на материале Tutorial: Create and run your first GitLab CI/CD pipeline | GitLab.

Предварительные требования

Перед началом работы убедитесь, что у вас есть:

  • Информация о том, как подключиться в приватному GitLab и как получить доступ к публичному GitLab. Информацию об этом можно получить от преподавателя.
  • Проект в GitLab (в частном или публичном).
    • Вы можете также бесплатно создать общедоступный проект на https://gitlab.com.
  • Роль сопровождающего (maintainer) или владельца (owner) проекта.

Убедитесь, что у вас есть доступные раннеры В GitLab исполнители - это агенты, которые выполняют ваши задания CI / CD.

Чтобы просмотреть доступные раннеры

Чтобы просмотреть доступные раннеры:

  • Перейдите в Settings > CI/CD и разверните Runners.

  • Пока у вас активен хотя бы один бегун с зеленым кружком рядом с ним, у вас есть бегун, доступный для обработки ваших заданий:

    Доступные раннеры группы

Теперь создайте .gitlab-ci.yml файл. Это файл YAML, в котором вы указываете инструкции для GitLab CI/CD.

В этом файле вы определяете:

  • Структура и порядок заданий, которые должен выполнять раннер.
  • Решения, которые должен принимать пользователь при возникновении определенных условий.

Чтобы создать .gitlab-ci.yml файл:

  • На левой боковой панели выберите Code > Repository.

  • Над списком файлов выберите ветку, к которой вы хотите привязаться. Если вы не уверены, оставьте master или main. Затем выберите значок плюса (+) и New file:

    Создание нового файла

  • В качестве имени файла введите .gitlab-ci.yml и в увеличенном окне вставьте этот пример кода:

    build-job:
    stage: build
    script:
    - echo "Hello, $GITLAB_USER_LOGIN!"

    test-job1:
    stage: test
    script:
    - echo "This job tests something"

    test-job2:
    stage: test
    script:
    - echo "This job tests something, but takes more time than test-job1."
    - echo "After the echo commands complete, it runs the sleep command for 20 seconds"
    - echo "which simulates a test that runs 20 seconds longer than test-job1"
    - sleep 20

    deploy-prod:
    stage: deploy
    script:
    - echo "This job deploys something from the $CI_COMMIT_BRANCH branch."
    environment: production

    В этом примере показаны четыре задания: build-job, test-job1, test-job2 и deploy-prod. Комментарии, перечисленные в echo командах, отображаются в пользовательском интерфейсе при просмотре заданий. Значения для предопределенных переменных $GITLAB_USER_LOGIN и $CI_COMMIT_BRANCH заполняются при выполнении заданий.

    Окно редактора нового файла

  1. Выберите Commit changes.

Конвейер запускается и выполняет задания, определенные вами в файле .gitlab-ci.yml:

Пройденный конвейер

Изучение состояния конвейера и заданий

Теперь взгляните на ваш конвейер и задания внутри него.

  1. Перейдите в Build > Pipelines. Должен отображаться конвейер с тремя этапами:

    Три этапа CI/CD

  2. Просмотрите визуальное представление вашего конвейера, выбрав идентификатор конвейера:

    Граф конвейера

  3. Просмотрите подробные сведения о задании, выбрав название задания. Например, deploy-prod:

    Просмотр сведений о задании

Вы успешно создали свой первый конвейер CI/CD в GitLab! Теперь вы можете приступить к настройке вашего .gitlab-ci.yml и определению более сложных заданий.

Советы по созданию .gitlab-ci.yml

Вот несколько советов, чтобы начать работу с .gitlab-ci.yml файлом:

  • Спецификацию .gitlab-ci.yml вы можете изучить в официальной документации.
  • Используйте редактор конвейеров для редактирования вашего .gitlab-ci.yml файла.
  • Каждое задание содержит раздел сценария и относится к этапу:
    • stage описывает последовательное выполнение заданий. Если доступны исполнители, задания на одном этапе выполняются параллельно.
    • Используйте ключевое слово needs для выполнения заданий вне последовательности этапов. Это создает ориентированный ациклический граф (DAG).
  • Вы можете задать дополнительную конфигурацию, чтобы настроить выполнение ваших заданий и этапов:
    • Используйте rules ключевое слово, чтобы указать, когда запускать или пропускать задания.
    • Сохраняйте информацию о заданиях и этапах на постоянной основе в конвейере с помощью cache и artifacts. Эти ключевые слова являются способами хранения зависимостей и выходных данных задания, даже при использовании эфемерных исполнителей для каждого задания.
    • Используйте default ключевое слово для указания дополнительных конфигураций, которые применяются ко всем заданиям. Это ключевое слово часто используется для определения before_script и after_script разделов, которые должны выполняться в каждом задании.

Часть 5. Создание и запуск усовершенствованного конвейера GitLab CI/CD

В данной части предстоит:

  • Добавить в репозиторий проект с юнит-тестами.
  • Настроить конвейер в GitLab:
    • Статический анализ кода.
    • Модульное тестирование.
    • Сборка проекта в бинарник и публикация в артефакты Releases.
    • Настройка раннеров.
  • Активация настроенных конвейеров.

Подготовка проекта

Создайте новый проект GitLab в указанном преподавателем месте.

Мы воспользуемся проектом, который рассматривали на предыдущим практическом задании. Доработаем его для того, чтобы мы могли посмотреть дополнительные возможности GitLab CI/CD.

Структура проекта будет выглядеть следующим образом:

awesome-project
├── calculator.py
├── README.md
├── .gitignore
├── calculator_tests.py
└── requirements.txt

Файл .gitignore мы можем заполнить по примеру из репозитория github/gitignore.

Содержимое calculator.py:

import math


# Рассчитать гипотенузу
def get_hypotenuse(a, b):
return math.sqrt(math.pow(a, 3) + math.pow(b, 3))

# Получить площадь прямоугольного треугольника
def get_area(a, b):
return a * b


if __name__ == "__main__":
print("Введите a:")
a = int(input())
print("Введите b:")
b = int(input())
print("c =", get_hypotenuse(a, b))
print("S =", get_area(a, b))

Для тестирования методов, представленных calculator.py, подготовлены модульные тесты в calculator_tests.py.

Если вам не знакомы модульные тесты Python, то рекомендуется ознакомиться со статьей Тестируем на Python: unittest и pytest. Инструкция для начинающих | Tproger.

Узнать о том, что такое модульные тестирование, можно узнать в статье Юнит-тесты: что это такое, зачем они нужны и как их проводят | Блог Яндекс.Практикума.

Файл calculator_tests.py выглядит следующим образом:

import unittest

from calculator import get_hypotenuse, get_area


class CalculatingHypotenuseTestCase(unittest.TestCase):
def test_case_1(self):
res = get_hypotenuse(3, 4)
self.assertEqual(res, 5)

def test_case_2(self):
res = get_hypotenuse(5, 12)
self.assertEqual(res, 13)

def test_case_3(self):
res = get_hypotenuse(8, 15)
self.assertEqual(res, 17)

class CalculatingAreaTestCase(unittest.TestCase):
def test_case_1(self):
res = get_area(3, 4)
self.assertEqual(res, 6)

def test_case_2(self):
res = get_area(5, 12)
self.assertEqual(res, 30)

def test_case_3(self):
res = get_area(8, 15)
self.assertEqual(res, 60)

Для того, чтобы наше приложение можно было запускать не только на локальном компьютере, необходимо указать перечень пакетов, который используется в проекте. Это можно сделать с помощью файлов requirements.txt.

Что такое requirements.txt и как с ним работать смотрите в статье Что такое requirements.txt и как с ним работать? | Pythonist.

Содержимое requirements.txt:

altgraph==0.17.3
astroid==2.12.12
autopep8==1.7.0
dill==0.3.5.1
isort==5.10.1
lazy-object-proxy==1.7.1
macholib==1.16.2
mccabe==0.7.0
platformdirs==2.5.2
pycodestyle==2.9.1
pyinstaller==5.5
pyinstaller-hooks-contrib==2022.10
pylint==2.15.4
toml==0.10.2
tomli==2.0.1
tomlkit==0.11.5
wrapt==1.14.1

Вот такие файлы должны получиться в вашем репозитории. Чтобы наполнить ваш проект содержимым, вы можете воспользоваться Web IDE, либо работать через локальный репозиторий с отправкой в удаленный репозиторий.

Подготовка к описанию конвейера

Кратко напомним процесс, который обычно реализуется конвейер в GitLab CI:

  • разработчик отправляет коммит в репозиторий, создаёт merge request через сайт, или ещё каким-либо образом явно или неявно запускает конвейер;
  • из конфигурации выбираются все задачи, условия которых позволяют их запустить в данном контексте;
  • задачи организуются в соответствии со своими этапами;
  • этапы по очереди выполняются - т.е. параллельно выполняются все задачи этого этапа:
    • если этап завершается неудачей (т.е. завершается неудачей хотя бы одна из задач этапа) - конвейер останавливается (почти всегда);
    • если все этапы завершены успешно, конвейер считается успешно прошедшим.

Таким образом, имеем:

  • конвейер (pipeline) - набор задач, организованных в этапы, в котором можно собрать, протестировать, упаковать код, развернуть готовую сборку в облачный сервис, и пр.;
  • этап (stage) - единица организации конвейера, содержит 1+ задачу;
  • задача (job) — единица работы в конвейера. Состоит из скрипта (обязательно), условий запуска, настроек публикации/кеширования артефактов и много другого.

Соответственно, задача при настройке CI/CD сводится к тому, чтобы создать набор задач, реализующих все необходимые действия для сборки, тестирования и публикации кода и артефактов.

Исходная позиция

Имеем:

  • репозиторий в GitLab.

Хотим:

  • Автоматическую сборку и тестирование для каждого merge request;
  • Помимо тестирования провести статический анализ кода;
  • В случае прохождения всех тестов, разрешить слияние. В противном случае заблокировать слияние.
  • В случае успешного мерджа в ветку по умолчанию, собрать бинарники программы и публиковать их в релизах.

Описанные требования органично ложатся на следующую модель конвейера:

  • Этап 1 — тестирование

    Проводим статический анализ кода и тестируем его.

  • Этап 2 - сборка бинарников

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

  • Этап 3 - подготовка релиза

    Подготавливаем красивый релиз с прикрепленными бинарниками.

Настройка Gitlab CI для приложения Python

Основано на статье "Setting Gitlab CI/CD for Python application" и "Python Unit Test And Some CI via GitLab (Python)".

В этой части работы предстоит настроить процесс GitLab CI для заданий:

  • статический анализ с использованием pylint;
  • модульное тестирование с использованием unittest.

Создание .gitlab-ci.yml

Первым шагом является добавление в репозиторий .gitlab-ci.yml yml определяет структуру и порядок конвейеров и определяет, как выполнять Gitlab Runner:

# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: "python:3.10.3"
# команды для запуска в контейнере Docker перед запуском каждого задания.
before_script:
- python --version
- pip install -r requirements.txt
# различные этапы в конвейере
stages:
- Static Analysis
- Test
# определяет задание в статическом анализе
pylint:
stage: Static Analysis
script:
- pylint -d C0301 calculator.py

Напомним, что перед этим заданием мы специально добавляли requirements.txt в репозиторий.

Мониторинг выполнения можно найти в левой вкладке Build > Pipelines в нашем проекте:

Обзор мониторинга pipelines

Состояние выполнения задач

Перейдя на вкладку мониторинга конвейер, можно увидеть, что он завершился с ошибкой:

Конвейер с ошибкой

Во вкладке "Неудачные задания" показывает причину, по которой задача с pylint завершилась с ошибкой:

Обзор задач с ошибкой

Внесение исправлений

После изучения перечня предупреждений, на которые указал pylint, программа calculator.py была изменена следующим образом:

'''Программа расчета величин для прямоугольного треугольника.'''
import math


def get_hypotenuse(side_1, side_2):
'''Рассчитать гипотенузу.'''
return math.sqrt(math.pow(side_1, 3) + math.pow(side_2, 3))


def get_area(side_1, side_2):
'''Получить площадь прямоугольного треугольника.'''
return side_1 * side_2


if __name__ == "__main__":
print("Введите a:")
a = int(input())
print("Введите b:")
b = int(input())
print("c =", get_hypotenuse(a, b))
print("S =", get_area(a, b))

Не забудьте зафиксировать изменения и отправить их в удаленный репозиторий!

Изменения, внесенные в код, привели к положительному результату. Теперь задача завершается успешно:

Исправление конвейера с задачей pylint

Теперь задания выполняются, и мы можем перейти к следующему этапу тестирования.

Запуск тестов с помощью pytest на GitLab CI

Добавим этап тестирования в .gitlab-ci.yml:

# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: "python:3.10.3"
# команды для запуска в контейнере Docker перед запуском каждого задания.
before_script:
- python --version
- pip install -r requirements.txt
# различные этапы в конвейере
stages:
- Static Analysis
- Test
# определяет задание в статическом анализе
pylint:
stage: Static Analysis
script:
- pylint -d C0301 calculator.py

unittest:
stage: Test
script:
- python -m unittest calculator_tests.py

unittest покажет название тестового примера и результат:

Провал задачи unittest

Исправление нашей программы после тестов

Давайте, наконец-то, починим нашу программу. Изменим calculator.py следующим образом:

'''Программа расчета величин для прямоугольного треугольника.'''
import math


def get_hypotenuse(side_1, side_2):
'''Рассчитать гипотенузу.'''
return math.sqrt(math.pow(side_1, 2) + math.pow(side_2, 2))


def get_area(side_1, side_2):
'''Получить площадь прямоугольного треугольника.'''
return side_1 * side_2 / 2


if __name__ == "__main__":
print("Введите a:")
a = int(input())
print("Введите b:")
b = int(input())
print("c =", get_hypotenuse(a, b))
print("S =", get_area(a, b))

Настройка условий исполнения задач

Основано на статье "Choose when to run jobs".

В GitLab используется rules для включения или исключения заданий в конвейерах.

Правила оцениваются по порядку до первого совпадения. Когда найдено совпадение, задание либо включается, либо исключается из конвейера, в зависимости от конфигурации. Смотрите rules Ссылку для получения более подробной информации.

Давайте явно укажем, что наши проверки кода необходимо выполнять только при запросах на слияние в ветку по умолчанию:

# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: "python:3.10.3"

# команды для запуска в контейнере Docker перед запуском каждого задания.
before_script:
- python --version
- pip install -r requirements.txt

# различные этапы в конвейере
stages:
- Static Analysis
- Test

# определяет задание в статическом анализе
pylint:
stage: Static Analysis
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
script:
- pylint -d C0301 calculator.py

unittest:
stage: Test
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
script:
- python -m unittest calculator_tests.py

Кстати, можно заметить что тут стало еще больше переменных со знаком $. Это переменные Gitlab CI/CD. Рекомендуется самостоятельно изучить какие они бывают и какие использовались для описания нашего конвейера.

И давайте зафиксируем какое-нибудь изменение в нашей программе в какой-нибудь новой ветке, чтобы потом эту ветку слить в ветку по умолчанию.

Создание запроса на слияние отдается на самостоятельное выполнение. Изучите материалы предыдущего практического задания.

Если вы все успешно сделаете, то увидите такой интерфейс запроса на слияние:

Отчет о тестировании в запросе на слияние

Давайте, наконец-то, починим нашу программу:

'''Программа расчета величин для прямоугольного треугольника.'''
import math


def get_hypotenuse(side_1, side_2):
'''Рассчитать гипотенузу.'''
return math.sqrt(math.pow(side_1, 2) + math.pow(side_2, 2))


def get_area(side_1, side_2):
'''Получить площадь прямоугольного треугольника.'''
return side_1 * side_2 / 2


if __name__ == "__main__":
print("Введите a:")
a = int(input())
print("Введите b:")
b = int(input())
print("c =", get_hypotenuse(a, b))
print("S =", get_area(a, b))

И теперь у нас в запросе на слияние все хорошо:

Запрос на слияние с успешно завершенным конвейером

Нам остается только принять запрос на слияние и осуществить слияние. Что и предлагается вам сделать.

Подготовка релиза

Мы научились тестировать. Теперь, давайте научимся собирать и выпускать релизы!

Измененный .gitlab-ci.yml для выпуска релизов:

# This file is a template, and might need editing before it works on your project.
# Official language image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/python/tags/
image: "python:3.10.3"

# команды для запуска в контейнере Docker перед запуском каждого задания.
before_script:
- python --version
- pip install -r requirements.txt

# различные этапы в конвейере
stages:
- Static Analysis
- Test
- Prepare Executable
- Release

# определяет задание в статическом анализе
pylint:
stage: Static Analysis
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
script:
- pylint -d C0301 calculator.py

unittest:
stage: Test
rules:
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH
script:
- python -m unittest calculator_tests.py

prepare-executable:
stage: Prepare Executable
rules:
- if: $CI_COMMIT_TAG
when: never # Не выполняется это задание, когда тег создается вручную
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Запускает это задание, когда коммиты будут перенесены или объединены в ветку по умолчанию
script:
- echo GE_JOB_ID=$CI_JOB_ID >> build.env
- pyinstaller calculator.py
- apt update
- apt install -y zip
- zip -r executable.zip ./dist
artifacts:
paths:
- executable.zip
reports:
dotenv: build.env # используется artifacts:reports:dotenv, чтобы предоставить переменные другим заданиям

release:
stage: Release
image: registry.gitlab.com/gitlab-org/release-cli:latest
needs:
- job: prepare-executable
artifacts: true
before_script:
- ''
rules:
- if: $CI_COMMIT_TAG
when: never # Не выполняется это задание, когда тег создается вручную
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Запускает это задание, когда коммиты будут перенесены или объединены в ветку по умолчанию
script:
- echo "running release_job for $TAG"
- echo 'Previous Job ID is printed below'
- echo $GE_JOB_ID
release: # См. https://docs.gitlab.com/ee/ci/yaml/#release для поиска информации о доступных свойствах
tag_name: 'v0.$CI_PIPELINE_IID' # Версия увеличивается для каждого конвейера.
description: 'v0.$CI_PIPELINE_IID'
ref: '$CI_COMMIT_SHA' # Тег создается из SHA конвейера.
assets:
links:
- name: 'Executable'
url: '${CI_PROJECT_URL}/-/jobs/${GE_JOB_ID}/artifacts/file/executable.zip'

В этом файле мы добавили новые этапы:

  • Prepare Executable
  • Release

В сборке исполняемого указаны правила запуска, которые позволяют правильно реагировать на события: мы хотим готовить релиз в тот момент, когда изменения происходят с веткой по умолчанию. В этом же задании собираем из папки ./dist архив executable.zip. Также мы дополнительно проводим некоторые операции, чтобы использовать некоторые переменные окружения в следующей задаче. В завершении описания задачи указано то, что executable.zip является артефактом задания, который нужно сохранить.

Про выпуск релизов имеется статья в официальной документации. Обратим внимание на то, как связаны между собой задачи Prepare Executable и Release: через needs. Также можно увидеть, что записанные переменные окружения в Prepare Executable доступны при выполнении задачи Release.

Дополнительно вы также можете использовать теги у задач. Они позволят вам определить какие конкретно раннеры вы хотите использовать.

Результаты

Давайте посмотрим на результаты:

Пройденный конвейер

Обзор связанности задач конвейера:

Связанность задач конвейера

Созданный тег репозитория:

Тег репозитория

В артефактах конвейера можно увидеть наш архив:

Артефакты конвейера

Часть 6. Изучение альтернативных инструментов для разворачивания CI/CD

GitHub Actions

Gitea Actions

Jenkins

Для дальнейшего изучения