Запуск тестов Shell в CI-конвейерах
Подключайте ShellCheck и Bats к GitHub Actions, чтобы каждая правка Shell проходила проверку только при успешных результатах
«Запуск тестов Shell в CI-конвейерах» — бесплатный урок Linux Command Line & Bash Scripting Mastery на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Linux Command Line & Bash Scripting Mastery, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.
Зачем нужны CI для скриптов оболочки
Скрипты оболочки — это код, и, как любой код, они заслуживают автоматических проверок качества. Без CI опечатка в скрипте развёртывания может незаметно попасть в рабочую среду и вызвать сбой в три часа ночи.
Надёжный конвейер CI для проектов на Bash обеспечивает две вещи при каждом запросе на включение изменений:
- Статический анализ с помощью
ShellCheck— обнаруживает синтаксические ошибки, небезопасные шаблоны и проблемы переносимости POSIX ещё до запуска скрипта. - Модульные и интеграционные тесты с помощью
Bats(Bash Automated Testing System) — выполняет Ваши функции и проверяет правильность их работы.
Вместе они образуют защитную сеть, которая позволяет без опасений реорганизовывать код и ускоряет знакомство новых участников с проектом. В этом уроке оба инструмента подключаются к GitHub Actions — наиболее распространённой бесплатной платформе CI для проектов с открытым исходным кодом и небольших команд.
Введение в GitHub Actions для проектов со скриптами оболочки
GitHub Actions — это управляемая событиями система CI/CD, встроенная в GitHub. Рабочий процесс представляет собой YAML-файл, хранящийся в каталоге .github/workflows/. Он запускается по событиям (отправка изменений, pull_request и т. д.) и выполняет задания на предоставленных исполнителях.
Основные понятия:
on:— событие запуска (например,push,pull_request)jobs:— параллельные единицы работы, каждая на новой VMsteps:— последовательные команды оболочки или повторно используемые действия внутри заданияruns-on:— образ исполнителя (мы используемubuntu-latest)
Файлы рабочих процессов необходимо зафиксировать в репозитории. GitHub обнаруживает их автоматически — дополнительная внешняя настройка не требуется.
# Minimal skeleton — .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
shell-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Steps go here"Установка ShellCheck в рабочем процессе
ShellCheck уже установлен на исполнителях ubuntu-latest, поэтому в большинстве случаев шаги установки не нужны. Однако предустановленная версия может отставать от последнего выпуска. Для воспроизводимых сборок зафиксируйте конкретную версию.
Две стратегии установки:
- Использовать предустановленный двоичный файл — самый простой вариант, достаточный для большинства проектов.
- Установить зафиксированную версию из официального архива выпуска GitHub — это гарантирует одинаковую версию анализатора локально и в CI.
Ниже показан подход с фиксированной версией: строка версии хранится в переменной окружения, поэтому для обновления достаточно изменить одну строку.
# .github/workflows/ci.yml — ShellCheck install step
- name: Install ShellCheck
env:
SC_VERSION: v0.10.0
run: |
curl -sSfL \
"https://github.com/koalaman/shellcheck/releases/download/${SC_VERSION}/shellcheck-${SC_VERSION}.linux.x86_64.tar.xz" \
| tar -xJf - --strip-components=1 -C /usr/local/bin shellcheck-${SC_VERSION}/shellcheck
shellcheck --versionЗапуск ShellCheck для каждого скрипта
После установки нужен шаг, который находит и анализирует все скрипты оболочки в репозитории. Используйте find, чтобы найти файлы, а затем передайте их в shellcheck через конвейер.
Важные флаги:
-e SC2034— исключает определённое правило (используйте редко и с комментарием).--severity=warning— завершает работу с ошибкой только при предупреждениях и более серьёзных проблемах, игнорируя рекомендации по стилю.-x— отслеживает директивыsource, чтобы анализировать также подключённые файлы.
Если shellcheck обнаруживает проблему, он завершается с ненулевым кодом, что автоматически делает шаг CI неуспешным — дополнительная логика не нужна.
# .github/workflows/ci.yml — ShellCheck lint step
- name: Lint shell scripts
run: |
# Find all .sh files and files with a bash/sh shebang
mapfile -t scripts < <(
find . -type f -name '*.sh' -not -path './.git/*'
)
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No shell scripts found — skipping.'
exit 0
fi
echo "Linting ${#scripts[@]} file(s)..."
shellcheck --severity=warning -x "${scripts[@]}"Что такое Bats и как он работает
Bats (Bash Automated Testing System) — совместимая с TAP платформа тестирования Bash. Каждый файл тестов имеет расширение .bats и содержит блоки @test.
Тест считается пройденным, если его тело завершается с кодом 0, и непройденным — если с ненулевым кодом. Bats предоставляет вспомогательные переменные и функции:
$status— код завершения последней командыrun.$output— объединённый стандартный вывод и вывод ошибок последней командыrun.$lines— массив строк вывода.run <cmd>— выполняет команду, не завершая тест с ошибкой при ненулевом коде выхода.
Вспомогательная команда run крайне важна: без неё неуспешная команда прервала бы тест до того, как Вы смогли бы проверить $status.
#!/usr/bin/env bats
# tests/greet.bats
setup() {
# Runs before every @test block
source "${BATS_TEST_DIRNAME}/../lib/greet.sh"
}
@test "greet outputs hello with the given name" {
run greet "Alice"
[ "$status" -eq 0 ]
[ "$output" = "Hello, Alice!" ]
}
@test "greet fails when no argument is provided" {
run greet
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}Установка Bats-Core с помощью подмодуля Git
Канонический способ добавить Bats в проект — использовать подмодуль Git. Это фиксирует конкретную ревизию, обеспечивает одинаковую версию средства запуска локально и в CI и избавляет от зависимости от менеджеров пакетов.
Выполните эти команды один раз локально, затем зафиксируйте результат:
git submodule add https://github.com/bats-core/bats-core test/batsgit submodule add https://github.com/bats-core/bats-support test/test_helper/bats-supportgit submodule add https://github.com/bats-core/bats-assert test/test_helper/bats-assert
В CI восстановите подмодули с помощью actions/checkout@v4 и параметра submodules: recursive. Ниже показана полная конфигурация получения кода.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersЗапуск тестов Bats в CI
Когда Bats доступен (через подмодуль или установку пакета), запуск тестов выполняется одной командой. Укажите каталог, и Bats рекурсивно найдёт каждый файл .bats с флагом --recursive.
Флаг --formatter tap выводит данные в формате TAP (Test Anything Protocol), который многие системы CI анализируют для формирования отчётов о тестах. Форматировщик pretty по умолчанию лучше подходит для чтения человеком в необработанных журналах.
Используйте --timing, чтобы заранее выявлять медленные тесты: тест, выполняющийся более 5 секунд, обычно указывает на нежелательный сетевой вызов или отсутствующую имитацию.
# .github/workflows/ci.yml — Bats test step
- name: Run Bats tests
run: |
# If installed as a submodule:
./test/bats/bin/bats \
--recursive \
--timing \
tests/
# If installed via apt or brew (alternative):
# bats --recursive --timing tests/Полный рабочий процесс: ShellCheck + Bats
Теперь объединим всё в один готовый к использованию рабочий процесс. Здесь применены следующие рекомендации:
- Два отдельных задания (
lintиtest) выполняются параллельно, обеспечивая более быструю обратную связь. - В задании
testуказаноneeds: lint, поэтому тесты запускаются только после успешного анализа — это предотвращает расход времени исполнителя на заведомо сломанный код. - Зафиксированные версии действий (
@v4) предотвращают неожиданные сбои из-за обновлений со стороны поставщиков. - Блок
permissions:ограничивает токен рабочего процесса минимально необходимыми разрешениями.
# .github/workflows/ci.yml
name: Shell CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
lint:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: |
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
[[ ${#scripts[@]} -gt 0 ]] && shellcheck --severity=warning -x "${scripts[@]}"
test:
name: Bats Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Run tests
run: ./test/bats/bin/bats --recursive --timing tests/Кэширование зависимостей для ускорения запусков
Если вспомогательные средства Bats или другие инструменты устанавливаются через менеджер пакетов внутри рабочего процесса, кэширование значительно ускоряет последующие запуски. GitHub Actions предоставляет для этого действие actions/cache.
Основные правила эффективного кэширования:
- Используйте ключ кэша, включающий OS, название инструмента и хеш файла блокировки, чтобы кэш автоматически сбрасывался при изменении зависимостей.
- Запасной вариант
restore-keysпозволяет рабочему процессу использовать устаревший кэш вместо повторного запуска с нуля, если кэш не найден. - Для подмодулей Git кэширование редко необходимо, поскольку получение подмодулей выполняется быстро. Кэш особенно полезен для
npm,pipили установки компилируемых инструментов.
# .github/workflows/ci.yml — cache step example
- name: Cache Bats npm helpers
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-bats-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-bats-
- name: Install helpers
run: npm ci # uses cache when availableЗащита ветвей: обязательные успешные проверки
Рабочий процесс CI, который не блокирует слияние, в лучшем случае носит рекомендательный характер. Правила защиты ветвей GitHub превращают проверки в обязательные условия.
Чтобы настроить их, откройте Settings → Branches → Add rule для main, а затем включите:
- Require status checks to pass before merging — выберите проверки ShellCheck и Bats Tests по названию.
- Require branches to be up to date before merging — это не позволяет включить изменения из запроса, прошедшего проверки на устаревшей основе, если они содержат сломанный код.
- Do not allow bypassing the above settings — правила применяются даже к администраторам репозитория.
После настройки этих правил единственный путь к слиянию — запрос на включение изменений со всеми успешно выполненными заданиями CI. Именно такую защитную сеть Вам и нужно получить.
Локальная отладка неуспешных шагов CI
Если запуск CI завершается с ошибкой, быстрее всего сначала воспроизвести её локально, а уже потом отправлять новый коммит. Два метода:
- Выполните точные команды из неуспешного шага в терминале — CI запускает обычную оболочку, поэтому команды можно воспроизвести копированием и вставкой.
- Используйте
act— инструмент, который запускает рабочие процессы GitHub Actions локально внутри Docker, обеспечивая максимально близкое соответствие окружению предоставленного исполнителя.
Распространённый источник ошибок, возникающих только в CI, — несоответствие версий инструментов на Вашем Mac (например, BSD find в macOS и GNU find в Ubuntu). Всегда тестируйте с флагами --posix или используйте act, чтобы локально запускать образ Ubuntu.
#!/usr/bin/env bash
# run_ci_locally.sh — mimic the CI lint step on your machine
set -euo pipefail
echo '=== ShellCheck ==='
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No .sh files found.'
else
shellcheck --severity=warning -x "${scripts[@]}"
echo "Linted ${#scripts[@]} file(s) — OK"
fi
echo '=== Bats ==='
./test/bats/bin/bats --recursive --timing tests/Проверка знаний: понятия конвейера CI
Проверьте, насколько хорошо Вы понимаете подключение ShellCheck и Bats к GitHub Actions.
Итоги: CI для оболочки с ShellCheck и Bats
В этом уроке Вы создали полный конвейер CI для проектов на Bash с использованием GitHub Actions. Были рассмотрены следующие темы:
- Основы GitHub Actions — YAML рабочих процессов хранится в
.github/workflows/, запуск происходит по событиям push и pull_request, а задания выполняются на исполнителяхubuntu-latest. - ShellCheck — предустановлен на исполнителях Ubuntu; используйте
findдля поиска скриптов и--severity=warning -xдля практической проверки качества. - Bats через подмодуль — зафиксируйте bats-core и вспомогательные средства как подмодули Git; восстанавливайте их в CI с помощью
submodules: recursiveв действии получения кода. - Порядок заданий — используйте
needs:, чтобы тесты запускались только после успешного анализа, обеспечивая быструю обратную связь и избегая напрасного расхода вычислительных ресурсов. - Защита ветвей — включите обязательные проверки состояния в настройках GitHub, чтобы ни один PR не попал в ветку без успешно выполненного CI.
- Локальное воспроизведение — копируйте команды CI непосредственно в терминал или используйте
act, чтобы отлаживать ошибки без дополнительных коммитов.
После настройки этого конвейера каждое изменение скриптов оболочки автоматически проверяется до того, как попадёт в основную ветку.
Часто задаваемые вопросы
Урок «Запуск тестов Shell в CI-конвейерах» бесплатный?
Да — полный текст урока «Запуск тестов Shell в CI-конвейерах» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Linux Command Line & Bash Scripting Mastery, подпишись на CoddyKit PRO. Курс Linux Command Line & Bash Scripting Mastery содержит 4 уроков всего.
Чему я научусь в уроке «Запуск тестов Shell в CI-конвейерах»?
Подключайте ShellCheck и Bats к GitHub Actions, чтобы каждая правка Shell проходила проверку только при успешных результатах Ты практикуешь Linux Command Line & Bash Scripting Mastery с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Linux Command Line & Bash Scripting Mastery?
Предыдущий опыт не требуется. Linux Command Line & Bash Scripting Mastery на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Запуск тестов Shell в CI-конвейерах»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Linux Command Line & Bash Scripting Mastery?
Да. Каждый урок Linux Command Line & Bash Scripting Mastery включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Модульное тестирование функций с Bats-core
- Имитация команд и заглушки внешних инструментов
- Тестовые данные, временные окружения и покрытие
- Запуск тестов Shell в CI-конвейерах