20
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Bitriseで gemとbrewのキャッシュ

Last updated at Posted at 2018-06-28

追記

Gemのキャッシュについてはbitrise公式で手順が解説されていたので、こちらをご覧ください。

はじめ

brewのcacheは他のCIでも使えるかも

公式のサポート記事があった。

キャッシュについての記事も分かり易かった。

BitriseではRubyのGemがインストールされるディレクトリを gem environment gemdir で取得できる。そのディレクトリを環境変数で保存しておいて、最後にCache:pushする一覧に入れておく。

cache:pullではキャッシュされたディレクトリをダウンロードしてくるので、次回の gem install からはそれを見に行ってる。

数十秒かかっていた gem install xxx が6秒ぐらいになった:angel:

とりあえず投稿して、フォーマットは後で整形します。

#1.Cache設定用スクリプトのステップを追加
ステップの場所: Git cloneとCache:Pullの間
名前は適当にGem cache settingsとかにした。

Gem_cache_settings.sh
#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

# cacheのための設定
set -ev
envman add --key GEM_HOME --value "$(gem environment gemdir)"

#2.gem install xxx スクリプトのステップを追加
ステップの場所: Cache:PullとCache:Pushの間

gem_install.sh
#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

# cacheのための設定
set -ev
gem install slather #例えば、iOSのカバレッジ出力

#3. Cache:Pushのパス一覧に$GEM_HOMEを追加

Cache pathsというフィールドの最後の行を改行して、 $GEM_HOME を追加する。

ステップの位置関係まとめ

  1. Git Clone
  2. Gem cache settings
  3. Cache:Pull
  4. gem install
  5. Cache:Push

※各ステップの上下には別のステップが入っても問題ない

brewのキャッシュ

記事がなかったのでHomebrewのソースコード読んだ。

どういう仕組みでinstall済みのfomulaを再installせずに済ませているのかを考えたら、

xxx.sh
xxx is already installed and up-to-date
To reinstall 1.8.3, run `brew reinstall xxx`

と表示される条件を見つければ良いと思ったので、installコマンドの中身を見に行って、この文章を見つけた。

install.rb
#多分コマンドラインで引数に渡したformulaを見に行ってeachで取り出してる。
#--forceオプションがbrewにあって、このソースの途中に `unless ARGV.force?` っていう構文あるから多分あってる。

