【実務・中級編】Poetryとuvを組み合わせた『ハイブリッドCI』の構築:ビルドはPoetry、テスト実行はuvの高速キャッシュを活かす適材適所の戦術 – ビルド・パッケージ管理ツール生産性向上バイブル

Poetryとuvを融合せよ:CI/CDのビルドネックを粉砕する「ハイブリッド戦術」の全貌

テックリードの皆さん、日々のCIパイプラインの待ち時間にどれだけの開発コストを溶かしているだろうか。

Pythonのパッケージ管理において、Poetryはその堅牢な依存関係解決(`poetry.lock`)とモデリングの美しさから、多くのモダンなチームで標準採用されている。しかし、大規模なプロジェクトになればなるほど、PoetryのCI上での「依存関係のインストール(`poetry install`)」の遅さにフラストレーションが溜まっているはずだ。仮想環境の構築、巨大なパッケージ群のホイールビルド、そして依存関係グラフの構築にかかるオーバーヘッドは、シリアルなボトルネックとして常に開発者体験(DX)を蝕んでいる。

そこで本記事では、Rust製パフォーマーである`uv`をCIの特定のフェーズに組み込み、「ビルドとロックはPoetry、爆速な環境構築とテスト実行はuv」といういいとこ取りをした、極限まで最適化されたハイブリッドCI戦術を解説する。

表面的なインストール手順ではない。なぜこの組み合わせが機能するのか、内部でロックファイルがどう解釈されるのか、そして実務の現場で即座にコピー&ペーストして使えるプロダクションレディな設定の全貌を、アーキテクトの視点から紐解いていく。

—

1. なぜPoetryとuvを組み合わせるのか?(アーキテクチャの思想)

このハイブリッド戦術の根幹にあるのは、それぞれのツールの「得意領域」を極限まで引き剥がし、パイプライン上で適材適所に配置するという思想だ。

  • Poetryの役割:厳格な真実のソース(Source of Truth)
  • `pyproject.toml` と `poetry.lock` の整合性維持。
  • セマンティックバージョニングに基づく厳密な依存関係解決アルゴリズム。
  • PyPIへのセキュアなパブリッシングとビルド。
  • uvの役割:極限のI/O・プロセス高速化エンジン
  • Rustによるマルチスレッド並列ダウンロード。
  • OSレベルのグローバルキャッシュ(ハードリンク/コピーオンライタ活用)による圧倒的なディスクI/O削減。
  • 仮想環境(venv)への数秒でのパッケージ群の爆速展開。

Poetryが生成する `poetry.lock` は、Pipenv時代からの洗練された依存関係グラフを保持している。これを捨て去る必要はない。むしろ、「依存関係の解決」というCPUバウンドで重い処理は信頼性の高いPoetryに任せ、解決された結果(ロックファイル)を元にした「重労働なダウンロードとインストール」をuvに代行させる。これがハイブリッドCIのコアコンセプトである。

—

2. チーム開発を加速する:Poetryの隠れた神設定とプラグイン

ハイブリッドCIに入る前に、ローカル開発環境およびPoetryのベースラインを最高効率にチューニングしておく必要がある。ここを疎かにすると、いくらCIを速くしてもローカルでの再現性が担保できなくなる。

必須のPoetryプラグイン:`poetry-plugin-up`

バージョンアップの追従を自動化する。手動で `pyproject.toml` を書き換える無駄な時間を排除する。

プラグインのインストール
poetry self add poetry-plugin-up

チーム共有のための `pyproject.toml` ベストプラクティス

仮想環境をプロジェクト直下(`.venv`)に作成させ、IDE(VSCodeやPyCharm)とのインテグレーションを完璧にする設定を強制する。

[tool.poetry]
name = “enterprise-backend-service”
version = “1.0.0”
description = “High-performance microservice with Poetry & uv hybrid CI”
authors = [“Architecture Lead “]
readme = “README.md”
packages = [{include = “app”, from = “src”}]

[tool.poetry.dependencies]
python = “^3.11”
fastapi = “^0.110.0”
uvicorn = {extras = [“standard”], version = “^0.28.0”}
pydantic = “^2.6.4”
sqlalchemy = “^2.0.28”

[tool.poetry.group.dev.dependencies]
pytest = “^8.1.0”
pytest-cov = “^4.1.0”
ruff = “^0.2.2”

[build-system]
requires = [“poetry-core”]
build-backend = “poetry.core.masonry.api”

— アーキテクト推奨:開発効率化設定 —
[tool.poetry.settings]
仮想環境をプロジェクトディレクトリ配下の .venv に確実に作成する
virtualenvs.in-project = true
グローバル環境の汚染を防ぐため、存在しない場合は作成する
virtualenvs.create = true

—

3. 実装:GitHub ActionsによるハイブリッドCIパイプライン

それでは、GitHub Actionsを用いたハイブリッドCIの全コードを公開する。
このワークフローのキモは、「Poetryでロックファイルからrequirements.txt形式等への変換、あるいは直接uvによるロックファイルの解釈を行い、キャッシュを最大効率でヒットさせる」点にある。

最新の `uv` は、Poetryの `poetry.lock` を直接読み込んで高速インストールする機能を備えている。つまり、中間ファイルを生成する必要すらない。

GitHub Actionsワークフロー設定例 (`.github/workflows/ci.yml`)

name: Hybrid CI (Poetry + uv)

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
name: Run Tests with uv acceleration
runs-on: ubuntu-latest

steps:
# 1. リポジトリのチェックアウト(シャローコピーで高速化)

  • name: Checkout repository

