Матричные сборкиNew
Матричные сборки значительно упрощают масштабирование и поддержку сложных пайплайнов, делая их более надежными, гибкими и эффективными.
Используя матричные сборки, вы можете:
- Эффективно тестировать код на множестве платформ, операционных систем или версий языков программирования, минимизируя при этом дублирование конфигураций.
- Сократить объем кода, делая его более чистым и поддерживаемым за счет параметризации задач.
- Динамически управлять созданием и выполнением задач, добавляя или исключая специфические комбинации конфигураций, или даже формируя матрицу на основе данных из внешних событий.
- Детально контролировать поведение задач при возникновении ошибок, определяя, должны ли все параллельные задачи быть немедленно отменены или же конкретные задачи могут завершаться с ошибкой без прерывания всего процесса.
- Управлять параллелизмом выполнения задач, что помогает оптимизировать использование ресурсов раннеров и предотвращать их перегрузку.
Матричные стратегии выполнения задач
jobs.<job_id>.strategy
и jobs.<job_id>.strategy.matrix
— это ключевые элементы в файлах конфигурации рабочих процессов, используемые для реализации матричной стратегии выполнения задач.
jobs.<job_id>.strategy
является общей декларацией использования стратегии, а jobs.<job_id>.strategy.matrix
— конкретным определением параметров для создания множества вариаций задач.
jobs.<job_id>.strategy
, стратегия выполнения задач
Секция jobs.<job_id>.strategy
в конфигурации конкретной задачи (<job_id>
) указывает стратегию выполнения. Эта секция содержит настройки того, как будут генерироваться и управляться множественные запуски.
jobs.<job_id>.strategy.matrix
, матричная стратегия
jobs.<job_id>.strategy.matrix
используется для определения матрицы различных конфигураций задач. В матрице можно задать одну или несколько переменных, каждой из которых присваивается массив значений.
Подсекция jobs.<job_id>.strategy.matrix
внутри strategy
определяет саму матрицу конфигураций. Здесь перечисляются переменные и массивы значений для каждой из них:
- одну или несколько переменных (например, python_version, runner);
- для каждой переменной указывается массив значений;
- CI/CD берет все возможные комбинации этих значений и для каждой уникальной комбинации создает отдельный запуск (job run) задачи.
Пример:
on:
push:
jobs:
build_and_test: # Название задачи
strategy:
matrix: # Определение матрицы
runner: ["ubuntu-cloud-runner"] # Раннеры
python_version: ["3.8", "3.9", "3.10"] # Версии Python
runs-on: ${{ matrix.runner }} # Использование раннера из матрицы
steps:
- name: Установить Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }} # Используем версию Python из матрицы
Ограничения и логика работы матрицы
- Ограничение — матрица может генерировать максимум 256 задач на рабочий процесс.
- Для каждой возможной комбинации переменных будет запущена отдельная задача.
Например, если у вас есть переменная
version
со значениями[10, 12, 14]
и переменнаяos
со значениями[ubuntu-latest, windows-latest]
, рабочий процесс выполнит 6 задач (3 версии * 2 ОС), то GitVerse по умолчанию максимизирует количество параллельно выполняемых задач в зависимости от доступности раннеров. - Порядок создания задач определяется порядком переменных в матрице — первая указанная переменная создает первую задачу в рабочем процессе.
- Доступ к переменным: определяемые переменные становятся свойствами в контексте
matrix
. Вы можете ссылаться на них в других частях файла, используя синтаксисmatrix.<имя_переменной>
. Например,matrix.version
иmatrix.os
позволят получить текущие значения переменныхversion
иos
, которые используются конкретным заданием.
Примеры использования матриц
Одномерная матрица
Одномерная матрица используется для запуска однотипных задач с разными значениями одной характеристики:
on:
push:
jobs:
example_job:
runs-on: ubuntu-cloud-runner
strategy:
matrix:
python_version: ['3.8', '3.9', '3.12']
steps:
- name: Install Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python_version }}
Этот рабочий процесс устанавливает переменную python_version
, содержащую список версий Python (['3.8', '3.9', '3.12']
), и запускает задачу для каждой указанной версии. Для каждой итерации используется соответствующая версия Python через синтаксис ${{ matrix.python_version }}
, передаваемая действию установки Python (actions/setup-python
).
Многомерная матрица
Многомерная матрица позволяет указывать несколько переменных, где для каждой возможной комбинации переменных будет запущена отдельная задача:
on:
push:
jobs:
test_environment:
strategy:
matrix:
architecture: ['x64', 'x86']
python_version: ['3.8', '3.9', '3.12']
runs-on: ubuntu-cloud-runner
steps:
- name: Install Python
run: |
echo "Install ${{ matrix.architecture }} python ${{ matrix.python_version }}"
Здесь демонстрируется использование многомерной матрицы для тестирования среды с разными комбинациями платформ и версий Python:
architecture
включает две значения —'x86'
и'x64'
, определяющие целевые архитектуры для запуска тестов;python_version
задает три версии Python —'3.8', '3.9', '3.12'
.
Таким образом, запустится шесть задач, выполняя каждую возможную комбинацию указанных архитектур и версий Python.
Матрица с массивом объектов
Переменная в матрице может быть определена как массив объектов. Это позволяет включать дополнительные, специфичные для каждой комбинации параметры:
matrix:
environment:
- name: production
- name: staging
database:
- type: postgresql
version: '13'
- type: mysql
version: '8.0'
config: innodb_buffer_pool_size=1GB
В данном примере демонстрируется использование массива объектов внутри матрицы, что позволяет гибко конфигурировать рабочие окружения для разных типов баз данных. Параметры матрицы:
environment
— определяет два окружения (production
иstaging
);database
— содержит конфигурации для PostgreSQL и MySQL, включая дополнительные настройки для конкретной версии базы данных.
Для каждого сочетания значений из этих матриц создаются уникальные задачи. Например, конфигурация задачи для MySQL 8.0
дополнительно включает настройку размера буферного пула InnoDB.
Всего создается четыре уникальных контекста для выполнения задач:
- matrix.environment.name: production
matrix.database.type: postgresql
matrix.database.version: '13'
- matrix.environment.name: production
matrix.database.type: mysql
matrix.database.version: '8.0'
matrix.database.config: innodb_buffer_pool_size=1GB
- matrix.environment.name: staging
matrix.database.type: postgresql
matrix.database.version: '13'
- matrix.environment.name: staging
matrix.database.type: mysql
matrix.database.version: '8.0'
matrix.database.config: innodb_buffer_pool_size=1GB
Расширение и исключение конфигураций
jobs.<job_id>.strategy.matrix.include
, расширение матрицы
Значение include
представляет собой список объектов. Используйте jobs.<job_id>.strategy.matrix.include
для расширения существующих матричных конфигураций или добавления совершенно новых:
- Добавление к существующим — пары
ключ:значение
из объектаinclude
добавляются в каждую комбинацию матрицы, если они не перезаписывают оригинальные значения матрицы. - Создание новых комбинаций — если объект из
include
нельзя добавить ни к одной существующей комбинации без перезаписи, он создает новую комбинацию матрицы.
Важные правила:
- Оригинальные значения матрицы не перезаписываются. Это означает, что если в базовой матрице уже есть определенное значение,
include
не изменит его. - Добавленные значения матрицы могут быть перезаписаны другими значениями из того же или последующих объектов
include
. - Невозможные для добавления комбинации создают новые варианты выполнения.
Все комбинации include
обрабатываются после exclude
. Это означает, что вы можете сначала исключить ненужные конфигурации через exclude,
а затем добавить обратно некоторые из них (или новые) через include
, если это необходимо.
Пример: работа с include
on:
push
strategy:
matrix:
city: [moscow, saint_petersburg]
transport: [bus, train]
include:
- weather: sunny
- weather: rainy
transport: bus
- city: moscow
population: large
- city: yekaterinburg
transport: train
В результате будет создано 6 задач со следующими комбинациями:
city: moscow, transport: bus, weather: rainy, population: large
;city: moscow, transport: train, population: large
;city: saint_petersburg, transport: bus, weather: rainy
;city: saint_petersburg, transport: train
;weather: sunny
;city: yekaterinburg, transport: train
.
Логика работы директивы include
Данный пример показывает работу механизма include
для расширения возможностей стандартной матричной стратегии. Исходная матрица состоит из городов (city
) и видов транспорта (transport
), к которым мы добавляем различные условия с помощью блока include
.
Пошаговая логика обработки:
- Базовые комбинации. Изначально получаем 4 базовых набора:
{city: moscow, transport: bus}
{city: moscow, transport: train}
{city: saint_petersburg, transport: bus}
{city: saint_petersburg, transport: train}
- Создание нового набора:
{weather: sunny}
без совпадения с имеющимися, добавится отдельно, итоговые наборы теперь выглядят следующим образом
{city: moscow, transport: bus, weather: sunny}
{city: moscow, transport: train, weather: sunny}
{city: saint_petersburg, transport: bus}
{city: saint_petersburg, transport: train}
{weather: sunny}
-
Уточнение условий. Следующая запись
{weather: rainy, transport: bus}
применяет условие только к задачам, использующим транспортbus
. -
Дополнение новой информацией. Затем идет правило
{city: moscow, population: large}
, которое устанавливает свойство population только для города Москва, остальные базовые правила остаются неизмененными. -
Создание нового набора. Если объект не совпадает ни с одной базовой комбинацией, он автоматически становится отдельной задачей. Так, сочетание
{city: yekaterinburg, transport: train}
генерирует новую строку.
Итоговая последовательность задач выглядит следующим образом:
- {city: moscow, transport: bus, weather: rainy, population: large}
- {city: moscow, transport: train, population: large}
- {city: saint_petersburg, transport: bus, weather: rainy}
- {city: saint_petersburg, transport: train}
- {weather: sunny}
- {city: yekaterinburg, transport: train}
Таким образом, инструкция include
помогает создавать специфические случаи или расширять стандартные матричные схемы, обеспечивая высокую степень гибкости в настройке рабочих процессов.
Пример: немного логики и снова include
Представьте, что нам нужно протестировать приложение на двух языках программирования (Python, JavaScript):
on:
push:
jobs:
test_applications:
strategy:
matrix:
os: [linux, windows, macos]
language: [python, javascript]
include:
- os: linux
language: python
additional_test: database_tests
- os: macos
language: javascript
additional_test: performance_tests
runs-on: ubuntu-cloud-runner
steps:
- name: Setup Environment
run: |
echo "Use ${{ matrix.language }}"
- name: Standard Test Suite
run: |
echo "Running standard tests..."
- if: ${{ matrix.additional_test }}
name: Additional Test Suite
run: |
echo "Running additional ${{ matrix.additional_test }}..."
Базовая матрица состоит из шести комбинаций (три ОС × два языка программирования).
Директива include
добавляет две специальные комбинации:
Ubuntu + Python → дополнительно запускаются тесты базы данных (database_tests).
Windows + JavaScript → дополнительно запускаются тесты производительности (performance_tests).
Эти дополнительные тесты выполняются только для указанных случаев благодаря условному выполнению шага if: ${{ matrix.additional_test }}
.
В итоге, рабочий процесс включает восемь уникальных заданий, шесть из которых выполняют только стандартные тесты, а два — включают дополнительные проверки.
jobs.<job_id>.strategy.matrix.exclude
, исключение конфигураций
Используйте jobs.<job_id>.strategy.matrix.exclude
для исключения конкретных конфигураций из матрицы. Для исключения достаточно частичного совпадения конфигурации — не обязательно указывать все параметры.
Все комбинации exclude
обрабатываются перед include
. Это означает, что вы можете сначала исключить ненужные конфигурации через exclude,
а затем добавить обратно некоторые из них (или новые) через include
, если это необходимо.
Пример исключения конфигураций
on:
push:
jobs:
display_combinations:
strategy:
matrix:
region: [us-east, eu-central]
instance_type: [small, medium, large]
service: [web-app, api-gateway]
exclude:
- region: us-east
instance_type: small
service: web-app
- region: eu-central
instance_type: large
runs-on: ubuntu-cloud-runner
steps:
- name: Display Combinations
run: |
echo "Region: ${{ matrix.region }}, Instance Type: ${{ matrix.instance_type }}, Service: ${{ matrix.service }}"
Получим следующую последовательность комбинаций, исключая указанные в разделе exclude:
Region: us-east, Instance Type: small, Service: api-gateway
;Region: us-east, Instance Type: medium, Service: web-app
;Region: us-east, Instance Type: medium, Service: api-gateway
;Region: us-east, Instance Type: large, Service: web-app
;Region: us-east, Instance Type: large, Service: api-gateway
;Region: eu-central, Instance Type: small, Service: web-app
;Region: eu-central, Instance Type: small, Service: api-gateway
;Region: eu-central, Instance Type: medium, Service: web-app
;Region: eu-central, Instance Type: medium, Service: api-gateway
.
Таким образом, наглядно продемонстрирована работа директивы exclude
: исключенные комбинации не попадают в выполнение рабочего процесса, и остаются только валидные наборы параметров.
Подобный подход полезен, когда необходимо исключить некоторые нежелательные или избыточные комбинации, уменьшая количество ненужных прогонов и оптимизируя процесс непрерывной интеграции.
Управление параллельным выполнением
По умолчанию GitVerse максимизирует количество параллельно выполняемых задач в зависимости от доступности runner'ов. Это означает, что если у вас достаточно ресурсов, все задачи из матрицы могут быть запущены одновременно.
Для ограничения максимального количества одновременно выполняемых задач при использовании матричной стратегии используйте параметр:
jobs.<job_id>.strategy.max-parallel
, ограничение параллельного выполнения
Этот параметр позволяет вам задать максимальное число задач из матрицы, которые могут выполняться параллельно в любой момент времени. Это полезно, например, для управления нагрузкой на self-hosted раннеры или для соблюдения ограничений на ресурсы.
Пример ограничения параллельного выполнения:
on:
push:
jobs:
parallel_matrix:
runs-on: ubuntu-cloud-runner
strategy:
max-parallel: 2 # сейчас это максимальное количество одновременных задач на GitVerse
matrix:
version: [10, 12, 14]
os: [ubuntu-latest, windows-latest]
steps:
- name: Show OS version
run: |
echo "OS: ${{ matrix.os }}"
echo "Version: ${{ matrix.version }}"
В этом примере, несмотря на то что матрица сгенерирует 6 комбинаций (3 версии × 2 ОС), одновременно будут выполняться не более 2 задач. Остальные задачи будут ждать в очереди, пока не освободятся ресурсы.