ARGV.formulae.each do |f|
(略
if f.keg_only? && f.any_version_installed? && f.optlinked? && !ARGV.force?
          # keg-only install is only possible when no other version is
          # linked to opt, because installing without any warnings can break
          # dependencies. Therefore before performing other checks we need to be
          # sure --force flag is passed.
          if f.outdated?
            optlinked_version = Keg.for(f.opt_prefix).version
            onoe <<~EOS
              #{f.full_name} #{optlinked_version} is already installed
              To upgrade to #{f.version}, run `brew upgrade #{f.name}`
            EOS
          elsif ARGV.only_deps?
            formulae << f
          else
            opoo <<~EOS
              #{f.full_name} #{f.pkg_version} is already installed and up-to-date
              To reinstall #{f.pkg_version}, run `brew reinstall #{f.name}`
            EOS
          end
(略

一番下のelseに到達できればキャッシュできたと言えそう。

###条件判定メソッド

method 方策、調査結果
keg_only? /usr/local/Cellarには存在するが、/usr/local/bin, /usr/local/lib などにリンクされていないformula
any_version_installed? 最低でも一つのバージョンのformulaがインストール済みかどうか。
optlinked? /usr/local/opt 以下にシンボリックリンクが存在するかどうか 。
outdated? 古いパッケージが入ってる
only?deps? --only-dependenciesオプションをつけるとこのフラグがtrueになる

Homebrewのkegとcellarの関係が分かりにくいからkeg_onlyが分かりずらい。

kegとは特定のformulaのインストール先Pathであり、cellarはkegのインストール先パス

つまり**/usr/local/Cellar/keg/**
という関係性。

なのでkeg_only?は、「Cellar以下にしかビルドが存在せず、/usr/local/binや/usr/local/libにシンボリックリンクが作成されていない」という状態を表す。

方策

/usr/local/Cellarをキャッシュすれば行けそう。
/usr/local/opt/をキャッシュすれば行けそう。

結果

できた。

70秒弱かかっていた brew install xxx が8秒弱になった:angel:

スクリーンショット 2018-06-29 11.42.02.png

1: Cache:Pullの前に、brewがインストール/シンボリックリンク作成/ダウンロードされるパスを環境変数に追加しておく。

envman add --key BREW_LICENSE_PLIST --value "$(brew --cellar)/license-plist"
envman add --key BREW_OPT_LICENSE_PLIST --value "/usr/local/opt/license-plist"

ちなみに以下は追加しなくていい。

envman add --key BREW_CACHE_LICENSE_PLIST --value "$(brew --cache)/license-plist"

homebrewのキャッシュってことで関係あるっぽいけど、コマンドを叩いてみると中身がわかる。 /Library/Caches/Homebrew/ 以下には、ダウンロードした圧縮ファイル(tar)が入るだけなので、CI上でのキャッシュ時には必要ない。(Cellarにビルドが入ってる前提なので)

ls.sh
$ ls ` brew --cache `
carthage-0.29.0.high_sierra.bottle.tar.gz
gettext-0.19.8.1.high_sierra.bottle.tar.gz
libidn2-2.0.5.high_sierra.bottle.tar.gz
license-plist-1.8.3.tar.gz
mint-0.9.1.high_sierra.bottle.tar.gz
openssl-1.0.2o_1.high_sierra.bottle.tar.gz
portable-ruby-2.3.3_2.leopard_64.bottle.tar.gz
swagger-codegen-2.3.1.high_sierra.bottle.tar.gz
swiftlint-0.25.1.high_sierra.bottle.tar.gz
wget-1.19.5.high_sierra.bottle.tar.gz
...

2: cache:pullとcache:pushの間でbrew install

brew link xxxをしておくと、/usr/local/bin/ にシンボリックリンクが作られるので、こうしておく。(再インストールを回避するための条件でないのでキャッシュはしてない)

brew tap mono0926/license-plist
brew install license-plist
brew link license-plist

3: cache:pullにパス追加

自分の場合はこうなった。一番上はデフォ。二番目はgem。それ以下はbrewの特定のfomula用。

$BITRISE_CACHE_DIR
$GEM_HOME
$BREW_LICENSE_PLIST
$BREW_OPT_LICENSE_PLIST
$BREW_CACHE_LICENSE_PLIST

(おまけ)Bitriseの挙動

Cache:Push

Cache:PushはデフォルトではPRでは走らない。
なのでPR時に走らせるように挙動を変えたければ以下のように run_if: ".IsCI" を追加する必要がある。

- cache-push:
        run_if: ".IsCI"
        inputs:
        - cache_paths: |-
        ...

キャッシュの意義

キャッシュはAWSのS3で保存されCloudFrontでキャッシュされている。毎回そこからキャッシュをダウンロードしている。

サイズが大きくなるようなキャッシュは、ダウンロード時間も増えるためメリットが少ない。
インストール時間 > ダウンロード時間 のようなパッケージはキャッシュするメリットがある。

/usr/local/Cellar/ のキャッシュ&ダウンロードはものすごく重たいので、このようにformulaを指定している。

envman add --key BREW_LICENSE_PLIST --value "$(brew --cellar)/license-plist"

Vagrantの立ち上げ時にすでに色々入っているので、 /usr/local/Cellar/ のキャッシュを試みたら3GB弱のキャッシュになった 普段は700MB弱。

Homebrewの挙動

brew installが呼ばれた時、すでにインストール済みかどうかなど色々チェックした後に新規インストールが必要であれば、FormulaInstaller が初期化されてインストールが始まる。

  1. kegにformulaがインストールされる
  2. /usr/local/opt/へのシンボリックリンクが作成される
  3. /usr/local/bin/ へのシンボリックリンクが作成される
20
13
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?