YARDタグからRBSを生成する

YARD

YARDはドキュメンテーションツールです。 Rubyのコメントに

# @param [String] a
# @return [void]
def foo(a)
end

みたいな記述を見たことがありませんか? この@paramがYARDのタグ名、[String]がタグのもつ型情報です。aは引数の名前ですね。

YARDではこの型情報を元にリンクを貼ったwebページを生成したりできます。

rubydoc.infoがまさにYARDによって生成されていますね。

RBS

RBSRuby公式の型を記述する書式、およびツールの名前です。

こんなかんじです。

def foo: (String a) -> void

YARD to RBS

では

# @param [String] a
# @return [void]
def foo(a)
end

とかかれたソースコードを読み込んで

def foo: (String a) -> void

とかかれたRBSを出力できたら便利だと思いませんか?

orthoses-yard

というわけで実装したのがorthoses-yardです。

github.com

orthoses-yardはorthosesの拡張です。 orthosesが何かは、RubyKaigi2022で紹介した資料を御覧ください。動画はもうすぐ出ると思います。*1

orthoses-yardではこのorthosesの仕組みに乗っかって、任意のpathのRubyをパースしたYARD情報からRBSを生成します。 「アプリケーションの一部だけしかYARDドキュメント書けてないんだよな」という場合でも柔軟に対応できます。

基本的には@paramタグを引数として、@returnタグを返り値として解釈し、メソッドの型定義を生成しています。 また、@yieldparamタグと@yieldreturnタグにも対応し、ブロックの型表現も可能です。

これ(Rubyコード)から

class Foo
  # @param [String] a
  # @return [Integer]
  # @yieldparam [Symbol] b
  # @yieldreturn [Boolean]
  def foo(a)
  end
end

これ(RBS)を作れます。

class Foo
  # @param [String] a
  # @return [Integer]
  # @yieldparam [Symbol] b
  # @yieldreturn [Boolean]
  def foo(String a) { (Symbol b) -> bool } -> Integer
end

orthosesは柔軟なので、アプリケーションだけでなくgemのRBSもorthoses-yardを使って生成できます。手始めにyard gem自体の型定義をYARDタグを元に生成してみました。yard gemならYARD記法を使いこなして広いパターンでの挙動確認ができそうだったのが選択理由です。

Add Yard gem by ksss · Pull Request #217 · ruby/gem_rbs_collection · GitHub

既存のYARDドキュメントを元にメソッドや定数の型を決定することができています。

RBS自動生成の肝

RBS自動生成最大の問題はmethodの型定義です。

orthosesによるRBS自動生成では、classの名前や継承関係、includeしているmoduleを自動的に洗い出したりするのは得意ですが、 methodの型定義を決めることは困難なのです。

なぜならmethodの型は基本的には人間が決めるべきであり、人間が書くべきです。 正しいものが何なのかわからないと、methodの実装自体が型チェックができないですしね。

def foo
  if cond
    "foo"
  else
    :bar
  end
end

上記例だと静的解析すればfoo: () -> String | Symbolとなりますが、 実際はStringだけを期待していてSymbolが返るのは誤りになるケースも多くあるでしょう。 ましてやattr_accessorだとさらに難しくなります。

methodの型は手で書くしか無いのでしょうか……。

でも自動化したいですよね。

もし既存の資産であるYARDドキュメントを利用できれば、methodやattr_accessorであっても型ファイルを生成できます。

このYARDからの生成によって、このmethod定義問題に良いアプローチができるんじゃないかと期待しています。

YARDを書けばRBSを生成できるならYARDを書くモチベーションを上げることもできますし、YARDとRBSで二重管理になることも防げます。

補足

ちなみにRubyの型界隈に詳しい人は、似たようなツールとしてsordを思い浮かべたと思います。 確かに今回実装したものはsordによく似ていますが、orthosesの拡張なので他のorthosesの機能と組み合わせられるという点が大きなウリとなっています。 例えば「Railsプロジェクトで一部だけYARDを使っている」という場合でも、orthoses-railsrails拡張と組み合わせてプロジェクト全体の型定義をコントロール可能です。

