はじめに #
第1回で開発環境ができたので、開発とCI環境の統一に注力しつつ、Gitlab CI/CDを利用できる様にします。
またGitlabにはただCI/CDを実行するだけでなく、開発を支援する機能がいくつも存在するのでこの利用法についても解説します。
1. 今回のdirectory構成 #
今回解説するコードはGitlabのrepositryのbranchseries-2
で公開しています。
.
├── app
│ ├── main.py
│ └── tests
│ └── small
│ └── test_main.py
├── compose.yml
├── dockerfiles
│ ├── ci
│ │ └── python.Dockerfile
│ └── Makefile
├── .gitlab-ci.yml ### NEW ###
├── Makefile
├── poetry.lock
├── pyproject.toml
└── README.md
2. lintとtest環境の整備 #
開発環境でlintとtestに成功したら.gitlab-ci.yml
を作成して、CI/CDを実施します。
.gitlab-ci.yml
# reference: https://docs.gitlab.com/ee/ci/caching/#cache-python-dependencies
stages:
- cache
- lint
- test
.python_cache:
image: $CI_REGISTRY_IMAGE/ci/python:latest
cache:
key:
files:
- poetry.lock
prefix: ci-python # Want to change the scope of the cache -> https://docs.gitlab.com/ee/ci/caching/#common-use-cases-for-caches
policy: pull
paths:
- .venv
# - mypy_cache # Uncomment if you use mypy
prepare_cache:
extends:
- .python_cache
stage: cache
cache:
policy: pull-push
script:
- poetry install -v
lint:
extends: .python_cache
stage: lint
script:
- make lint
small-test:
extends: .python_cache
stage: test
script:
- make small
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage.xml
3. 開発とCI環境の同一化 #
最初の注目ポイントは「開発とCI環境の同一化」です。第一回で紹介したようにここを注力ポイントにしているので、同じ環境で同じコマンドを使えるようにします。
$CI_REGISTRY_IMAGE/ci/python:latest
と開発環境と同じimageを使っている。make lint
やmake small
と開発環境と同じimageを使っている。
4. キャッシュによる高速化 #
次の注目ポイントは、prepare_cache
と.python_cache
です。
docker image内部にpipのパッケージを入れなかったので、CI/CD毎にpoetry install
を実施してpipパッケージをインストールする必要があります。
しかしlintで1回、testで1回、更にpipeline毎にinstallをするのは時間の無駄になります。
そこでjobprepare_cache
と、template.python_cache
作成してキャッシュ + 使い回すことで毎回のpoetry installを避けつつpoetry.lock
が変わったら再度installするという手法を利用します。
poetry install
を避けるという良いとこどりをします。
こちらの手法については以前解説していますので、以下を参照ください。
5. test結果の可視化 #
ここまででlintとtestは自動化できましたが、Gitlabには更に便利な機能が備わっています。
pytest
とpytest-cov
を利用していれば、簡単に導入ができるので設定しておきましょう。
5-1. coverageの%表示 #
coverageの%表記を正規表現で取得することで、m-rに記載することができます。 公式docsでは、正規表現の例やカバレッジヒストリーの確認方法などが他の活用法も紹介されています。
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
5-2. coverageの可視化 #
Cobertura XML形式のcoverageレポートをartifacts:reports:coverage_report
に保存することで、m-rの差分画面にcoverage結果を可視化することができます。
特にpytest-covを使っている場合であれば、--cov-report xml:coverage.xml
と指定するだけで、レポートを生成できるので非常に簡単に表示ができます。
For the coverage analysis to work, you have to provide a properly formatted Cobertura XML report to artifacts:reports:coverage_report. https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization/cobertura.html
5-3. Unit test結果の表示 #
Junit形式のUnit testレポートをartifacts:reports:junit
に配置することで、Pipeline画面にUnit testの結果を可視化することができます。
pytest-covを使っている場合は、--junitxml=junit.xml
と指定するだけで、こちらも簡単にGitlab上で確認することができます。
First, GitLab Runner uploads all JUnit report format XML files as artifacts to GitLab. Then, when you visit a merge request, GitLab starts comparing the head and base branch’s JUnit report format XML files https://docs.gitlab.com/ee/ci/testing/unit_test_reports.html
おわりに #
すごいsimpleですが、「開発環境で開発 → CI/CDでチェック → review」という一連の開発を実施できるようになりました。
次回は開発できた内容をリリースする手法を紹介したいと思います。
おまけ -github actionの場合- #
CIのjob毎にimageを使わずubuntu-latest
等のbase-imageに対して1つずつsetupコマンドを実施していくのが一般的の模様
- なぜimageを使わないかはgithubのdiscussionWhy use setup-python action instead of a python container?が参考になる。
poetryではなくuvだがenable-cache: true
によって、ubuntu-latest
に対して素早く展開できる模様
Makefileではなく専用のscriptを配置することで、開発環境とCI中で同じコマンドを実行するようになっている
jobs:
test-backend:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: "0.4.15"
enable-cache: true
- run: docker compose down -v --remove-orphans
- run: docker compose up -d db mailcatcher
- name: Migrate DB
run: uv run bash scripts/prestart.sh
working-directory: backend
- name: Run tests
run: uv run bash scripts/tests-start.sh "Coverage for ${{ github.sha }}"
working-directory: backend
- run: docker compose down -v --remove-orphans
- name: Store coverage files
uses: actions/upload-artifact@v4
with:
name: coverage-html
path: backend/htmlcov
include-hidden-files: true