GitHub Packages と Gem の配布先

TL;DR 公開 gem の配布先は従来どおり rubygems.org のみで良いかなといまのところ思っています。


GitHub では GitHub Packages がリリースされて、gem を含めていくつかのパッケージに対してサポートされている。

help.github.com

gem に対する GitHub Packages への現時点での考え。

OSS など公開 gem のリリース先については、従来どおり rubygems.org のみで良いかなといまのところ思っています。理由としては、rubygem.org ではダウンロード数などの数字によって、その Gem やリリースがユーザーに与える影響への指標のひとつとして中央集権先として見ることができるためです。将来的に複数のホスティングに分散するメリットが明確になったらその時は改めて考えてみたいところ。

一方で、gem server を使った社内 Gem サーバーなどを運用している場合は、GitHubパッケージをホスティング先にすると便利そうです。その他、リリース時に rubygems.org が死んでいたら死ぬみたいな状況を回避したいようなシステムの冗長構成先として使えるかもしれません (DNSimple が死んで rubygems.org が引けなかったことが数年前にあったけれど、滅多にないと思う) 。

このエントリの出典は、RuboCop リポジトリに以下のような GitHub Packages 向けに Gem をリリースするタスク追加の PR が送られてきたやりとりの際に考えたことでした。

github.com

備忘を兼ねてここに書いておこしておきます。

RuboCop Faker 1.0 をリリースした

Faker::Base.unique メソッドが使われている際の false negative に対応した RuboCop Faker 1.0 をリリースした。

rubygems.org

もともと Faker 1 系から Faker 2 系へのインタフェースマイグレーションをゴールとした Gem で API としては安定していたので、今回のリリースを期にメジャーバージョン 1.0 を付与した。

今回は以下のイシューに Real world ユースケースが示されており、以下のような Fake::Base.unique が使われていた場合に false negative があった。

github.com

public_key { Faker::Lorem.unique.characters(255) }

これはユニーク値を生成する形で以下の API が呼び出されている (実装には method_missing が使われている) 。

Faker::Lorem.characters(255)

今回のリリースで RuboCop Faker でサポートしているひととおりの Faker のメソッドに対して unique が使われているときも検知できるように対応している。

ruby-build のパッケージ定義ファイルを生成する

Ruby の処理系がリリースされた際に rbenv/ruby-build に PR を開いている定義ファイルを生成するスクリプトを upstream に提案した。既に ruby-build には PR を開いていて統合済み。

github.com

過去の PR と見る人が見れば分かる定義ファイルだが、既に自動化されていたTruffleRuby 向けと Rubinius 向けを参考に CRuby 向けと JRuby 向けも自動化した。

頭の中のチェックメモはあって基本的な部分を含めて記すと以下になる。

  • 公式サイトでのリリースアナウンスをソースとする
  • CRuby と JRuby のいずれもダウンロードできるパッケージがいくつか公開されているので、それぞれ適したものを入手する
  • CRuby の場合は公式サイトで SHA256 が書かれているので一致を確認する (していない場合はどこかに問題がある場合があり公式サイトの値が違っていたことがある)
  • CRuby の場合は openssl のバージョンを他の定義ファイルと突き合わせる
  • JRuby の場合は require_java のバージョンを他の定義ファイルと突き合わせる
  • PR を開くことになる定義ファイルでインストールと Hello, world くらいが成功することを確認する

このあたりいくらか職人の温かみのあるセルフレビューの必要が残っているものの update-truffleruby と同様にスクリプトの実行で定義ファイルが作れる。

以下を ~Downloads ディレクトリにパッケージをダウンロード済みである状態で実行する。

# Ruby 2.7.0 の定義ファイルを生成する場合
% cd path/to/rbenv/ruby-build
% script/update-cruby 2.7.0 ~/Downloads

# JRuby 9.2.11.0 の定義ファイルを生成する場合
% script/update-jruby 9.2.11.0 ~/Downloads

注意点としては macOS を使っている人は SHA256 のコマンドが gsha256sumshasum -a 256 なんかだと思うので、sha256sum を symlink しておくなどのハックがいると思う。このあたり rbenv は type コマンドでうまいこと解決しているが、ベースの update-truffleruby の実装に合わせていることや利用者がその周辺で限定されているものだったのであえて対応せずコンパクトな形にしている。

RVM にもこういったのがあると便利だと以前から思っていはいるので、そのうち用意したいところ。

RuboCop の Cop 名をマイグレーションする

RuboCop では Cop が所属する先の部署や Cop 名が変更されることがある。

変更された際に所属先の部署や Cop 名をマイグレーションする仕組みが用意されているものについて紹介する。

Migration/DepartmentName cop

現状の RuboCop では部署名が省略されいても、一意に特定できれば動く仕組みが入っている。これはおそらく昔の RuboCop では部署が存在せず、部署導入時に移行をスムースにするための機構だったようだ。

rubocop-hq/rubocop#7717 にて以下のコメントがある。

RuboCop no longer supports ambiguous cop names (those without a department).

このとおり今後の RuboCop では部署名の省略は許可しないようになるだろう。これに向けて部署が省略された disabled コメントに部署名を補完する Cop が追加されている。それが Migration/DepartmentName cop である。

github.com