YARDの限界

YARDは非常に歴史あるプロダクトであるため、途中から追加されたkeyword argumentsの対応が弱いように感じます。YARDタグだけではそれがキーワード引数なのかどうか判別できません。もちろんYARD的には問題ないのですが、型定義を生成する場合は情報が足りません。

# どっちも同じ書き方

# @param [String] key
def bar(key)
end

# @param [String] key
def foo(key:)
end

orthoses-yardではMethodオブジェクトからparametersをとってYARD記法とマッチングさせることによってこの問題を解決しています。*2 また、RBSでのoverloadが表現できないことも若干ネックかなと思います。

# YARDだと表現できない
def foo: (Integer) -> Integer
       | (String) -> String

これから

大まかな機能はできましたが、まだまだ細かいTODOが残っているので機能を拡充してプロダクトで使ってみて更に磨きをかけていこうかと思います。

*1:動画では「orthoses-yardが欲しいんだけど誰か作らない?」と言ってますが自分で作ってしまいました。プログラミング楽しい。

*2:引数を移譲するメソッドではこの方法では対応できないので別途対応を考え中

RubyKaigi2022で発表した

rubykaigi.org

speakerdeck.com

RubyKaigi2022で発表した。オンライン参加で、発表は録画だったので、発表したかどうかは奇妙な感じだけど、ともかくできるだけのことはやりきった。

僕にとって、RubyKaigiは2013が最初の参加だった。以来「いつかは発表を……。」と思ってはいたけど、ネタがなかったり、CFPを出したけどRejectだったりだった。

今回、"Rubyに関する国内最大級の国際会議"に初登壇できたのは万感の思いだ。本当に嬉しい。

一方、採用率(採用数 / CFP数)は実は2倍もない。つまり誰にでもチャンスはあって、やれる覚悟があるかどうかが重要なのだ。みんなも発表やってみよ。

KPI

"GitHubリポジトリのスター100個"を目標にしてたけど、結果としては30くらいだった。

https://github.com/ksss/orthoses

やはり何をするものか伝わらなかったり、どうやって使うか分からないから試せないなど、色々と足りないと思うので目標に向かってがんばっていきたい。

トークの反応

良かったのか悪かったのかはよくわからない。なにせ何人の人が会場の席に座ってたのか分からないし、何人がオンラインで観ていただけていたのかも分からない。

Twitterの反応はそこそこいただけたと思う。本当に嬉しかった。Twitterに書いていただいた感想は最後に乗せるが、良さげな反応をいただいて光栄だった。

トーク内容

技術的な詳細(TracePoint系やRBSのマージ系)はRubyKaigiっぽいと思いつつも省略した。実装はコード見て。

代わりにミドルウェアの仕組みや書き方を多めに解説し、エコシステムを作る目標によせた。発表は宣伝効果が高いはずなのでこれを狙った。

実は伝えたかったこと

エコシステムを作りたい

自分にしては大きな規模の構想なので、エコシステムを作って人を巻き込んでより大きなことをやってみたい。モデルとしてはfluentdだ。あんな感じでコミュニティで拡張gemを作ったり使ったりするようになったらいいなあ。

gem_rbs_collection問題

railsアプリケーションの型付けという目標を達成するためには、既存のrails gem用の型では不十分で、大きく直さなければならない。

また、gem_rbs_collectionのメンテナンスコストを下げることも目標の一つなので、orthosesでrails gem用の型を生成する運用を狙って導入PRも作ったが、反応がない。ちょっとやり方が唐突かつ差分が大きかったのが原因かと思うので、これを改善してなんとか導入に繋げたい。

オンライン参加

直近、家族の病気が続いているのもあって間違った選択だったとは思っていない。

思ってはいないけどインパーソンが羨ましい気持ちは十分にある。せっかく"発表者"という話しかけられやすい状況なのに交流ができない。ノベルティは何一つもらえないので、物で思い出が残らない。。。

