令和時代のRubyコア開発
Ruby Core Development 2019というタイトルでRubyKaigiのCFPにプロポーザルを書いたのだが、 もう一つ書いた方の話が採択されたのでその話はしなかった。
さて、今日はRubyコア*1の開発がSubversionからGitに移った節目でもあったので、そっちのトークで言いたかったことの一部を記事にしておこうと思う。
Subversion → Git 移行 [Misc #14632]
去年くらいから @hsbt さんが cgit というGitフロントエンドを使ってGitリポジトリの準備を始め Misc #14632、ついに今日正式にcgitの方がupstreamになった。平成の時代でSubversionでのtrunkのRubyコア開発は幕を閉じた。
この辺を進めているのは主に @hsbt さんな中、僕がこれを偉そうに書いたり今回のRubyKaigiで壇上でアナウンスをやったりしていたのは、必要な作業量が多そうで半年くらい前から僕も移行作業を手伝っていて、割と多くのパートで貢献していたため。
そもそも何故Gitにするのか
他の場所でのSubversionの用途は知らないが、Rubyコミッタの間では、大体以下の話が散見された。
- 最近は多くのコミッタがgit-svnを使っているが、git-svnを使うのが微妙に面倒くさい
- Subversionだとコミッタ以外のパッチをマージする時にauthor情報がコミットメッセージにしか残らない
- git blame や https://github.com/ruby/ruby/graphs/contributors に名前を残したい層がいて、その人たちのcontribution意欲を削いでいる
GitよりSubversionの方が優れている点も当然あるが、Git化したいという話は作者のまつもとさんが時おり言っており、 MatzがGoと言えばGoなのである。
何故GitHubではなくcgitなのか
- 極めて重要なメンテナであるEric Wong*2がプロプライエタリソフトウェア(GitHub)を使わない主義である
- 彼はWindowsには絶対に触らないし、ブラウザもCUIなのでJavaScriptがないと動かないCIは見ない
コミットhookが svn.ruby-lang.org のサーバー上で動くことを多少前提にしているのでいきなりGitHubに持っていくと少し大変という話もあるが、ブロッカーなのは以上の一点しかないという理解をしている。
今のところ彼はメンテナをやめる意向をたまにRedmineに書いているがおそらくまだ公式にそうなったという感じではない。僕らがGitHubでマージボタンを押せず、自分たちでgitサーバーを運用するコストに見合うくらい彼の貢献や活動力は貴重なので、すぐにGitHubに移行しよう、とはならない気がする。特に、auto-fiberと呼ばれている目玉機能は彼が作ったがまだコミットされていないのである。
Git化に必要だった作業達
- コミットフック https://github.com/ruby/ruby-commit-hook のGit対応 (いっぱい機能がある)
- Rubyリポジトリ内の
tool/*
スクリプトのGit対応 - 公式のissue trackerであるRedmineのGitリポジトリ対応
- RubyCI や ci.rvm.jp など、Subversionを元に動いていたCIやbotのGit移行
- cgitの用意、運用、アナウンス
最初の2つを僕がやった。半年前にガッと進めて、かつ今回のRubyKaigi中もガッとやってやっと終わった程度には、作業量があった。
懸念した点
- リビジョンがインクリメンタルな数字ではなくなる
- chat botのbisectとかCIがこれに依存していたりするので移行が必要になる。が、git bisect通りの挙動をすればまあいいはず
RUBY_REVISION
の型がIntegerがStringに変わってしまう。これはどうしようもない。すみません。
- 我々のcgitの運用方法が共有ユーザーでsshしてコミット、なので署名しない限りあるコミットを誰がコミットしたのか信頼できない
- GitHubのマージボタンをどうにか使えるように双方向同期するか?
- GitHubへの移行を諦めるならいつかやりたい気はする。実装が面倒そうなのでいつか。
- trunk を master に変えるか?
- Gitでtrunkブランチを使うのは不自然で間違えるのでやめたい。が、CIとかが決めうちしてそうなので、一度に壊しまくらないようそれの移行はタイミングを分ける
- @yugui さんが
git symbolic-ref refs/remotes/origin/master refs/remotes/origin/trunk
というのを共有していた
- 古いバージョンのブランチをGit化するとそれ自体が非互換になりそう
- なので 2.6 までのブランチはSubversionのままやることになった
- Subversion用のViewVCでクローラーがサーバーに負荷をかけており、同居しているcgitがたまに使えなくなる
Git化してよかったこと
- git-svnを使わずにgitで普通〜に開発できる。
- コミットすると https://github.com/ruby/ruby/graphs/contributors に名前が残るのでcontributorのモチベーションが上がる?
- 僕はコミッターになる前の機能のgit blameが自分じゃないのはちょっと寂しいなと思っていた
- まあでも実際そこに名前が残らないから何もしない、という人はどの道コミットしないような気もする :p
- 将来GitHub化するなりGitHubからも同期するなりでPull Requestのマージボタンが押せる世界が近付いた
- 今は多くのコミッター(僕も含む)はPull Requestなしでいきなりtrunkに突っ込んで何かあったらrevertするという原始的な開発手法を採用しているが、trunkのCIが破滅しがちで辛いので、文化を変えた方がよさそう
C99化 [Misc #15347]
古いVisual StudioやSolarisで動かないことに懸念があり、Rubyコアは去年まで長らくの間C89/C90で開発されていた。
どういうことかというと、 //
コメントが書けないとか、boolが使えないとか、関数途中で変数を宣言できないとか、arrayやstructの便利な初期化シンタックスが使えないとか、そういう感じになる。
2019年にこれはしんどい。C99というのは1999年だからC99なんだけど、もう20年たったのだ。もう平成は終わりなのだ。
記事が長くなってきて疲れたので適当にまとめるが、Ruby 2.7からplatformの要求バージョンを上げるなどで対応した。また、C99を全てはサポートしていないplatformがあるが、それら用に重点的にCIを回すことで対策をするなどした。詳細に興味がある人は https://github.com/ruby/ruby/pull/2064 をどうぞ。このプロジェクトは主に僕が主導していた。
インデントのハードタブ廃止 [Bug #14246]
去年の途中まで、Rubyコアはインデントが4つまではスペース、8つに達するとハードタブ、その次の4つはスペース、といった方法でインデントされていた。 僕のエディタ(Vim)で普通にRubyコアのリポジトリを開くとデフォルトではインデントがめちゃくちゃになりがちで、vim-cruby をいれ、cloneする度に .vimrc.local を書きそれを有効にする、といった苦労があった。また、GCCやClangがVMのコードをpreprocessしてJIT用のヘッダを生成する時にタブだけ1スペに変換されるという挙動があり、gdbでデバッグするのがものすごく辛い、という実害があった。
Rubyの二大コミッタのうち、 @nobu さんが上記の方法でコミットし、 @akr さんが常にスペースだけでコードを書いていてインデント方法陣取り合戦みたいになっていて、@shyouhei さんがどっちかにしてくれという話をしたのが Bug #14246。結果全部スペースに倒すことになった。
今はいじった行や新しい行にハードタブが含まれているとbotが勝手にspaceに変換してくるというフックが入っており、revertする時とかに鬱陶しいという話があるが、一気に置換すると、git blameの歴史が一層積まれる他にbackportのconflictが辛そう、という話があり妥協でそのままになっている。まあ、なんか困ったらファイルごとに一括置換していくなりしたらいいと思う。
僕は他に misc/ruby-style.el の更新とかをやったりした。
MinGW, JIT 向けCIの追加
RubyのJITは最初のWindows platformとしてMinGWに対応したため、MinGWのCIが欲しくなった。 AppVeyorでMinGWが動かせるので、AppVeyorにこれを追加した。これは確か僕が最初にやって @nobu さんが修正した感じだった気がする。
また、JITは普通は非同期にコンパイルを行なうが、JITがリクエストされた瞬間同期的に必ずメソッドをコンパイルするようにすると、結構シビアなJITのテストができる。なので、これをWerckerといういままで使ってなかったCIで実行することによって、JITはテストされている。これのおかげでJIT向けにがんばってテストを書かなくても、既存のテスト資産全てがJITの開発に利用できている。すごい楽!!! これは僕用なので一人でメンテしている。
Werckerはコミットごとに1回なんだけど、ランダムで発生する失敗を見つけまくるため、 @ko1 さんが http://ci.rvm.jp/ というCIをメンテしていて、そこでJITのCIが24時間ぶん回っていて、これが2.6のバグ減らしにかなり貢献していたりする。
bundler, bundled-gems 向けCIの追加
@hsbt さんがAzure Pipelineに新たなCIを設定した。僕もちょっと手伝った。 これは標準ライブラリであるがRubyコアに直接存在していないbundled-gemsや、 存在はするが時間がかかるのでCIに参加させにくいbundlerのCIを、並列度が高く新設のAzure Pipelinesで実行しようというもの。
Azure Pipelinesが新しすぎて通知周りがいろいろいけてないとか、単純にbundled-gemsのCIが不安定などでtrunk限定で実行している。 Ruby trunkでbundlerやbundled-gemsが動かなかった時にすぐ気付けて大変便利。 最近 @nobu さんがおもむろに加えた変更でbundlerのテストがコケたので僕がbundlerにパッチを送って直した、ということがあった。
まとめ
Rubyの開発はとても快適になったので今すぐ https://github.com/ruby/ruby にPull Requestしよう!!! *3