RubyKaigi 2022 に登壇します

RubyKaigi 2022 に『Make RuboCop super fast』というタイトルで登壇します。

私の登壇は2日目である 2022年9月9日(金) の 11:30-12:00 です (昨年と同じ日時だった) 。

rubykaigi.org

最近 RuboCop に追加した server mode の話です。server mode 導入のはじまりの PR は以下。

github.com

server mode の導入に至った課題点とその解決の仕組み。現状での弱点や今後の展望といった利用者として知っておくと良さそうな話なんかも含めて話す予定です。server mode は絶賛開発途上でもあるので、割りと鮮度高めの話になるのではと思っています。

また、今年の RubyKaigi には勤務先の永和システムマネジメントから同僚の @ima1zumi@fugakkbn も登壇します。お楽しみに。

test-queue 0.5.0 をリリースした

前回の v0.4.2 のリリースが 2017年1月らしいので、5年越しのリリースになった。

rubygems.org

自分が欲しかったのは以下の警告を抑制するパッチだったけれど、前回リリースからの差分が他にもあったので、ざっと見ておいたことも書き残しておく。

rspec-queue spec
Starting test-queue master (/tmp/test_queue_69954_4740.sock)
/Users/koic/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/test-queue-0.4.2/lib/test_queue/runner.rb:299: warning: instance variable @early_failure_limit not initialized

今回 v0.5.0 リリース前に確認したところ、TEST_QUEUE_SLAVE_MESSAGE という環境変数設定の名前が、非推奨警告も何もなくカジュアルに TEST_QUEUE_REMOTE_MASTER_MESSAGE に変更されていてユーザーフレンドリーじゃないだろうと、既存の環境変数は非推奨警告付きで利用可能な形にしておいた。

`TEST_QUEUE_SLAVE_MESSAGE` is deprecated. Use `TEST_QUEUE_REMOTE_MASTER_MESSAGE` instead.

いちおう slave という言葉は控えましょうという世論が背景に強かった頃の変更らしいけれど、変更先に master が入っているのはいいのか?という疑問は残りつつ、私自身はベターネームを付けたいための時間も強い意思もないので、そこはそっとしておいた。

ということで、v0.5.0 についての変更要点は以下を参照してください。

github.com

今後、test-queue 開発の内部改善としては CI 環境が travis.org のままで動いていないので、travis.com 移行をするかどうするかというのがあり、そのあたりは GitHub Actions か何かに引越しすれば良いだろうかというお気持ち。

ユーザー向けの機能的には RSpec 4 (beta) に向けた PR が来ていて、そのレビューとかあるけれど、RuboCop で試してみたらどうも RSpec 4 対応のそのパッチ自体不正確なようで期待どおりの動きをしていないのだった (端的にいうとパスすべきテストが落ちている) 。とりわけ RSpec 4 (beta) の API がどの程度安定しているのかも分かっていないし、急ぐ話でもないので追い追い無理ないくらいに。

とりあえず、RSpec 4 が出る頃には次のバージョンを出そうかなという気持ちはあるものの、他とのトリアージなので、もし急ぎの何かがあればメンションなりしてください。

test-queue のコミッターになった

Ruby の parallel test runner である、test-queue のコミッターになった。

github.com

数年前に送った自分のパッチのリリースが欲しくて、RubyKaigi 2014 のクロージングキーノートスピーカーでもある @tmm1 とやりとりしたのがきっかけ。@tmm1 は現在使っていないとのことで、私の方でいくらかのメンテナンスを巻き取る予定でリリース権ももらっています。

現状でサッとリリースできないか最新安定版 v0.4.2 からの差分を眺めたところ、緩和すべき Breaking Change があったりしたので、もろもろ調整した後にリリースする予定です。

RuboCop 1.30 で非推奨になった自動修正オプション名

RuboCop 1.30 で、自動修正する際のオプションである --auto-correct--auto-correct-all は非推奨になり、それぞれ --autocorrect--autocorrect-all になりました。

変更対象のオプション名は long name のみで、short name である -a-A に変更はありません。

まとめると、自動修正に関するオプションは今後以下が正です。

安全な自動修正

  • rubocop -a
  • rubocop --autocorrect

安全でない自動修正

  • rubocop -A
  • rubocop --autocorrect-all

今回の変更は自動修正に関する用語として混在していた auto-correct と autocorrect を、autocorrect の方に統一することを目的としたものです。

なお、この変更が行われた PR は以下です。

github.com

おそらく RuboCop 2.0 あたりで非推奨になったオプションは削除されます。

long name のオプション名を組み込んでいるプログラムはもちろんのこと、書籍やインターネット文献では非推奨になったオプション名を見かけることになると思うため、書き残しておきます。

RuboCopの解析バージョンサポートを見直した