伊勢海老カレーはいただけたのが救いだった。味も美味しかったけど、作るのも楽しかった。ESM様ありがとうございます。

録画配信

壇上発表ではなく録画配信は初めてだったけど、これはこれでメリットがあるように思った。 トーク以外に力が割ける。

視聴者様の理解に少しでもつながればと事前の予習記事を用意してスライドも当日朝に公開できた。 トーク中もTwitterを覗いてlikeをつけてまわった。質問ぽいのはリプライもした。自分のトークを観てくれて反応までしてくれているのは嬉しい。尊い。 「ちなみに」みたいな補足情報を投げれるのも録画配信のおかげだった。

体調を考慮しなくてもいいので確実に発表できるし、録音もちょっとずつ撮ってそれぞれをつなげるYouTuber方式ができた。

録画方法

録画はKeynoteiMovieを使った。Keynoteには録画機能があるので、これで頭からスライドに合わせて声を撮り、かんだりしたらやり直しつつ少しずつ前に進む。完全に声優のアフレコ現場だ。 Keynoteの発表者ノートに話す"セリフ"を完全に書き出す。これが台本だ。 マスクをしてポップノイズを減らし、ゲーミングヘッドセットのマイクで録音する。ノイズを減らすためにクーラーの音が入らない締め切った部屋で撮った。汗だくになって二晩かかった。途中でスライドを足したりしたけど、時間にして6時間くらい。 Keynoteで録画ができたら、iMovieで編集作業。 編集作業は、間を調整したり、クーラーや洗濯機や風の音によるの背景ノイズを除去したり、音レベルを調整したり、咳払いをカットしたり。Keynoteである程度NGとかはなくなっているはずなので一晩で終われた。 最後にmp4動画とKeynoteファイルを提出。keynoteファイルには日本語での"セリフ"が入っているので翻訳しやすくなったはず。

英訳

本番中に英語の放送も気になって見てみたが、「自分の話したことが英訳される」という経験したことのない感動があった。英語版もYouTubeに上がったらいいなあ。

しゃべり

何年も子供に絵本を読み続けて身につけた技能として、「色々な声色で子供にも分かりやすく書いてある文字を読む」というスキルを身に着けていることに気が付き、これを活用したかった。

もっと堂々とした引き込まれるような分かりやすい発声を心がけたが、実力不足でボソボソとしてしまい怪しいセミナーみたいな謎の雰囲気が出たっぽい。

別セッション

自分の動画はイヤというほど観ているので裏番組のSyntax Treeのセッションも観ようと思ったけど、Twitterで思いの外反応をいただけてこちらの反応に忙しくて観れなかった。

スポンサー

僕の表現方法がまずくて、まるでorthosesプロジェクトにGitHub社とWantedly社がスポンサーとして付いているかのような表現になってしまった。 正しくは、2社とも個人のとしてGitHubスポンサーをいただいているだけで、orthosesプロジェクトとは関係ないです。なんなら個人スポンサーがついた理由も分からずにいる。

もしものときのために

もし通信障害が起きたときのためにYouTubeに動画をuploadしてた。こちらは使う機会がなくてよかった。配信チームに感謝。

Railsアプリケーション

やっぱりRailsアプリケーションの型生成例を出したほうがインパクトがあるとは思うので、これはなんとか提示したかったところ。手元では作れているけど、適当なRailsアプリケーションを探したりとかまでできなかった。そのうちやりたい。

雑談

このまま誰とも話さずRubyKaigiを終えるのは辛かったので雑談に加わった。 めちゃ楽しかった。

トーク中のTwitterの反応

さいごにトーク中のTwitterの反応を掲載します。 無許可掲載すみません 🙏

去年ブログを作っていた

Next.jsで作ったプロジェクトをvercelに乗せる形でブログを作っていた。2021年の3月に。

https://ksss.ink/