v0.75 でデフォルト無効で追加されているが、現状の v0.80 ではデフォルトで有効化している。今後、disabled コメントへの部署名の省略をランタイムで受け入れる機構を削除する可能性があることから、ユーザーに対して disabled コメントへの部署名の補完を推進する意味合いがある。

Migration/DepartmentName cop は auto-correct をサポートしているので、rubocop -a --only Migration/DepartmentName で以下のように部署名を補完することができる。

-def parse value # rubocop:disable MethodLength
+def parse value # rubocop:disable Metrics/MethodLength

また、以前の RuboCop から部署名が変わっているようなレガシー部署からの移行についても RuboCop 0.80 からサポートしているので、RuboCop のバージョンを上げる際にはその辺りも活用できると思う。

github.com

Mry

pocke さん作の gem 。Mry Migrates .Rubocop.Yml の頭文字で Mry とのこと。コマンドライン一発でマイグレーションできるのでとても便利。

github.com

.rubocop.yml に指定している Cop 名についてレガシーの名前を新しい名前に置換してくれる。

繰り返しになるが、将来的に部署名を省略した disabled コメントのサポートは消される方向性があるため、Migration/DepartmentName cop と組み合わせて 部署名/Cop名 の形式に移行しておくと良い。

RVM のコミット権をもらった

RVM は Travis CI や Active Record Oracle enhanced adapter で使っている Oracle 向け rails-dev-box で使われている流れからパッチを送っていたところコミット権をもらった。以前、パッチを送った時に PR へのアサインがされていたので「おや?」と思ったものだけれど伏線だったのかもしれない。

ただこれによって RVM に割ける時間が増えるわけではないので、これまでどおり少しずつやれることを増やしながら続けるだけだけれど、できることが増えたことで捗ることがあるかもなので、何か困ったことがあればパッチ会や地域Rubyコミュニティなどで声を掛けてください。

github.com

『NO HARD WORK!: 無駄ゼロで結果を出すぼくらの働き方』を読んだ

『NO HARD WORK!: 無駄ゼロで結果を出すぼくらの働き方』を数日前に読了した。

DHH の何がすごいかという一面として CTO として長年にわたって会社経営にも携わっていて、Rails が切り出されるプロダクトとしての Basecamp の面ではなく、会社としての Basecamp について CEO のジェイソン・フリードとの共著で記されている。書籍にも記されているとおりすべてを真似すればハッピーになるというものではないと思うけれど、長年小さなチームで成果を出して自分たちの幸せを維持するというそのあり方は何かしらの参考になると思う。読んでいて思い起こしたのはエクストリームプログラミングの初版で当時まだ世界的に馴染みのない開発プラクティスが紹介されたように、本書でも世界的に未開的であろういくつかのプラクティスが紹介されているが、それらを組織にあわせてカスタマイズ導入を検討してみると面白い変化を作れるかもしれないな、、、とか現在と未来を考えることができるとても良い書籍だった。

ちなみにこの書籍は『It Doesn't Have to Be Crazy at Work』が原書となり、Ginza.rb で yahonda さんと masa_iwasaki さんに邦訳があることを教えてもらった。

It Doesn't Have to Be Crazy at Work (English Edition)

It Doesn't Have to Be Crazy at Work (English Edition)

おかげで面白い本を母国語で読むことができました。ありがとうございました。

RuboCop に JUnit フォーマッタを組み込んだ

JUnitXML フォーマッタが CircleCI 利用時に有用だけれど gem の方がメンテナンスされていなくて、コアで巻き取ってもらえないかというイシューに対応した。

github.com

次にリリースされる予定の RuboCop 0.80 から --format junit オプション (あるいは --format ju) で使えるようになる。

% rubocop --format junit
<?xml version='1.0'?>
<testsuites>
  <testsuite name='rubocop'>
    <testcase classname='example' name='Style/FrozenStringLiteralComment'>
      <failure type='Style/FrozenStringLiteralComment' message='Style/FrozenStringLiteralComment: Missing frozen string literal comment.'>
        /tmp/src/example.rb:1:1
      </failure>
    </testcase>
    <testcase classname='example' name='Naming/MethodName'>
      <failure type='Naming/MethodName' message='Naming/MethodName: Use snake_case for method names.'>
        /tmp/src/example.rb:1:5
      </failure>
    </testcase>
    <testcase classname='example' name='Lint/DeprecatedClassMethods'>
      <failure type='Lint/DeprecatedClassMethods' message='Lint/DeprecatedClassMethods: `File.exists?` is deprecated in favor of `File.exist?`.'>
        /tmp/src/example.rb:2:8
      </failure>
    </testcase>
  </testsuite>
</testsuites>

これまでの JUnit フォーマットプラグインを見たところでは以下のふたつがあり、今回は報告のあったのと利用量が多そうだった前者をベースに組み込んでいる。

master ブランチにマージされているので、次の RuboCop のリリースで入る予定。個人的には JUnit という古来からのフォーマットの活用事例ということは、コアでメンテナンスして良いという大きな判断要素だった。

本体でサポートすることになったので、gem 依存で使っているアプリケーションでは Gemfile 記載の依存を減らせるかなと思います。一方で実装としては Ruby 2.8.0-dev (Ruby 3.0) で添付から外された REXML に依存するので、以降内部的には REXML への依存が入ります。