hamakou108 blog

CLI ツール "depwatch" をリリースした

Cover Image for CLI ツール "depwatch" をリリースした

depwatch という CLI ツールを OSS としてリリースした。

hamakou108/depwatch - GitHub

開発の背景や具体的な用途などについて記す。

何に使えるか

Four Keys のデプロイ頻度や変更のリードタイムを集計するための生データの収集に利用できる。 README に従ってコマンドを実行すると、

first_committed_at,merged_at,deployed_at
2023-02-25T00:48:18+00:00,2023-02-25T00:57:06+00:00,2023-02-25T00:58:11+00:00
2023-02-25T00:46:52+00:00,2023-02-25T00:54:05+00:00,2023-02-25T00:55:12+00:00
2023-02-25T00:43:47+00:00,2023-02-25T00:45:33+00:00,2023-02-25T00:46:39+00:00

のようなデータが得られる。これを BigQuery などでクエリを書いて分析するような用途を想定している。

なぜ開発したか

開発のきっかけは現職のチームで進めることになった Four Keys のモニタリング基盤の構築だ。 Four Keys に注目するようになった背景については現職の開発者ブログに書いたので省くが、ざっくり言うと変更のリードタイムを計測するために Git のリビジョン作成時刻やデプロイの完了時刻をどのようにして取得するかが課題になっていた。

変更のリードタイムは DORA の2022年のレポートによると次のように定義されている。

For the primary application or service you work on, what is your lead time for changes (i.e., how long does it take to go from code committed to code successfully running in production)?

現職のチームでは「PR の最初のリビジョンが作成されてから CI の本番環境デプロイのワークフローが完了するまで」を定義にしていた [^1] 。 起点となる code committed については GitHub のようなリポジトリ管理プラットフォームから Git のリビジョン時刻のデータを、終点となる code successfully running in production については CircleCI のような CI プラットフォームからデプロイワークフローの完了時刻のデータを収集する必要がある。

まず Git のリビジョン作成時刻を収集するためのツールは幾つか見つかった。

しかしこれらのツールではデプロイの完了時刻を収集できなかったため、 CI プラットフォームの API を呼び出してデータを取得するような別の方法が必要だった。

これらのツールを fork してデプロイ時刻を収集する機能を追加するか、あるいは自分で新たにツールを開発するかで悩んだが、今回は後者を選択した。前述したように変更のリードタイムの定義は一意に定まっていないため、個々のチームによってデータ収集ツールに求めるニーズは変わってくるように思う。すると fork 戦略の場合、既に存在する機能の実装をショートカットできるメリットがある一方で、ニーズの変化に応えることが難しくなるデメリットがある。このメリットとデメリットを天秤にかけた結果、デメリットの方が大きいと判断して自分でツールを開発することにした。加えて OSS をしっかりと開発したことがなかったので練習がてら一度作ってみるのも良い経験になるかなという気持ちも後押しとなった。

どのように開発してきたか

2月14日から開発を開始し、3月4日にリリースした。開発中の工夫や苦労について幾つかピックアップする。

PDM によるパッケージ管理

depwatch では言語として Python を採用している [^2] 。昨今 Python のパッケージマネージャのトレンドは目まぐるしく変化しており、軽く調べただけでも次のようなツールがあった。

調べていく過程で PEP 582 というプロポーザルがあることを知った。 PEP 582 はざっくり言うとワーキングディレクトリ直下に __pypackages__ というディレクトリを配置してパッケージを管理するというもの。以前 pipenv を使ってパッケージ管理していた頃、仮想環境のパスを IDE に認識させるために毎度設定で苦労していたので、この仕様に準拠しているツールを選択して苦労を減らそう (伏線) と考え、準拠しているツールの中でも現在勢いがありそうな PDM を使ってみることにした。ちなみに各ツールのリポジトリのスター数の遷移は次のようになっており、勢いがあるのは Poetry、 PDM、 Hatch 辺りのようだ。

Python のパッケージ管理ツールのスター数の遷移
Python のパッケージ管理ツールのスター数の遷移

https://star-history.com/#pypa/pipenv&python-poetry/poetry&pdm-project/pdm&David-OConnor/pyflow&pypa/hatch&Date

実際に PDM を使ってみたところ、幾つかハマりどころがあった。

まず python コマンドの実行時、 PDM でインストールしたパッケージについて ModuleNotFoundError が発生し、小一時間ハマった。これは pdm run python のように実行しないと sys.path__pypackages__ が追加されず、パッケージを認識できないという事象のようだった。詳細については Zenn に次の記事を書いた。

PDM を使用したプロジェクトで ModuleNotFoundError が発生する - Zenn