いろいろ手作り感があるけど、無料でお手軽に自分のサイトを作ってデプロイするという塩梅はおもしろい。

Next.jsは体験がよく、JS初心者にとっては考えることが少なくてとっつきやすかった。vercelもGitHubと連携するだけでデプロイで来てDNSレコードまで管理できる。無料で。すごい。

いろいろガシガシ記事を書くかと思いきや1年くらい新規記事が書けずに停滞している。難しいね。ブログ。

メリットは"自由"という他ないんだけど、デメリットとして様々なサービス連携ができない点がはてなブログ等と比べるとある。はてブ数で"発信力"を決めるサービスとか。

とりあえず作ったしここに記録を残しておく。1年前くらいに書いておくべきだったなあ。

2021年なにしたっけ

くらし

二人目の子供が大きくなっていき、それにつれて一人目の子供も刺激されて成長していくさまを見ながら一緒に暮らしていた。 子供のことは生活のほとんどを占めるが、プライベートの詳細には立ち入らないのであんまり書くことがない。

しいていうなら睡眠の重要性を再確認した。 夜ふかしした次の日は何をやってもダメダメだし、子供の対応でどうせしっかりとは寝れない。 かといって早寝が続くとやりたいことが何もできないので鬱憤が溜まっていく。 今は夜ふかしと早寝を交互に繰り返している。

買い物

洗濯機

洗濯機がドラム式になった。 ドラム式がいいらしいと聞いていたり、なにかと天気に左右される生活を改善したいという考えが夫婦間で大きくなってきたのでエイヤと買ってみた。 こういうのは二人共苦手としているが、がんばってよかった。 今は夜に洗濯・乾燥する生活に落ち着いている。

ハンドディスペンサー

子育てしていると、とにかく手を洗う。コロナ禍もあってさらによく手を洗う。 こんなに手を洗うなら、もしかして自動で泡が出てきたら便利なんじゃないかと思って買ってみたらやはり便利だった。 ただ、最初に買ったのは作りがイマイチで、泡が電池部分に入り込んでしまい、最終的に半年くらいで故障した。 二個目は電池部分に泡が入り込まないように完全に分離しているタイプに変えてみた。電池が大量にいる。

Kindle Paprewhite

もともと7年くらい使っていたし、画面には傷がついていてライトが付けられないし、typeCになったし、多分早くなってるだろうと思って買い替えてみた。 あいかわらず漫画はiPadで見ているが、小説にはやはりよい。これで三体を読み切った。

iPhone13 pro MAX

10からの買い替え。ひょんなことから10万円分のAppleカードをもらったので使うタイミングを伺っていた。 これまでは裸で使っていたが、考え方を変えてケースとAppleCareをつけた。高いもんなんだから保護もコストを掛けていい。 Apple純正のケースにMagsafeでくっつくリング用のベースをつけてさらにリングを付けてる。 充電もMagsafeのやつを買ってくっつけてる。このくっつけるのが楽しいので充電が捗る。

いろいろ最初だけ読んでは途中でほったらかしというパターンが多かった。 読み切れたのは三体ぐらいで、しかも半年ぐらいかかった。読み切れた事自体は達成感がある。

読み終わったあとも三体のことについて、しいては宇宙、人生について考えている……。

来年はもう少し読みたい。

健康

実は5月に全身麻酔の手術入院をしていたんだけど、この出来事をまとめようまとめようと思ってずるずる年が終わってしまった。 病気というわけではなくて、耳の一部を小さく切り取っただけ。いつかブログにまとめたいが、しない気もする……。 ともかく全身麻酔の体験は興味深かった。痛みというより意識に対する麻酔という感じ。 この手術の直前に「全身麻酔の原理は解明されていない」という記事を見て大変興味深かった。

全身麻酔が効く仕組みから「意識の発生源」が見えてきた - ナゾロジー

OSS

