Налаштовуємо GitLab CI для застосунку на Python

6 хв. читання

Інтро

У статті розберемося, як організувати процес неперервної інтеграції (Continuous Integration, CI) на GitLab для ваших Python-застосунків. Для наочного прикладу використаємо реальний застосунок.

Налаштовуємо GitLab CI для застосунку на Python

Після прочитання цієї статті ви матимете уявлення про те, як за допомогою CI виконати такі завдання для застосунків на Python:

  • модульне та функціональне тестування з pytest;
  • лінтинг за допомогою flake8;
  • статичний аналіз з pylint;
  • перевірка типів з mypy.

Що таке CI

Неперервна інтеграція передбачає постійне тестування робочих копій застосунку. Термін «тестування» в цьому контексті може означати:

  • інтеграційне тестування;
  • модульне тестування;
  • функціональне тестування;
  • статичний аналіз;
  • перевірку стилю (лінтинг);
  • динамічний аналіз.

Аби максимально полегшити виконання усього розмаїття тестів, краще запускати їх автоматично як частину управління конфігураціями (git). Тут на допомогу і приходить GitLab CI!

Початок роботи з GitLab CI

Перед тим як перейти безпосередньо до GitLab CI, оглянемо деякі основні терміни:

  • pipeline — набір тестів, які запускаються після одного git-коміту;
  • runner — сервер, який виконує тести при кожному коміті. GitLab передбачає власні runners, проте ви також можете використовувати свої сервери;
  • job — один тест, запущений у pipeline;
  • stage — група пов'язаних між собою тестів, запущених у pipeline.

Це зображення допоможе краще розібратися в наведених поняттях:

Налаштовуємо GitLab CI для застосунку на Python

GitLab використовує файл .gitlab-ci.yml для запуску CI pipeline у проекті. Цей файл слід шукати в директорії верхнього рівня.

Існує багато способів запустити тест в GitLab CI, проте рекомендується запускати тести в окремих контейнерах Docker. Насправді, час на запуск контейнера у порівнянні з загальним часом виконання тестів у CI дуже незначний.

Створюємо Job в GitLab CI

Перший job, який ми додамо до проекту — запуск лінтера (flake8). Для локального середовища розробки виконаємо таку команду:

$ flake8 --max-line-length=120 bild/*.py

Щоб перетворити цю команду на job у GitLab CI, треба внести до файлу .gitlab-ci.yml такі зміни:

image: "python:3.7"

before_script:
  - python --version
  - pip install -r requirements.txt

stages:
  - Static Analysis

flake8:
  stage: Static Analysis
  script:
  - flake8 --max-line-length=120 bild/*.py

Цей yaml-файл описує, що саме GitLab CI повинен запустити при кожному коміті, відправленому до репозиторію. Розберемо його крок за кроком.

У першому рядку (image: "python: 3.7") ми вказуємо GitLab CI використовувати Docker для виконання усіх тестів для цього проекту, зокрема використовувати для цього образ python:3.7, розташований на DockerHub.

Наступна частина before_script — набір команд, які запускаються в контейнері Docker перед початком кожного job. Дійсно зручно отримувати вже налаштований контейнер Docker, встановивши усі необхідні застосунку пакети Python.

Далі переходимо до stages, де визначено різні етапи в pipeline. Поки там є лише статичний аналіз, але трохи пізніше ми додамо наступний етап — тест. Етапи можна вважати зручним способом групування пов'язаних між собою job.

І наостанок: четверта частина (flake8) визначає сам job. Спочатку вказується етап (у нас це статичний аналіз), частиною якого буде job, а також команди, що будуть запущені в контейнері Docker для визначеного job. В нашому прикладі для Python-файлів застосунку буде запускатися лінтер flake8.

На цьому етапі зміни файлу .gitlab-ci.yml треба закомітити та запушити в GitLab:

git add .gitlab_ci.yml
git commit -m "Updated .gitlab_ci.yml"
git push origin master

GitLab CI визначить конфігураційний файл та використає його для запуску pipeline:

Налаштовуємо GitLab CI для застосунку на Python

Тож ми розібралися, як запустити CI процес для проекту на Python. Перейдемо до тестів.

Запуск тестів з pytest на GitLab CI

Для запуску модульних та функціональних тестів з pytest у середовищі розробки слід виконати таку команду в директорії верхнього рівня:

$ pytest

Перша спроба створити новий job для запуску pytest у файлі .gitlab-ci.yml мала такий вигляд:

image: "python:3.7"

before_script:
  - python --version
  - pip install -r requirements.txt

stages:
  - Static Analysis
  - Test

...

pytest:
  stage: Test
  script:
  - pytest 

Однак це не спрацювало, оскільки pytest не міг знайти модуль bild (тобто сирцевий код) для тесту:

Налаштовуємо GitLab CI для застосунку на Python
$ pytest
========================= test session starts ==========================
platform linux -- Python 3.7.3, pytest-4.5.0, py-1.5.4, pluggy-0.11.0
rootdir: /builds/patkennedy79/bild, inifile: pytest.ini
plugins: datafiles-2.0
collected 0 items / 3 errors
============================ ERRORS ====================================
___________ ERROR collecting tests/functional/test_bild.py _____________
ImportError while importing test module '/builds/patkennedy79/bild/tests/functional/test_bild.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/functional/test_bild.py:4: in <module>
    from bild.directory import Directory
E   ModuleNotFoundError: No module named 'bild'
...
==================== 3 error in 0.24 seconds ======================
ERROR: Job failed: exit code 1

Проблема полягала в тому, що модуль bild був недоступний для файлів test_*.py, тому що директорія верхнього рівня в проекті не була визначена в системному шляху:

$ python -c "import sys;print(sys.path)"
['', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages']

Розв'язати таку проблему можна, додавши директорію верхнього рівня до системного шляху всередині контейнера Docker потрібного job:

pytest:
  stage: Test
  script:
  - pwd
  - ls -l
  - export PYTHONPATH="$PYTHONPATH:."
  - python -c "import sys;print(sys.path)"
  - pytest

Тепер, як бачимо, job може успішно виконуватися:

$ pwd
/builds/patkennedy79/bild
$ export PYTHONPATH="$PYTHONPATH:."
$ python -c "import sys;print(sys.path)"
['', '/builds/patkennedy79/bild', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages']
Налаштовуємо GitLab CI для застосунку на Python

Остаточна конфігурація GitLab CI

Такий вигляд матиме остаточний файл .gitlab-ci.yml, що запускає jobs статичного аналізу (flake8, mypy, pylint) та тести (pytest):

image: "python:3.7"

before_script:
  - python --version
  - pip install -r requirements.txt

stages:
  - Static Analysis
  - Test

mypy:
  stage: Static Analysis
  script:
  - pwd
  - ls -l
  - python -m mypy bild/file.py
  - python -m mypy bild/directory.py

flake8:
  stage: Static Analysis
  script:
  - flake8 --max-line-length=120 bild/*.py

pylint:
  stage: Static Analysis
  allow_failure: true
  script:
  - pylint -d C0301 bild/*.py

unit_test:
  stage: Test
  script:
  - pwd
  - ls -l
  - export PYTHONPATH="$PYTHONPATH:."
  - python -c "import sys;print(sys.path)"
  - pytest

Результат з GitLab CI:

Налаштовуємо GitLab CI для застосунку на Python

Зверніть увагу, що pylint висуває деякі попередження для нормальної роботи; їх можна позбутися, налаштувавши allow_failure:

pylint:
  stage: Static Analysis
  allow_failure: true
  script:
  - pylint -d C0301 bild/*.py
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.5K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація