えいのうにっき

a-knowの日記です

「みんなのGo言語」が良かったので、自分のためだけのCLIツールを作ってみた

言わずと知れた書籍「みんなのGo言語」。発売直後くらいに購入してはいたんだけど、目次あたりをパラパラ見て「あ、これは2,3日集中してガッとやりたくなるやつだ」と思って。で、タイミングを見計らっているうちに年末年始休暇に突入してしまったのだけど、そのタイミングでちゃんと「ガッ」とできたので、今日はそのお話をば。

僕と Go 言語

僕の Go 言語遍歴について少しだけ触れておく。

以前、個人的にちょっと作ってみたいツールがあった時期があって。そのとき既に Go がそこそこもてはやされていたこともあって、そのツールは Go で書いてみていた。↓がリポジトリなんだけど(コミットログを見てみたら first commit は2014年11月だった)、だいぶひどい書き方をしてると思うので閲覧注意で。w

github.com

ただ、実際にやってみての感想としては、「ディレクトリ構成とかパッケージの分け方とか、さっぱりわかんね」「セオリーどおりに書いてみたかったけど、そのセオリーがわからん」といったもので、かといってその疑問に応えてくれるものに出会うこともできず、結局そのまま放ったらかしにしてしまっていた。

すでに読んだ人はわかると思うけど、「みんなの Go 言語」はまさにそういう点について書かれている本(特に第1章が良かった)なので、2年という時を越えて僕の疑問に応えてくれる本が出た!という感じで、そういう意味でも待望の一冊だった。

この本を読んで「やってみたい」と思ったこと・やったこと

で、この本を一通り読んでみて、僕は以下のようなことを「やりたい!」と思った。

  • (学習目的と割り切ってしまってよいので、)なにか CLIツールを作る
  • ちゃんとテストを書いて CI を回す(lint やカバレッジも)
  • Go のクロスコンパイルの恩恵に預かってみたい
    • Mac / Windows/ Linux 、どの環境でも動作するバイナリをリリースしたい
    • GitHub の releases まわりもよくわかってなかったので、ここらへんもついでに触ってみたい

で、いずれも実際にやることができたので、ひとつずつ紹介していく。

なにか CLIツールを作る

多分これが最初にして一番のハードルで、僕のように「CLIツールを作ってみたいな〜」と思う人はきっとたくさんいるんだろうけど(それこそ、この本を読んだ人なら全員が思うんじゃないかな?)、「じゃあ何をCLIツールとして作るのか?」となったときにノーアイデアでフィニッシュです、となってしまうのが大半なんだと思う。

それだけは避けたいのと、かといってあんまり難しすぎるとそこで折れてしまうので、「"自分" に関するCLIツール、 a-know を作ることにしよう」と決めた。

例えば以下のような挙動をするようなツール。これなら作れそうでしょ?w

$ a-know blog
http://blog.a-know.me

実際の作業は以下の p-r。

github.com

github.com

実装を始めるにあたって、「みんなのGo言語」のパッケージ分けについての記述がとても参考になった。

  • Go でプログラムを書く場合、1つのリポジトリ内に多数のパッケージが必要になることはあまりない
  • CLIツールのバイナリのみを成果物とする場合は、単純にルートディレクトリに main パッケージのコードを配置する
  • バイナリをメインの成果物とし、同時にそのライブラリも提供したい場合は lib ディレクトリを作りそこにライブラリのコードを配置する
  • ライブラリをメインの成果物とし、同時にバイナリも提供したいような場合には cmd/a-know のようなディレクトリを作りそこに main パッケージのコードを配置する

その他、第4章「コマンドラインツールを作る」の「4.4 サブコマンドを持ったCLIツール」とにらめっこしながら、実装を進めた。

ちゃんとテストを書いて CI を回す(lint やカバレッジも)

最初のツールを書いたときの引け目のひとつが、テストを書けてなかったこと・書き方がよくわかんなかったこと、だったので、今回はそこもちゃんとしたいなと思っていた。

で、せっかくテストを書くのなら CI を回せるようにしたいし、100%のカバレッジも(趣味だからこそ)目指したい。あとは仕事柄、Go のコードを書くことも今後増えそうだったので、lint も掛けることで「Go の書き方」を身に付けたい、とも思った。

僕は CircleCI がすきなので、それを使って上述のようなことにトライしたのが以下の p-r。

github.com

github.com

たぶんできてると思う...。。

カバレッジ管理には coveralls を使っていて、カバレッジの投稿には mattn さんの goveralls を使っている。上述のとおり、今回は CLIツールのバイナリのみを成果物とする 目的なので main パッケージのみで構成されるコードとしたんだけど、もしこれを複数パッケージ構成とした場合、cannot use test profile flag with multiple packages が出るっぽい。普通のテストだけならパッケージごとにテストすればいいだけっぽいけど、カバレッジを投稿するためには各パッケージごとのプロファイルを出力させたあとにそれをマージする必要がありそうだった。ご参考まで。

Go のクロスコンパイルの恩恵に預かってみたい

せっかく Go でツールを書いたのなら、クロスコンパイルでもって環境ごとのバイナリを生成させてみたい。そう思うのは人のサガってもんでしょう!

ついでに、「GitHub の releases でキレイにリリースをしている人」というのをずっと羨ましく眺めていたところもあったので、それにもぜひ挑戦してみようと思った。

で、そのあたりの p-r は↓こちら。

github.com

結論としては gox ghr を使えば一瞬で出来てしまうんだけど、今回はまぁそれがわかったというだけでも良しとする。良しとした。ただ、これらの最新版を使うには Go のバージョンが 1.7 である必要があるらしかったのだけど、CircleCI のコンテナはまだそれに追随できていないらしく、新しい Go を自力でセットアップしてやる必要はあった。

github.com

リリース自体は、少し試行錯誤したのだけど、「tag を打って push したタイミングで行なう」、ということにしてみた。

github.com

感想とか今後

てなわけで、やりたいなと思ったことは一通りできたし、思いつく限りのサブコマンドの実装もしおわったので、いったんのまとめとしてこのエントリを書いている。a-know のリポジトリ本体は以下。

github.com

あくまで僕自身に関するツールだけど、 go get すれば誰でも使えるはず。。w

今回の取り組みによって、とても浅くはあるけど、今までよくわかってなくて引け目に感じてたようなところがわかって、そういった気持ちも少しやわらいだので、それはよかったなと思っている。

今後も a-know は Go 回りのいろんなことを試すための良い「砂場」として付き合っていきたい。直近では、もう少し複雑なことを行えるようなサブコマンド(例えばコマンドラインからツイートする、とか...)をちょっとずつでもいいから拡充していきたいなと思っている。

ただ、現時点でも xargs と組み合わせたら地味に便利で(例えば $ a-know twitter --url | xargs open とかってやるとブラウザで自分の Twitter アカウントページを一発で開ける)、これは事前に想定してなくてなんかウケた。

自分のためだけのCLIツール、みなさんも作ってみると面白いかも?