仕事が一段落した頃、Rubyの型についてキャッチアップしようと触ってみたらドハマリした。 これは実用的に使えるツールで開発環境を大きく変えると直感した。 仕事のRailsプロジェクトにも入れて便利に使った。 PRもいくつか送っていて、8月くらいから寝る前のちょっとした時間を使ってmergeされたのもされなかったのも合わせて60個ぐらい送っていたらしい。 仕事用のコードでエラーになっている部分のうち、これはおかしいのでは?と思った部分をひたすら改善していった。なので実用性を重視した改善が多いと思う。いやまあ興味本位での手当たりしだいというPRも多いとは思う。。。 使い始めた頃に比べたらだいぶ使いやすくなったきがするけど、まだまだ発展途上なのでもっと便利にしていきたい。 作業時間の捻出が課題。

来年

睡眠を大事にがんばりすぎない感じでがんばっていきたい。

三体 / 劉慈欣

三体の感想を書くことは不可能だ。


単純に三部作で相当な分量があり、しかも一部毎にジャンルが違うと思うくらい多様な話が盛り込まれている。三体の感想を一言で述べるということは、言葉を原子に見立てるならば、原子を無限に圧縮することになるため途中で核融合が発生してしまう。そのため永遠に到達できない。

よってこれから語るのは三体の感想ではない。ただの個人の日記である。


2021-12-23。僕は三体Ⅲを読み終えた。

僕は三体を2021年5月頃に読み始めた。Ⅰだけでも歴史あり、科学あり、VRあり、サスペンスあり、人類賛歌ありと盛りだくさんだった気がする気がするというのは、現実での時間の経過もあるが、本編の話のスケールが大きすぎて、Ⅰは遠い過去の話のように感じるのだ。

途中間も空いたりしているが、Twitter検索によるとⅡの上を読み終えたのが10月となっている。Ⅱを読み終えたのは11月末だ。

そう考えるとⅢは1ヶ月程度と、僕にしてはかなり早く読んだことになる。僕は「年内に読み切る」と目標を立てた。

目標とは、他の余計なことをしないために自分にかける呪いだ。この呪いのおかげもあってか、それとも三体の魅力もあってか、加速的に読み進めていった。


これだけの壮大なスケールにも関わらず、三体は読みやすい。難しい表現は出てこず、かなりわかりやすさに配慮しているんじゃないかと感じた。表現は淡々としていて、それでいて壮大なシーンも僕でも難なく思い描けるように書かれている。それゆえ、壮大なシーンがダイレクトに精神に届くため、〈水滴〉のシーンや〈紙切れ〉のシーンは精神的に落ち込んだ。


僕個人は、壮大な人類の歴史にとってほんの小さなひと粒の粒子でしか無い。そう考えると生きる意味についても、どうしても考えさせられてしまう。

僕は今年いっぱいでMICINを退職し、来年1月からREADYFORへ転職する。しかし、そんなことは人類にとっても宇宙にとっても些細なことだ。僕がこの人生で何をしたのかなんて、多く見積もっても百年と残らないだろう。宇宙規模で見れば、全ての個人の営みは等しく希薄化され、一切意味がないかのように思える。


はたしてそれが真実なのだろうか?


本書ではこうも語られている。

”きょうを楽しめ”というのがいつだって正しい道だったんだからね。

私達の目では原子は目に見えないほど小さい。一つ欠けたところで大して変わらないだろう。代わりはいくらでもいる。


しかし世界はその原子でできている。


宇宙規模で見ればちっぽけな命でも、宇宙の歴史はその生命の一つ一つで紡がれている。

宇宙は大きい。でも、生命はもっと大きい。

生きる意味なんてどうでもいい。生きることが意味なのだから。

Rails MVCしか知らなかったバックエンド開発者が、最近のフロントエンド開発を学んで得た知見


これは、これまでRailsの古き良きMVCな開発体制しか知らなかったバックエンド開発者が、環境が変わってフロントエンド開発を学ばざるをえなくなった者の記録です。

歴史的に正しい事実を書いたものではなく、私個人の理解を整理するための妄想日記です。

 