uses: actions/checkout@v4

# 2. Python環境のセットアップ(バージョン固定)

  • name: Set up Python 3.11

uses: actions/setup-python@v5
with:
python-version: ‘3.11’

# 3. 超高速なRust製ツール ‘uv’ のセットアップ

  • name: Set up uv

uses: astral-sh/setup-uv@v5
with:
# uv自体のバージョンをピン留めして挙動を安定させる
version: “latest”
enable-cache: true
# キャッシュキーにpoetry.lockのハッシュを組み込むことで、依存関係変更時のみキャッシュを破棄
cache-dependency-path: “poetry.lock”

# 4. Poetryのインストール(依存関係の解決・ロックファイル検証用)
# ※依存関係の「インストール」には使わず、Poetryエコシステムとの互換性を保つために用意

  • name: Install Poetry

uses: snok/install-poetry@v1
with:
version: 1.8.2
virtualenvs-create: true
virtualenvs-in-project: true

# 5. 【検証フェーズ】Poetryでロックファイルの整合性をチェック
# ロックファイルが古くないか、pyproject.tomlと同期しているかをCIで担保する

  • name: Verify Poetry lock file is up-to-date

run: poetry lock –check

# 6. 【ハイブリッドインストールフェーズ】uvによる爆速仮想環境構築
# Poetryが生成した .venv に対して、uvがpoetry.lockを解釈して数秒でパッケージを流し込む

  • name: Install dependencies with uv using Poetry lock

run: |
# uv sync は poetry.lock を直接読み込み、プロジェクトの .venv を構築できる
uv sync –frozen –all-extras –dev

# 7. 【実行フェーズ】テストの実行
# uvが構築した仮想環境(.venv)のアクティベート、または直接パスを指定して実行

  • name: Run pytest with coverage

run: |
# .venv/bin/pytest を直接叩くことでシェル環境の差異を排除
.venv/bin/pytest tests/ –cov=src –cov-report=xml

# 8. テストカバレッジのアップロード(必要に応じて)

  • name: Upload coverage to Codecov

uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
fail_ci_if_error: false

—

4. なぜこの構成がチート級に速いのか?(内部挙動の解説)

上記のワークフローが従来の `poetry install` のみ構成と比べて圧倒的なスピード(通常、インストール時間が 5分の1から10分の1 に短縮)を誇る理由を、内部データの動きから解説する。

1. 依存関係解決の分離

  • `poetry lock –check` は、厳格なPoetryのエンジンを使い、依存関係の競合がないかを完璧にチェックする。ここで安全性は100%担保される。

2. uv sync と poetry.lock のネイティブ統合

  • `uv sync –frozen` コマンドは、`poetry.lock` をパースし、追加の変換プロセスなしに直接依存関係のグラフを構築する。 `–frozen` フラグをつけることで、CI上で意図しないロックファイルの書き換わりを防ぎつつ、厳密な再現性を維持する。

3. グローバルキャッシュ層の共有

  • `astral-sh/setup-uv` アクションと `enable-cache: true` により、GitHub Actionsのキャッシュストレージとランナー上の `~/.cache/uv` が密に連携する。
  • 一度ダウンロードされたホイール(Wheel)は、OSのハードリンク機構を利用してプロジェクトの `.venv/lib/…` へ一瞬で展開されるため、実質的なファイルコピーのオーバーヘッドがほぼゼロになる。

—

5. チーム開発における運用ルールとアンチパターン

このハイブリッド戦術を現場に導入するにあたり、テックリードとしてチームメンバーに徹底すべき「運用ルール」を定義する。

運用ルール 1: ローカルでも `uv` を活用した高速化の選択肢

開発者個人のローカル環境でも、Poetryの遅さにストレスを感じている場合は `uv pip` や `uv sync` を併用して構わない。ただし、「新しいパッケージの追加・削除(`poetry add`, `poetry remove`)」は必ずPoetry経由で行うこと。これによって `poetry.lock` の整合性が常に保たれる。

運用ルール 2: CIでの `poetry install` の完全禁止

CI上では絶対に `poetry install` を実行しないこと。Poetryのインストール処理は内部でpipをラップして直列的にダウンロードを行うため、キャッシュが効いていてもuvの足元にも及ばない。依存関係の同期は常に `uv sync` に託す。

避けるべきアンチパターン

  • ❌ `requirements.txt` を手動でexportして運用する
  • Poetryを使っている意味が完全に失われ、依存関係の多重管理地獄(ドリフト)に陥る。必ず `poetry.lock` を単一の真実のソースとしてuvに読ませること。
  • ❌ キャッシュキーにOSやPythonのマイナーバージョンを含め忘れる
  • 異なる環境間でキャッシュがコンタミネーション(汚染)を起こし、セグメンテーション違反やバイナリの不整合を引き起こす原因になる。公式の `setup-uv` アクションにキャッシュ管理を任せるのが最も安全である。

—

結び:開発者の時間を奪うな

CIの待ち時間は、開発者の集中力を途切れさせ、組織のデリバリー速度を鈍らせる最大の隠れた負債である。「Poetryの堅牢なエコシステム」と「uvの圧倒的な物理的スピード」。この2つを組み合わせたハイブリッドCI戦術は、妥協なき品質と、極限のスピードを同時に手に入れるための現時点での最適解である。

明日からのパイプラインにこの構成を組み込み、ビルド待ちのコーヒーブレイクを不要なものにしてほしい。あなたのチームの生産性は、ここから劇的に跳ね上がる。

タイトルとURLをコピーしました