RuboCop は Ruby の EOL バージョンについて、EOL の 1年後までサポートしています。

RuboCop の Ruby バージョンサポートといった時に「ランタイムバージョン」と「解析バージョン」の 2 つがあり、このエントリを書いている時点の最新 RuboCop 1.30 リリースまで、このふたつのバージョンが混同された状態でライフサイクルを共にしていました。

ここでそれぞれのバージョンをおさらい。

  • ランタイムバージョン ... RuboCop を実行する Ruby のバージョン。rubocop コマンドを実行する ruby 処理系のバージョンです。
  • 解析バージョン ... RuboCop が解析する Ruby コードのバージョン。.rubocop.yml の TargetRubyVersion などで指定しているバージョンです。詳しくはこちらの過去記事をどうぞ。

例えば Ruby 2.5 は RuboCop 1.28 でサポート打ち切り、Ruby 2.4 は RuboCop 1.12 でサポート打ち切りとなっていますが、これは 「ランタイムバージョン」と「解析バージョン」の両方を意味していました。

RuboCop 1.30 では、Ruby 2.5 以下をランタイムバージョンは使うことはできませんが、TargetRubyVersionRuby 2.0 以上を指定することができるようになっています。

例えば、Gem 開発で古い Ruby バージョンから新しい Ruby バージョンまでをサポートしたいというニーズや、古い Ruby バージョンのアプリケーションだけど RuboCop はアップグレードして新しい機能を使いたいといったことに対応できます。

特に前者は「RuboCop がサポートバージョンを打ち切ったため、Gem の Ruby 最小要求バージョンが繰り上げる」という事例をいくつか見てきており、RuboCop が Gem のメンテナンスサイクルに作用するのがエンドユーザーにとって幸せかという疑問を解決する形になります (TargetRubyVersionRuby 2.0 まで解析可能になったので) 。

GitHub Actions などで RuboCop をリンターとしている場合は、Ruby 2.6 以上をワークフローのランタイム Ruby バージョンに指定して、.rubocop.yml の TargetRubyVersion に最小要求の Ruby バージョンを指定すると良いでしょう。

ちなみに、Ruby 1.9Ruby 2.0 以降との非互換による Cop のメンテナンスコストの方が高くつきそうなので、いずれもサポートしません。

github.com

先日公開された『2022 Ruby on Rails Community Survey Results』の Ruby バージョンの利用率調査だと、EOL の利用は Ruby 2.6 (15%) 、Ruby 2.5 (8%) 、Ruby 2.4 (4%) 、Ruby 2.3 (3%) 、Ruby 2.2 (1%) 、Ruby 2.1 (1%) 、Ruby 2.0 (0%) 、Ruby 1.9 (2%) 、Ruby 1.8 (1%) ということで、全体の 35% くらいあるようです。そのうち Ruby 2.0 ~ 2.6 までの 32% くらいは救えるだろうか?といったところ。

rails-hosting.com

とはいえ、アプリケーションであればはやめに Ruby バージョンのアップグレードをしていけると良いですね。

Ruby 3.2.0dev にマージされた Rust YJIT をビルドする

YJIT の Rust 実装がマージされました。いまのところ今年の Shopify からの代表作ではと見ています (YJIT 自体は Ruby 3.1 で C 実装導入されている機能です) 。

github.com

そういうわけで、現在の Ruby 3.2.0dev で YJIT を有効にする場合は、Rust の処理系 (1.60.0 1.58.1 以上) が必要になります。Rust のインストールに使う rustup については以下の公式ページなどを参照してください。

www.rust-lang.org

YJIT はデフォルトではビルドされません (なので YJIT を使わないビルドであれば Rust 処理系は不要です) 。

デフォルトの YJIT なしの Ruby をビルドしている場合は --yjit オプションを渡しても、ruby: warning: Ruby was built without YJIT support と以下のように警告が出力されます。YJIT の API は存在しないので、使おうとするとエラーになります。