私はこれまではWebアプリの開発ばかりやってきて、RailsでHTMLテンプレートエンジン使ってviewを作るスタイルでしか開発してきませんでした。

 

f:id:ksss9:20210221232753p:plain

しかし、ネイティブフロントとWebフロント両方があるアプリケーションが開発されているところを見て、ある事を思いつきました。

 

「Webフロントもネイティブフロントのように開発できれば、バックエンドエンジニアはバックエンドに、フロントエンドエンジニアはフロントエンドに分業できて、開発しやすくなるのでは?」

 f:id:ksss9:20210221232937p:plain

この気付きが超重要でした。このイメージを持てたおかげでフロント開発の意義がスルスル入ってきました。

 

Railsフルスタックフレームワークなので、RailsでHTMLを生成して返すことができますが、その開発体制だと、「バックエンドエンジニアは、得意なバックエンドを沢山書いて、不得意なフロントエンドをちょっと書く。フロントエンドエンジニアは得意なフロントエンドを沢山書いて、不得意なバックエンドをちょっと書く。」ということが起こっていたように思います。

だんだんと、「ちょっと不得意な人が書いたコード」が増えていきます。レビューで指摘しあえれば良いのですが、なかなか完璧にはいきません。

 

もし、RailsのV部分を引き剥がして、フロントエンドが得意なエンジニアに開発を任せることができれば、RailsAPIだけでよくなり、ネイティブフロントもWebフロントも同じようにAPIを提供する形で開発できます。

 

どうやらこれを実現するのがSPA(シングルページアプリケーション)という事なのかなと思います(多分間違ってる)。

だからこそRailsAPIモードができたり、PWAだとかいう話が出てきたのかなと想像します。

 

そしてSPAを作りやすいReactやVueが盛り上がったと予想します。このSPAをS3とかなんかいい感じのプラットフォーム(これがNetlifyとかなのか?)にデプロイすれば、サーバーを管理せずとも簡単なアプリなら作れるし、Lambdaとかと組み合わせれば結構なことまで出来ちゃいます(これがAWS amplify?)。いわゆるLinuxサーバーを立てたりしなくてもマッシュアップサービスとかが作れてしまうので、サーバーレスとかも叫ばれてきたのかなあ?

 

このSPAも細かく言うと色々やりようがあるようです。

 

例えばブラウザでページを開いたときにJSが起動してHTMLを構築するシンプルな方法をCSR(クライアント サイド レンダリング)と言うそうです。だいたいのことはCSRで行けるし、create-react-appが出てきて学習コストも下がってきたので、今も結構使われてるのかなと予想します。

 

しかしながらGoogleクローラーがJSを実行するかしないか微妙な時期があったのか、最初のJS実行時のラグを気にしてか、サーバーサイドでReactなりVueなりを実行してHTMLを生成する手法が出てきました。これがNext.js(React)とかNuxt.js(Vue)ということかなと思います。これがSSR(サーバー サイド レンダリング)と言うそうです。

 

Next.jsの方をちょっと触っただけですが、動的画像生成を実装した経験からも、next/imageだけでもかなり便利そうです。

これは、libvipsを使って動的に画像を生成して、ブラウザ毎に最適な形式でファイルを作り、キャッシュ用のヘッダーもいい感じにして、なんなら専用サービス(Vercel?)にデプロイすれば生成したファイルをキャッシュしてくれます。

これだけでも導入する動機になりそうです。触ってみた感じ、S3のファイルとかでもドメインさえ設定すればなんでもいけそう。

ただし、この画像生成機能はビルド時ではなく画像へのリクエストがサーバー上に来たときだけに使えるので、SSRでは使えても後述するSSGでは使えないようです。

 

