`git:` インストール時にJavaScriptアセットを自動ビルドするgem拡張を追加
#861でビルド済みJSアセットをgit管理から除外した後、gem "lexxy", git: "..." 形式のインストールでアセットが存在しない問題を、Bundlerのgem拡張機構で解決しました。
背景
#861は、app/assets/javascript/以下のビルド成果物(lexxy.js、.min.js、.br、.gz)をgit管理から除外する変更でした。これらのファイルはyarn buildで生成される成果物であり、src/に触れるPRのたびにマージコンフリクトを引き起こしていたためです。
しかし、このgit除外はGitHubリポジトリを直接参照する gem "lexxy", git: "..." 形式のインストールに影響を与えます。公開済みの.gemファイルにはビルド済みアセットが含まれますが、gitリポジトリからのインストールではリポジトリの内容がそのまま使われるため、アセットが存在しない状態になります。本PRはこのギャップを埋めるための対応です。
技術的な変更
Bundlerの gem拡張機構(spec.extensions)を利用して、bundle install時に自動的にアセットをビルドする仕組みを追加しました。変更はext/Rakefileの新規追加とlexxy.gemspecへの1行追加の2点です。
ext/Rakefileは:defaultタスクとして以下のロジックを実装しています:
task :default do
root = File.expand_path("..", __dir__)
target = File.join(root, "app", "assets", "javascript", "lexxy.js")
next if File.exist?(target)
puts <<~MSG
Lexxy: JavaScript assets not found — building from source.
This is expected when using Lexxy via `git:` in your Gemfile.
It requires Node.js and Yarn to be installed.
MSG
Dir.chdir(root) do
unless system("command -v yarn > /dev/null 2>&1")
abort <<~MSG
Lexxy: `yarn` is not installed. Lexxy needs Node.js and Yarn
to build its JavaScript assets when installed from git.
Install Yarn: https://yarnpkg.com/getting-started/install
MSG
end
sh "yarn install --frozen-lockfile"
sh "yarn build"
end
end
lexxy.gemspecにはspec.extensionsの1行を追加し、Bundlerにこの拡張ファイルの存在を通知します:
spec.extensions = [ "ext/Rakefile" ]
タスクの先頭でlexxy.jsの存在確認(next if File.exist?(target))を行うことで、公開済みgemのインストール時はこのタスクが即座にスキップされます。Yarnが未インストールの場合はabortでインストールドキュメントへのURLを提示して処理を中断します。
設計判断
冪等性の確保と公開済みgemとの互換性維持が本実装の設計の核心です。
アセットファイルの存在確認を最初に行う設計により、この拡張は2つの状況で正しく動作します。公開済み.gemファイルからのインストールではアセットが既に含まれているためタスクはno-opとなり、git:経由のインストールでのみビルドが実行されます。これにより、ユーザーがgemのインストール方法を切り替えても同じspec.extensions設定が機能します。
Yarnの検出にcommand -v yarnを使用し、未インストール時はインストールドキュメントのURLを含むメッセージでabortする設計は、エラーの原因と解決方法を明確に伝えるDX(開発者体験)への配慮です。
まとめ
git管理からビルド成果物を除外するという正しい判断(#861)を維持しながら、Bundlerのネイティブな拡張機構を活用することでgit:インストール時のユーザー体験を損なわない設計を実現しています。アセットの存在確認によるno-op設計が、公開gemとgitリポジトリ参照の両方で単一の拡張コードが機能するという一貫性を生んでいます。