また PEP 582 に沿って __pypackages__ 方式で開発していた [^3] ところ、 VS Code でも IntelliJ IDEA でもテスト時に ModuleNotFoundError が発生する問題に行き当たり、この解決法をどうしても見つけられなかった [^4] 。幸い PDM には vitrualenv を使ってパッケージ管理する方法も提供されていたので、そちらに移行した。

後々確認したら PEP 582 に対応する Python のバージョンは 3.12 だった。開発時の最新バージョンは 3.11.2 だったので、行き詰まるのは道理だった。 PEP を参考にするときは対応している Python バージョンもちゃんと見ようね、という学びを得た (伏線回収)。

Linter

Linter を選択する際は次の記事を大いに参考にした。

Python Linter Comparison 2022: Pylint vs Pyflakes vs Flake8 vs autopep8 vs Bandit vs Prospector vs Pylama vs Pyroma vs Black vs Mypy vs Radon vs mccabe - The Invent with Python Blog

型チェックは mypy がデファクトスタンダードのようだったが、 Error Linter と Style Linter (Code Formatter) は正直どれでも良かったので、無難そうな PyflakesBlack を選択した。 Security linter や Docstring linter は時期尚早と判断して導入しなかった。

ちなみに Linter でスター数を比較した結果は次のようになっている。

Python の Linter のスター数の遷移
Python の Linter のスター数の遷移

https://star-history.com/#PyCQA/pyflakes&PyCQA/pylint&PyCQA/pycodestyle&psf/black&peter-evans/autopep8&asottile/pyupgrade&Date

また GitHub Actions を使って自動化を行っている。詳細については Zenn に次の記事を書いた。

PDM + GitHub Actions でテストとパッケージングを自動化する - Zenn

パッケージング

今回のツールは CLI ツールなので、 pyproject.toml に次の設定を追加して depwatch でコマンド実行できるようにしている。

[project.scripts]
depwatch = "depwatch.main:main_cli"

PyPI にアップロードする前にローカル環境で動作を確認した。 pdm build でビルドし、 Docker コンテナ上で解凍したパッケージをローカルインストールしてコマンドを実行する。

$ python -m pip install ./depwatch-0.1.0
$ GITHUB_ACCESS_TOKEN=YOUR_TOKEN CIRCLECI_ACCESS_TOKEN=YOUR_TOKEN depwatch user_name/repo_name

PyPI へのアップロードは GitHub Actions を使って自動化を行っている。詳細については上述した Zenn の記事を参照。

ChatGPT の活用

今回の開発では ChatGPT に大いに助けてもらった。具体的には OSS の名前の候補を ChatGPT に考えてもらったり、 README の雛形を作ってもらったりした。

OSS の名前の候補を ChatGPT に挙げさせている様子のスクリーンショット
OSS の名前を ChatGPT と共に考えてみる

目安として5分くらい悩んだら ChatGPT に助けを求めるのが良さそう。今後は急速に AI の守備範囲は広がると思うので、こういったツールは使いこなせるようになっておきたい。

最後に

記事公開時点で実装が足りていない点は issues にしてある。しばらくの間はこれらの開発を続ける予定だ。

今回 (個人的には) しっかりと OSS の開発に取り組んでみて思ったのは、コードを書くための学びはもちろん、コードと直接関係のない部分でも多くの学びを得られるということだ。 Python の言語仕様やパッケージ管理ツールのトレンド、使用するライブラリの実装上の工夫 [^5] などなど...。以前に @yosuke_furukawa さんが登壇で息を吐き続けることに触れており、個人的に強く印象に残っている。今回その価値が少しだけ実感を伴って理解できた気がする。

[^1]: 変更のリードタイムで code committed をどのタイミングとするかは一意に定まっていないようである。変更のリードタイムのバリエーションに関しては junichiro さんの記事に詳しく書かれている。また DORA が公開している計測プラットフォーム構築用のリポジトリ dora-team/fourkeys には Four Keys の計算ロジックも含まれているようだが、自分のコードリーディングスキル不足により定義を読み取ることはできなかった...。 [^2]: 開発当初は情報の収集だけでなく計算も視野に入れていたので、数値計算系のエコシステムが充実している Python を選択した。 [^3]: 詳細については Zenn に「Python + PDM のプロジェクトを IntelliJ IDEA と VS Code で開発する」という記事を書いた。 [^4]: VS Code については PDM のリポジトリに同様の問題に対する issue を見つけたが、そこに記載されている workaround を真似しても解決することができなかった。 [^5]: 特に PyGithub/PyGithub では Iterator パターンを使って API の呼び出しを隠蔽することで、利用側が API のページネーションを意識することなく単にループ処理を書くことができるように設計されており、今後の開発時に参考にできそうだった。