さて、そんなSSRも、GoogleクローラーがJSを実行してくれるようになったり、CDNが一般的になったり、そもそもサーバー側の要素が増えるので、実装が複雑になってしまいがちでした。そこで新たな策として、予めビルド時(デプロイ時)にDBなりを見てもいいからHTMLを生成しておいて、これをCDNなりで静的ファイルを配信すれば、サーバーのプロセスもいらないし、なんならDBもいらないし、既にレンダリングされているから表示も早いしでいい事ずくめとなったのがSSG(スタティック サイト ジェネレーター)なのかなーと予想します。

これがNext.jsの今の売りっぽいです。CSRに似ていますが、HTMLのあらかじめ生成度合いが、CSRが0だったのがSSGで80〜100になったイメージです。もちろんAPIをクライアントサイドから叩いて動的コンテンツを追加することもできます。なんかこの辺からJamstackと言うらいしいっぽい感じがします。ブログとかほとんどSSGでいけそうです。

 

更にISGやISRなんてのもあるっぽいですが、まだまだキャッチアップ中です。。。 

 

さてさて、こんなことを最近学んでみて、また、少しNext.jsアプリケーションを作ってみて、昔ながらのRailsアプリケーションに対する考え方が変わり、ようやく時代に追いつけてないことが自覚できたレベルにはなれたかも?と思いました。

これまで作ってきたものは、サーバーサイドでほぼ同じHTMLを作って返すものなので、最近のフロント事情からしたら無駄が多いのかもと思えてきました。

また、Next.jsをcreate-next-appで作ってみると、驚くほど簡単にコンテンツが作れてしまいます。フロントはからっきしだった私が、簡単なアンケートサイトみたいなやつなら作れたので確かです。

Next.jsはルーティングも簡単に(ファイルパスを切るだけ)できるので、最早VもCもフロントでできてしまいます。

そして、どうせSSRするなら、そのnodeサーバーからprismaなりでDBを叩いて動的コンテンツを返せれば1プロセスだけでいいし、認証もFirebase AuthenticationやAuth0なんかで作れてしまいます。

そうなると「もはやRailsはいらない!」みたいな思考になる人が現れてもおかしくないなと思う気持ちも理解できます。

もちろんRailsでHTMLを生成する方法も、状況によってpros/consあるかとは思いますが、自分が見ないようにしていたものの大きさを急に知って、驚くばかりです。

っていうかもはや私としては、バックエンドエンジニアとは?自分の存在価値は?みたいな気持ちです。

 

これから、改めて「どうやってアプリを作るのか」を考え直してみたいと思います。

entrykitがM1 mac(Apple Silicone)で実行できないときの対処法

現状

M1 macでDockerのApple M1 Tech preview 7 *1 を動かしています。

golang:1.15 imageがたまたま手元で動いていたのでこれで試します。

entrykitはv0.4.0のバイナリを配布していますが、これを実行しようとすると以下のようにgoレベルで落ちてしまい実行できません。

$ docker run --rm -it golang:1.15 bash
root@aa08b9d02add:/go# wget https://github.com/progrium/entrykit/releases/download/v0.4.0/entrykit_0.4.0_Linux_x86_64.tgz
root@aa08b9d02add:/go# tar -xvzf entrykit_0.4.0_Linux_x86_64.tgz

root@aa08b9d02add:/go# ./entrykit
runtime: failed to create new OS thread (have 2 already; errno=22)
fatal error: newosproc

runtime stack:
runtime.throw(0x84a820, 0x9)
    /usr/local/go/src/runtime/panic.go:527 +0x90
runtime.newosproc(0xc820026000, 0xc820035fc0)
    /usr/local/go/src/runtime/os1_linux.go:150 +0x1ab
runtime.newm(0x8e5980, 0x0)
    /usr/local/go/src/runtime/proc1.go:1105 +0x130
runtime.main.func1()
    /usr/local/go/src/runtime/proc.go:48 +0x2c
runtime.systemstack(0xa2b6e0)
    /usr/local/go/src/runtime/asm_amd64.s:262 +0x79
runtime.mstart()
    /usr/local/go/src/runtime/proc1.go:674

