この gem を使っているアプリケーションを探す

技術部開発基盤グループの鈴木 (id:eagletmt) です。 Ruby アプリケーションが使っている gem を一覧できる GemCollector というツールを作りました。 今回はその GemCollector の機能と、GemCollector の利用シーンの拡大について紹介したいと思います

なお GemCollector は Rails Engine の形で gem として公開しています。 https://github.com/cookpad/gem_collector

GemCollector 作成の動機

GemCollector を作った元々の動機は我々開発基盤でメンテナンスしている gem を更新したいとき、とくに非互換を含む変更を行いたいときに、変更の影響を受けるアプリケーションを把握することでした。 社内でよく使われている gem であれば、事前に社内での使われ方を調査し、現実的に修正可能かどうか調べることができます。 そして現実的に修正可能であり実際に変更を行った後も、どのアプリケーションで更新作業が終わっていないかを把握しやすくなります。

GemCollector の動作

通常の Rails Engine を使うときのように、適当な Rails アプリケーションを用意し gem_collector gem をインストールして GemCollector::Engine をマウントして使います。 それに加えて PostgreSQL を用意したり GitHub のアクセストークンを設定したりする必要がありますが、詳しい手順は README をご覧ください。

GemCollector の動作はとてもシンプルで、GitHub の webhook の push イベントを契機としてそのリポジトリの最新のリビジョンを取得し、そこに含まれている Gemfile.lock の情報を DB に入れるだけです。 webhook は GitHub の設定ページから https://gem-collector.example.com/github-webhook を追加してもよいですが、GemCollector の Web UI からも追加できるようになっています。

New repository

それぞれの gem を利用しているリポジトリは Web UI から閲覧できるようにしています。実際に GarageClient をすべてのアプリケーションで更新したいときがあり、GemCollector を見ることで GarageClient を利用しているアプリケーションを把握することができました。

Repositories using garage_client

またこの利用シーンを拡張する形で、ある gem をバージョン X 以下で使っているリポジトリにバージョンアップのお知らせ issue を立てる機能が @taiki45 によって追加されました。 バージョンでフィルタした上で、作成する issue のタイトルと内容を指定できます。

Create news issue on repositories

GemCollector の使い道

最初は非互換な gem の変更の影響を受けるアプリケーションを把握するために書かれたツールでしたが、実際に利用してみるとある gem を使っているリポジトリを把握できると便利な場面が他にもありました。

  • 自分のアプリケーションでその gem を使おうとしているが、困っている部分があるのでそこをどう回避しているのかを社内で実際に動いているコードで見ることができる
  • 自分がメンテナンスしている gem で Rails 3.x のサポートを切りたいが、まだ Rails 3.x を使っているアプリケーションがあるか調べることができる
  • ある gem で security fix を含むバージョンがリリースされたが、社内でアップデートが済んでいないアプリケーションはどれか把握できる

gem の最新度

社内の Gemfile.lock の情報が集まってくると、今度は「自分のアプリケーションが使っている gem はどれだけ最新か」というのも見積もれるようになってきます。 技術部では開発者の生産性向上を目標の1つとしており、生産性に寄与する指標の1つとしてどれだけ最新の gem を使っているかを数値化して使えないだろうかと考えていました。 そこで社内の他のアプリケーションが使っている gem のバージョンと比較してどれくらい上位にあるかのポイントを独自の基準で計算して、Up-to-date Point という数値として表示するようにしました。

https://github.com/cookpad/gem_collector/blob/v1.0.0/app/models/gem_collector/repository.rb#L17-L53

以下のスクリーンショットは GemCollector を動かしているアプリケーションでの実際の例です。概ね最大値である 1.0 に近い値になっていますが、社内で Rails 5.1.0.rc1 を使っているアプリケーションがあるため、Rails 系の gem に対するポイントは 1.0 ではなくなっています。

Repository gem_collector

また、登録されている各リポジトリのポイントを日次で InfluxDB に入れることで、Grafana でリポジトリ毎のポイントの推移を確認できるようにしています。 見易さのために比較的良いスコア (> 0.8) と悪いスコア (< 0.5) に分けて閲覧できるようにしています。

Up-to-date Point chart

Up-to-date Point はまだ実験的な値で、社内で適用したところ大まかな指標としては間違っていなさそうですが、まだまだ改善点は多そうです。 たとえばこの方法だと、社内で利用者が少ない gem の場合に高得点になりがちです。そのアプリケーションでしか使ってない gem の場合、バージョンに関係なくその gem に対するポイントは 1.0 になってしまいます。 RubyGems.org では週次の PostgreSQL のダンプデータを公開しており、これを利用することでより正確に gem の最新度を見積もれるかもしれません。 https://rubygems.org/pages/data

まとめ

社内のアプリケーションが使っている gem の情報を集める GemCollector と、それの利用シーンについて紹介しました。 GemCollector は単純なしくみですが、これによって見えるようになった情報はとても役に立っています。 GemCollector のようにアプリケーションに関するデータを集め、それを見える状態にしたり数値化してみるのはいかがでしょうか。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/