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

CI/CD

  • CI (Continuous Integration) — автоматические проверки. При каждом изменении сервер сам запускает тесты и линтер.
  • CD (Continuous Delivery / Deployment) — автоматическая выкладка. После проверок и merge код сам попадает в продакшен.

Без CI проверка зависит от человека. Кто-то забыл запустить тест. Кто-то не заметил дубли. С CI проверка работает каждый раз, одинаково.

  • Не сломать продакшен. Битый SQL не дойдёт до main. Красный CI блокирует merge.
  • Каждая версия хранится в Git. Любое изменение можно найти и откатить.
  • Коллеги смотрят код. Они видят diff, результаты проверок и пишут замечания.
  • Проверки делает машина. Человек в ревью думает о логике, а не о запятых и стиле.

Путь изменения выглядит так:

push в ветку → CI: линтер + тесты → ревью коллег → merge в main → CD: выкладка
│ красный? merge закрыт

Начните с малого. Две проверки закрывают большую часть проблем:

ПроверкаИнструментЧто ловит
ЛинтерsqlfluffОшибки синтаксиса и разный стиль SQL
ТестыPython + DuckDB (или dbt test)Запрос ничего не вернул, в данных дубли или отрицательные суммы

Для dbt-проектов обычно запускают dbt build на тестовых данных. Глубже в это не идём. С опытом вы добавите свои проверки.

Файл tests/test_revenue.py запускает запрос на маленьком CSV и проверяет результат:

import duckdb
con = duckdb.connect()
con.execute("create table orders as select * from 'data/orders.csv'")
rows = con.execute(open("models/revenue.sql").read()).fetchall()
assert len(rows) > 0, "revenue model returns no rows"
assert all(amount >= 0 for _, amount in rows), "negative amount"
print("OK:", len(rows), "rows")

Файл data/orders.csv — маленький тестовый набор:

order_id,amount,status
1,100,completed
2,50,cancelled
3,70,completed

Этот файл маленький, поэтому его хранят в Git. CI берёт данные из репозитория.

Локально проверка запускается так:

Окно терминала
pip install duckdb sqlfluff
sqlfluff lint models --dialect duckdb
python tests/test_revenue.py

Создайте файл .github/workflows/ci.yml:

name: CI
on:
pull_request:
push:
branches: [main]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: pip install duckdb sqlfluff
- name: Lint SQL
run: sqlfluff lint models --dialect duckdb
- name: Test models
run: python tests/test_revenue.py

Как читать файл:

  • on — когда запускать: при каждом Pull Request и при push в main.
  • jobs — что делать. Здесь одна задача checks.
  • steps — шаги по порядку: забрать код, поставить Python, поставить пакеты, линтер, тест.

Если любой шаг падает, проверка красная. В Pull Request появляется красный крестик.

Результаты смотрят на вкладке Actions. Каждая строка — один запуск: какой коммит, какая ветка, сколько шёл, зелёный или красный.

Вкладка Actions на GitHub: список запусков workflow, у каждого зелёная галочка, ветка main и время работы

Откройте запуск, чтобы увидеть его задачи (jobs). Красную задачу открывают и читают лог: там написано, какой шаг упал и почему.

То же самое для GitLab. Файл .gitlab-ci.yml в корне репозитория:

image: python:3.12
stages:
- check
checks:
stage: check
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- pip install duckdb sqlfluff
- sqlfluff lint models --dialect duckdb
- python tests/test_revenue.py

Идея та же. Меняется только формат файла.

Результат в GitLab: Build → Pipelines. Задачи собраны в колонки по этапам (stages). Этапы идут слева направо. Следующий этап начнётся, только если предыдущий зелёный.

Pipeline в GitLab: этапы build, application-security-testing, qa и test в колонках, у каждой задачи зелёная галочка

В большом проекте десятки задач. В нашем — одна задача checks в одном этапе.

GitHub ActionsGitLab CI
Файл.github/workflows/ci.yml.gitlab-ci.yml
Запускon:rules:
Шагиsteps:script:
Где смотреть результатВкладка Actions и статус в PRРаздел Pipelines и статус в MR

CI бесполезен, если его можно обойти. Включите защиту ветки main:

  • GitHub: Settings → Branches → Add branch ruleset. Включите Require a pull request before merging и Require status checks to pass.
  • GitLab: Settings → Repository → Protected branches. В Settings → Merge requests включите Pipelines must succeed.

После этого напрямую в main писать нельзя. Только через PR или MR с зелёным CI и approve.

CD — следующий шаг. После merge в main пайплайн сам выкладывает изменения. Что именно выкладывается, зависит от проекта:

ПроектЧто делает CD
AirflowКопирует DAG на сервер или в облако
dbtЗапускает dbt build на продакшен-схеме
TerraformПрименяет изменения инфраструктуры
DockerСобирает образ и выкладывает его

Пример CD — этот сайт. Его код лежит на GitHub. После каждого merge в main workflow deploy.yml сам собирает сайт и выкладывает его:

Запуск workflow deploy.yml на GitHub: задачи build, deploy и indexnow соединены стрелками, все зелёные, у deploy ссылка на сайт

  • build — собирает сайт. Если сборка упала, дальше ничего не идёт.
  • deploy — выкладывает сайт. Под задачей ссылка на результат.
  • indexnow — сообщает поисковикам о новых страницах.

Никто не копирует файлы на сервер руками. Merge — и через пару минут изменения на сайте.

На старте CD не нужен. Сначала настройте хороший CI. Для CD понадобятся секреты (пароли и ключи). Их хранят в Secrets платформы (Settings → Secrets), а не в коде.


Добавьте в sql-reports файлы data/orders.csv и tests/test_revenue.py из урока. Запустите проверки локально.

Проверка: python tests/test_revenue.py печатает OK: 2 rows. sqlfluff lint не показывает ошибок.

Если sqlfluff ругается на стиль, поправьте models/revenue.sql или запустите sqlfluff fix models --dialect duckdb.

Подсказка

Хороший вид запроса для sqlfluff:

select
order_id,
amount
from orders
where status = 'completed'

Добавьте .github/workflows/ci.yml. Сделайте новую ветку, коммит и Pull Request.

Проверка: на странице Pull Request есть проверка checks, и она зелёная.

Решение
Окно терминала
git switch -c add-ci
mkdir -p .github/workflows
# создайте ci.yml с текстом из урока
git add .github tests data
git commit -m "Add CI with sqlfluff and tests"
git push -u origin add-ci

Затем откройте Pull Request на GitHub.

В ветке сделайте ошибку: измените запрос так, чтобы в нём не осталось строк, например where status = 'unknown'. Сделайте коммит и git push.

Проверка: проверка в Pull Request стала красной. В логе видно revenue model returns no rows.

Верните правильный запрос, сделайте коммит и push. Проверка снова зелёная.

Решение
Окно терминала
# правим models/revenue.sql
git commit -am "Break filter on purpose"
git push
# смотрим красный CI, затем возвращаем:
git revert HEAD --no-edit
git push

Включите правило защиты main: только через Pull Request и только с зелёной проверкой.

Проверка: git push origin main напрямую отклоняется.

Скопируйте тот же проект в GitLab. Добавьте .gitlab-ci.yml из урока. Откройте Merge Request.

Проверка: в Merge Request виден зелёный pipeline.