% ruby --yjit -e 'p RubyVM::YJIT.enabled?'
ruby: warning: Ruby was built without YJIT support
-e:1:in `<main>': uninitialized constant RubyVM::YJIT (NameError)

p RubyVM::YJIT.enabled?
        ^^^^^^

このあたり Ruby 3.1 と異なるデフォルト (のビルド) 挙動なので、YJIT を有効にしているユーザーは気に留めておくと良さそうです。

% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) +YJIT [x86_64-darwin19]
true

Ruby 3.2.0dev において、YJIT 込みで Ruby 本体をビルドする場合は以下のように ./configre にオプションを渡します。

% ./autogen.sh
% ./configure --enable-yjit=dev ...
% make -j install
building Rust YJIT (dev mode)
% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.2.0dev (2022-04-27T15:00:54Z master b43eb54a0c) +YJIT [x86_64-darwin19]
true

リリースモード --enable-yjitデバッグ開発モード --enable-yjit=dev でオプションが異なります。詳しくはドキュメントを参照してください。

github.com

rbenv を使っているようであれば、環境変数 RUBY_CONFIGURE_OPTS を使ったオプション渡しが手早いです。

% RUBY_CONFIGURE_OPTS=--enable-yjit rbenv install 3.2.0-dev
% ruby --yjit -ve 'p RubyVM::YJIT.enabled?'
ruby 3.2.0dev (2022-04-27T15:00:54Z master b43eb54a0c) +YJIT [x86_64-darwin19]
true

さらに Ruby のランタイムでも YJIT はデフォルト無効なので、YJIT を有効化する場合は ruby コマンドのオプションに --yjit を渡すか、環境変数 RUBY_YJIT_ENABLE=1 なんかを渡します。直接 ruby コマンドを使わずに rake 経由なんかで実行する場合は環境変数の指定方法を使うことになると思います。

あと当然ですが、Ruby のビルド環境に Rust がインストールされていなければ、YJIT 有効のビルド時に以下のようなエラーになります。

% RUBY_CONFIGURE_OPTS=--enable-yjit rbenv install 3.2.0-dev
(snip)

Last 10 log lines:
checking whether -pie is accepted as LDFLAGS... yes
checking whether wrapper for LD_LIBRARY_PATH is needed... no
checking whether dtrace USDT is available... no
checking for __builtin_setjmp... yes with cast ()
checking for _setjmpex as a macro or function... no
checking for _setjmp as a macro or function... yes
checking for sigsetjmp as a macro or function... no
checking for setjmp type... __builtin_setjmp
checking for rustc... no
configure: error: rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install

Rust 処理系をインストールしましょう。

コード的には ruby/yjit ディレクトリ以下が、Rust の世界になっているようで、興味のある人は見てみると発見があるかもしれません。

github.com

RubyGems にも Rust 拡張に向けた Cargo ビルダーがマージされたこともあり、Ruby エコシステムと Rust との関わりが今後の関心どころです。

github.com

そういった流れもあるので、個人的には The Rust Programming Language (日本語訳) の後に『プログラミング Rust 第2版』を読んでいるところですが、痒い所に手が届く感じでおすすめです (先に The Rust Programming Language で全体の雰囲気を掴んでおくと理解がスムースかも) 。

🦀

【iCARE Dev Meetup #32】OSSとの向き合い方に登壇した

@ogijun からオファーをもらって、【iCARE Dev Meetup #32】OSSとの向き合い方に登壇しました。

icare.connpass.com

@ogijun とはゼロ年代Java の勉強会なんかでお互いによく遭遇していたのがはじまりで、かつて高橋会長でかい文字のプレゼン本を深夜の大阪の JAL ホテルラウンジでゲラハックしたりと古くからの繋がり。そんな @ogijun からの「OSS をテーマにしたイベントをするので、RuboCop でひとつ」となったのがきっかけ。

私の登壇について、昨年の RubyKaigi Takeout 2021 の登壇で話の展開と尺の都合で収まらなくて削った秘蔵パートを、まるっと全面改訂して再構成したもの。なので、スライドが英語は何かを狙っていたというより、ベースの流れでそのまま作っていた部分が大きいです。

タイトルは『パターンハッチング』と『Test Driven Development: By Example』をモチーフにしたもので、ウォードのサンプルではなく、RuboCop を元にしたコントリビューションへの発見といったものをテーマにした。

当日のスライドは以下です (時間の関係上、凝ったことはしていませんが、スライドの表紙も『TDD by Example』を意識して、それっぽくしていた) 。

技術的な何かを持ち帰ってもらう話の全体像としては ghq, gem-src と git の tips を交えつつ、最後はサピア=ウォーフの仮説を含めた OSS への向き合い方みたいな話をした。

ghq もろもろの tips について、弊社から参加していたメンバーの反応はこんな感じでした (弊社に入社するとよろこんで OSS メンタリングします) 。

こんな感じで今日からはじめることができるので、まだの人はおすすめの環境構築です。また、もし既存のローカルリポジトリghqディレクトリスタイルに移管するのが手間ということであれば、ghq_transfer という gem など用意していますのでよければどうぞ。

github.com

特に今回のイベントでは、他の登壇者方の OSS 開発者の開発観を聞くことで、自分の過ごしている OSS の世界を再認識できたことでした。何となく OSS 開発者の考え方は似通ってくるようなところを感じたのは、印象的でおもしろかったです。登壇者の皆さん、iCARE さんありがとうございました!

最後に余談ですが、本発表をとおした「OSS と人生」について、X.Y.Z.→A のサードアルバム『LIFE』収録曲の『生きるとは何だ』を聴きながらストーリーを練っていました。ジャパメタ好きの人におすすめです。What is life all about?