CI の Python を 3.9.6 から 3.11.9 へ上げた話(古いランタイムに縛られた自動化の移行)

  • #CI
  • #Python
  • #self-hosted runner
  • #composite action
CI の Python を 3.9.6 から 3.11.9 へ上げた話(古いランタイムに縛られた自動化の移行)

今回やったこと

self-hosted runner 上で動く自動化の Python 実行環境を、Apple 同梱の 3.9.6 の絶対パス参照から、pyenv で管理する 3.11.9 の解決へ移行した。対象は10個のワークフローで、単純に数字を置き換えるのではなく、共通の composite action から実行環境をそろえる構成にした。

この記事の実装ステータスは実装済みである。扱う範囲は、リポジトリ内に記録された runner とワークフローの移行方針であり、特定のホスト名や接続情報ではない。古いランタイムを使っていたという事実と、移行後に何を共通化したかを中心に整理する。

絶対パスが増えると、更新の単位が壊れる

10個のワークフローがそれぞれ /usr/bin/python3 のような固定の呼び出し方を持っていると、ランタイムの更新は10箇所の個別作業になる。今回の対象は Apple 同梱 Python 3.9.6 を絶対パスで参照していた。実行環境が変わったとき、古い参照が残ると、ワークフローごとに成功・失敗の条件がずれる。

問題は、古いバージョンを新しいバージョンへ置換するだけでは解決しない。どの Python を選ぶか、依存モジュールをいつ用意するか、選択に失敗したときにどう止めるかが、各ジョブに散らばったままになるからである。

変更前: workflow A ─┐
       workflow B ─┼─ 固定された Python 3.9.6
       workflow C ─┘

変更後: 各 workflow ──> composite action ──> pyenv の Python 3.11.9

更新の単位をワークフローから共通アクションへ移すと、実行環境の選択と依存関係の準備を一か所で確認できる。将来バージョンを更新する場合も、まず共通入口を変更し、各ジョブがその入口を使っているかを検査できる。

composite action に寄せた理由

今回の移行では、pyenv から 3.11.9 を解決し、必要なモジュールをジョブ冒頭で宣言する形にした。ここで大事なのは、環境を「たぶん入っているもの」として扱わないことだ。依存モジュールを明示すると、runner の状態にたまたま残っていたパッケージで通る状態を避けられる。

共通アクションには、少なくとも次の責務を持たせる。

責務目的
Python の解決各ジョブの参照先をそろえる
バージョンの固定runner ごとの差を減らす
必要モジュールの宣言不足を暗黙に見逃さない
ジョブ冒頭での実行後続ステップの前提を明確にする

一方で、composite action に寄せればすべてが自動で安全になるわけではない。各ワークフローが古い絶対パスを残していないか、共通アクションの呼び出し漏れがないかは別途確認が必要である。移行の完了条件を「共通アクションを作った」ではなく、「10個の対象が共通入口を通る」と定義する必要がある。

移行で得られた運用上の境界

今回の変更から得られる境界は、ランタイム更新とジョブ固有の処理を分けられたことだ。Python の版数やモジュールの準備は共通アクションが担当し、各ワークフローは自分の検査や生成処理に集中する。これにより、あるジョブだけが古い実行環境を使い続ける可能性をレビュー対象として見つけやすくなる。

また、古いランタイムを置き換える作業では、移行前の数値を記録しておくことが有効である。今回は 3.9.6 から 3.11.9 への変更であり、対象は10ワークフローだった。こうした移行単位を残しておけば、後から「何を、いくつ、どの入口へ寄せたか」を確認できる。バージョンだけを更新履歴に残しても、適用漏れの有無は分からない。

やってみてわかったのは、ランタイムの更新は依存関係の変更ではなく、運用の責任範囲を整理する機会になるということだ。固定パスを検索して置換するだけでは、次の更新で同じ問題が戻る。共通アクションを入口にし、版数と依存関係をそこで明示することで、更新の判断を一つの場所へ集約できる。

更新履歴

  • 2026-08-31: 初稿。CI 設定をもとに整理。

参照

  • .github/actions/m1-python/action.yml