Ruby バージョン自動更新システムの実装
rails/devcontainer に、Ruby の新バージョンをチェックして自動的にプルリクエストを作成する GitHub Actions ワークフローが追加されました。これにより、手動での Ruby バージョン管理作業が不要になります。
背景
これまで Ruby の新バージョンがリリースされた際、devcontainer の設定を手動で更新する必要がありました。具体的には、.github/ruby-versions.json への新バージョン追加、features/src/ruby/devcontainer-feature.json のバージョン更新、テストファイルの修正といった一連の作業を手作業で行っていました。
本 PR は、この手動プロセスを完全に自動化するワークフローとコマンド群を導入しています。rbenv/ruby-build のリリース情報を監視し、新バージョンを検出すると自動的に設定を更新して PR を作成する仕組みです。
技術的な変更
アーキテクチャの再設計: 既存の lib/add_ruby_version.rb が、責任を分離した複数のサービスクラスに分割されました。
変更前のアーキテクチャ:
module AddRubyVersion
class Runner
def call
# バージョン追加ロジック
# コンソール出力
# ファイル更新
# すべてが1つのクラスに混在
end
end
end
変更後のアーキテクチャ:
本 PR では、以下の 4 つの層に機能を分離しています:
-
ビジネスロジック層:
lib/ruby_version_adder.rbが純粋なバージョン追加ロジックを担当 -
外部連携層:
lib/ruby_version_checker.rbが rbenv/ruby-build からのバージョン取得を、lib/ruby_version_pr_creator.rbが PR 作成を担当 -
プレゼンテーション層:
lib/commands/配下のクラスが CLI 出力を担当 -
共通基盤:
lib/console.rbが出力フォーマットを集約
GitHub Actions ワークフロー: .github/workflows/check-ruby-versions.yaml が追加され、毎日午前 2 時(UTC)に自動実行されます。
schedule:
- cron: '0 2 * * *' # Daily at 2 AM UTC
workflow_dispatch: # Allow manual triggers
ワークフローは bin/check-and-create-pr スクリプトを実行し、その出力から PR 番号を取得します:
- name: Check for new versions and create PR
run: |
PR_NUMBER=$(bin/check-and-create-pr)
if [ -n "$PR_NUMBER" ]; then
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
fi
PR が作成されると、自動マージ が有効化されます:
- name: Enable auto-merge
if: steps.check-and-create.outputs.pr_number != ''
run: |
gh pr merge ${{ steps.check-and-create.outputs.pr_number }} --auto --squash
バージョンチェック機構: RubyVersionChecker は Octokit gem を使用して rbenv/ruby-build のリリース情報を取得します。
# Minimum Ruby version to consider (non-EOL versions only)
MIN_RUBY_VERSION = "3.2.0"
# Ruby-build repository
RUBY_BUILD_REPO = "rbenv/ruby-build"
# Version format pattern (only stable releases: x.y.z)
VERSION_PATTERN = /\A\d+\.\d+\.\d+\z/
EOL を迎えたバージョン(3.2.0 未満)は除外され、プレリリース版(rc、preview など)も対象外となります。
PR 作成機構: RubyVersionPRCreator は Git 操作と GitHub API 呼び出しを組み合わせて PR を作成します。
ブランチ作成、コミット、プッシュを Git コマンドで実行し、PR の作成には Octokit を使用します。作成された PR には、追加されたバージョンのリストと変更されたファイルの一覧が自動的に記載されます。
依存関係の追加: 自動化に必要な gem が Gemfile に追加されました:
- octokit: GitHub API 操作用
- mocha: テストのモック作成用
- webmock: HTTP リクエストのスタブ用
テストの拡張: 各サービスクラスに対応する独立したテストファイルが作成されています:
-
test/ruby_version_adder_test.rb: ビジネスロジックのテスト(旧test/add_ruby_version_test.rbをリネーム) -
test/ruby_version_checker_test.rb: バージョンチェック機能のテスト(WebMock でモック化) -
test/ruby_version_pr_creator_test.rb: PR 作成機能のテスト(Mocha と WebMock でモック化) -
test/commands/add_ruby_version_test.rb: CLI コマンドの出力テスト -
test/commands/check_and_create_pr_test.rb: ワークフロー全体の統合テスト
テストは外部依存を完全にモック化しているため、ネットワーク接続なしで高速に実行できます。
設計判断
単一責任の原則の適用: 既存の AddRubyVersion モジュールを複数のサービスクラスに分割する設計が採用されました。RubyVersionAdder(バージョン追加)、RubyVersionChecker(バージョンチェック)、RubyVersionPRCreator(PR 作成)が、それぞれ独立したテスタブルなユニットとして実装されています。
lib/commands/ 配下のクラスは、これらのサービスを組み合わせて CLI 向けの出力を提供するファサードとして機能します。ビジネスロジックとプレゼンテーションの分離により、サービスクラスは別の文脈(API、Webフック)でも再利用可能です。
共通モジュールの抽出: 出力フォーマット(色、絵文字)を Console モジュールに集約する判断がなされました。include Console することで、すべてのクラスが統一されたフォーマットを使用できます:
module Console
COLORS = {
reset: "\e[0m",
green: "\e[32m",
# ...
}.freeze
EMOJI = {
search: "🔍",
edit: "📝",
# ...
}.freeze
end
この設計により、出力スタイルの一元管理が実現されています。
自動マージの組み込み: PR 作成後に自動マージを有効化する設計が選ばれました。gh pr merge --auto --squash により、CI が成功すれば人手を介さずマージが完了します。
これは Ruby のマイナーバージョンアップが通常は破壊的変更を含まないという前提に基づいています。ただし、ワークフローが失敗した場合は Issue を自動作成する仕組みも用意されており、問題の見逃しを防いでいます。
最小バージョンの制限: MIN_RUBY_VERSION = "3.2.0" により、EOL を迎えたバージョンを自動的に除外する判断がなされました。rbenv/ruby-build は古いバージョンも含むため、このフィルタリングなしでは不要な PR が作成される可能性があります。
正規表現 /\A\d+\.\d+\.\d+\z/ で安定版リリースのみを対象とすることで、rc や preview といったプレリリース版も除外されています。
まとめ
本 PR は、Ruby バージョン管理を完全自動化する基盤を確立しました。単一のモノリシックなスクリプトを、テスタブルで再利用可能な複数のサービスクラスに分割し、GitHub Actions による定期実行と自動マージを組み込むことで、人手を介さないバージョン追跡を実現しています。この変更により、Ruby の新バージョンリリースから devcontainer への反映までのリードタイムが大幅に短縮されます。