goroutine 1 [running]:
runtime.systemstack_switch()
    /usr/local/go/src/runtime/asm_amd64.s:216 fp=0xc820020770 sp=0xc820020768
runtime.main()
    /usr/local/go/src/runtime/proc.go:49 +0x62 fp=0xc8200207c0 sp=0xc820020770
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1696 +0x1 fp=0xc8200207c8 sp=0xc8200207c0

あまりパソコンに詳しくないのですが、 downloadしたものはx86_64(amd64)環境のbuildと思われるので、aarch64(arm64)環境では動かないのかなと推測します。

また、entrykitは長らくreleaseされておらず、作者も興味を失っている気がする(憶測)。

界隈で一瞬流行ったので、なんとなく使われているということが多いのではないでしょうか。

考えうる解決法

解決方法はいくつか考えられます。

1. entrykitを捨てる

そもそもprehookだけの利用の場合、entrykitは必要ありません。

prehookコマンドの正体は、

$ prehook ① -- ②

となっている場合、

  1. ①を外部コマンドとして実行する(複数可)。その後②でexecする。
  2. --以降がなく①だけの場合は①でexecする。

ただこれだけです。

よって

Dockerfile

ENTRYPOINT[ \
  "prehook", "ruby -v", "--", \
  "prehook", "bundle install", "--" ]

Dockerfile

ENTRYPOINT[ "./entrypoint.rb" ]

entrypoint.rb

#! /usr/bin/env ruby

system("ruby -v")
system("bundle install")

exec *ARGV

と別ファイルに切り出せます。

もちろんrubyじゃなくshellでもいいです。

Multi stage build

これだけなら話は早いですが、entrykitを使うからには、おそらくprehook以外のentrykitコマンドと組み合わせたいという使い方が多いと思います。

そんなときの対処方法としてMulti stage buildがおすすめです。

FROM golang:1.15 AS entrykit
RUN go get -v -ldflags "-s -w" github.com/progrium/entrykit/cmd

とすると、Docker環境内でdownloadとbuildが始まり、/go/bin/cmdという実行ファイルが生成されます。これがentrykitの実体です。 cmdじゃなくentrykitという名前でbuildする方法がいまいち分からなかったので誰か教えて下さい……。-oはgo getでは使えないらしい。

ともかくこれで、M1環境でしかもgolang version 1.15でbuildしたentrykitのbinaryが手に入ります。簡単。

次にentrykitを使う場所で、entrykitの実体だけCOPYしてきます。

Dockerfile

COPY --from=entrykit /go/bin/cmd entrykit
RUN entrykit --symlink

これで各種コマンドが生成されます。symlinkはCOPYできないのでCOPY後に--symlinkするようにしましょう。 また、使うコマンドが1つだけなら、

COPY --from=entrykit /go/bin/cmd render

だけでもいいです。お好みで。

Cross buildしてbinaryをプロジェクトリポジトリに置いとく

手元でCross buildして複数環境用にbuildしておき、生成されたbinaryが使われるようにuname -mとかで切り替える。。。

面倒そうな上にMulti stage build以上のメリットはなさそうです。

結論

  • entrykit本当にいるのかどうかよく考えよう。
  • Multi stage build便利。

課題

Multi stage buildの場合、entrykitのファイル配置がおかしいのか、go getでversion指定する方法がよく分からない……。go歴0秒なのでご教授いただけると幸いです。

逆に言うとversion指定しなくてもいいのでメンテナンスレス!

感想

バイナリ配布型のプロダクトの場合、fat gemなどと同じく継続的なreleaseが必要でメンテナ負担が大きいのではないかなと推測します。(作ったこと無いのでなんとも言えないけど)

その点、スクリプト型言語なら再buildなどが必要ないので、 ミニマムに使えるけどメンテナンス負担が大きいバイナリ配布と、 導入は大げさだけどメンテナンス負担が小さいスクリプト配布と使い分けがありそうですね。

とはいえ今回のように都度buildすりゃいいじゃんと言われればまあそうなのかも。