Ruby バージョン自動更新システムの実装

rails/devcontainer

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 つの層に機能を分離しています:

  1. ビジネスロジック層: lib/ruby_version_adder.rb が純粋なバージョン追加ロジックを担当
  2. 外部連携層: lib/ruby_version_checker.rb が rbenv/ruby-build からのバージョン取得を、lib/ruby_version_pr_creator.rb が PR 作成を担当
  3. プレゼンテーション層: lib/commands/ 配下のクラスが CLI 出力を担当
  4. 共通基盤: 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 への反映までのリードタイムが大幅に短縮されます。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「総論→各論→結論」の構成が明確です。リード文、背景、技術詳細、設計判断、まとめの各要素がすべて含まれており、理想的な記事構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)とGitHubのPRリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

GitHub Actionsのワークフロー設計やRubyのサービスクラス設計に関する内容であり、専門知識を持つエンジニアという対象読者に完全に適合しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が全体を通して守られています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容(追加されたファイル、定数、YAML設定など)を正確に引用しています。ファイルパスも正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「単一責任の原則」「ファサード」「Octokit」「WebMock」など、使用されている技術用語は文脈に即しており、正確です。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

アーキテクチャの再設計、GitHub Actionsの自動マージ機能、バージョンフィルタリングのロジックなど、技術的な変更点に関する説明はすべてDiffの内容と整合しており、正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

PRにDescriptionがないにもかかわらず、Diffで追加・変更されたコードを根拠に、設計判断や実装の詳細を的確に説明しています。根拠のない主張や推測(ハルシネーション)は見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#112)、cronのスケジュール('0 2 * * *')、Rubyの最小バージョン(3.2.0)など、記事に含まれる数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトル「Ruby バージョン自動更新システムの実装」は、PRのタイトル「Create automation to check for new Ruby versions and create PRs」の内容を的確に要約しており、主題は完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

EOLに関する言及は、コード内の`MIN_RUBY_VERSION`定数の意図を補足説明するものであり、PRにない情報を不適切に追加しているわけではありません。許容範囲内です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

記事内には時間表現に関する記述がほとんどなく、PR情報との不整合もありません。