🤐

ヒミツのgemを利用するアプリをHerokuにデプロイする2025年版

2025/02/08に公開

Herokuなどのプラットフォームでは、依存ライブラリを公開されているレポジトリからダウンロードして利用するのが一般的ですが、お仕事のRubyアプリには社内用のヒミツのgemに依存するものもあります。社内用のgemをrubygems.orgに公開するには社内の手続きに手間がかかり、rubygems.orgの名前空間を汚してしまうこともあり、できれば公開せずに済ませたいところです。

試行錯誤したところ、ヒミツのgemを手元のワーキングコピーから保管した後にGemfileを編集して保管先のパスを参照するようにすることで、ヒミツのgemを公開せずHerokuで利用できることがわかりました。この記事では、Rubyはバージョン3.4.1を利用します。HerokuのRuby Buildpackで起動されるBundlerのバージョンは2.6.2でした。

ヒミツのgemをつくる

例示用のgemを作っておきます。

$ bundle gem hello
$ cd hello
$ vi lib/hello.rb
$ vi hello.gemspec

lib/hello.rbは下記のようにしました。

# frozen_string_literal: true
require_relative "hello/version"

module Hello
  class Error < StandardError; end

  def self.greet
    "Hello, World!"
  end
end

bundle gem helloコマンドで生成されるhello.gemspecではファイルの列挙にgitコマンドを利用しますが、Herokuのビルドプロセスでは利用できないので、あらかじめspec.filesにファイルを列挙しておきます。

# frozen_string_literal: true
require_relative "lib/hello/version"

Gem::Specification.new do |spec|
  spec.name = "hello"
  spec.version = Hello::VERSION
  spec.authors = ["zunda"]
  spec.summary = "Hello, World!"
  spec.license = "MIT"
  spec.files = %w[LICENSE.txt README.md Rakefile hello.gemspec lib/hello.rb lib/hello/version.rb sig/hello.rbs]
  spec.bindir = "exe"
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]
end

ローカルなワーキングコピーにコミットしておきます。

$ git add hello.gemspec lib/hello.rb
$ git commit -m 'First local release'

ヒミツのgemをつかうアプリをつくる

例示用のアプリを作ります。ヒミツのgemと同じ階層のディレクトリを利用します。

$ git init
$ rbenv local 3.4.1
$ bundle init
$ bundle add rackup puma sinatra
$ bundle add hello --git=`dirname $(pwd)`/hello
$ vi Gemfile
$ vi web.rb
$ bundle install

Gemfileは下記のようにしました。

# frozen_string_literal: true
source "https://rubygems.org"
ruby file: '.ruby-version'

gem "rackup", "~> 2.2"
gem "puma", "~> 6.6"
gem "sinatra", "~> 4.1"
gem "hello", "~> 0.1.0", :git => "/home/zunda/c/src/local/hello"

web.rb/へのGETリクエストにヒミツのgemから挨拶を返します。

require 'sinatra'
require 'hello'

get '/' do
  Hello.greet + "\n"
end

Herokuにデプロイする (失敗編)

Herokuにデプロイするため、Procfileを用意します。

web: bundle exec ruby web.rb -p $PORT

ローカルなワーキングコピーはpushされないので、デプロイは失敗します。

$ git add .
$ git commit -m 'First commit'
$ heroku create
$ git push heroku master
  :
remote:        fatal: repository '/home/zunda/c/src/local/hello' does not exist
remote:
remote:  !
remote:  !     Failed to install gems via Bundler.
remote:  !
  :

Herokuにデプロイできるようにする

必要なgemをvendor/cacheに保管します。--allオプションでヒミツのgemも保管されます。

$ bundle package --all
$ ls -d vendor/cache/hello*
vendor/cache/hello-cf18744fd01d

Gemfileを編集してヒミツのgemをvendor/cache以下から参照するようにします。

# frozen_string_literal: true
source "https://rubygems.org"
ruby file: '.ruby-version'

gem "rackup", "~> 2.2"
gem "puma", "~> 6.6"
gem "sinatra", "~> 4.1"
#gem "hello", "~> 0.1.0", :git => "/home/zunda/c/src/local/hello"
gem "hello", "~> 0.1.0", path: "vendor/cache/hello-cf18744fd01d/"

Gemfile.lockの内容をGemfileに合わせて更新します。

$ bundle install

これで、ヒミツのgemも含めてHerokuアプリとしてデプロイできるようになりました。

$ git add Gemfile Gemfile.lock vendor/cache
$ git commit -m 'Use path'
$ git push heroku master
GitHubで編集を提案

Discussion