おそらくはそれさえも平凡な日々

ghq v0.11.1 でGoの依存取得や一括アップデートが便利になりました

motemenにcollaboratorに追加してもらったので、滞留していたissue及びpull requestを大体さばきつつ、機能追加などをおこない、先程v0.11.1をリリースしました。GW中になんと45個のpull requestをマージしました。

今回の目玉は go get/import の機能追加です。具体的には、ghqで取り込んでいるリポジトリの一括アップデートや、Goの依存の一括取得が簡単になりました。

変更点

詳しくは、 v0.10.0以降のCHANGELOGを見てもらえればと思いますが、一部ピックアップして取り上げます。

ghq get/import への入力にホストも受け付け可能に

https://github.com/motemen/ghq/pull/119

実はこれまで以下のようにホスト名をつけられなかったのですが、それができるようになりました。ghq import への入力も同様です。

% ghq get github.com/motemen/ghq

go-import metaタグサポート

https://github.com/motemen/ghq/pull/120

go get がパッケージのリポジトリを探す時に、 meta name="go-import" というhtmlタグをヒントにするという仕様がありますが、これに準拠するようにしました。

Go固有の仕様なので、対応可否は微妙なラインですが、Goを書く身にとっては欲しい機能なので、入れることにしました。

これとホスト付加が可能になったことにより、以下のコマンドでGoのプロジェクトの依存の一括取得が可能になりました。

% go list -f '{{join .Deps "\n"}}' | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | ghq import

これは、$GOPATH/src が廃止されるGo Modules時代に非常に便利でしょう。

go import --parallel/-Pオプション (Experimental)

https://github.com/motemen/ghq/pull/136

go import での並列取得が可能になりました。6並列決めうちにしています。多くのブラウザの同時接続数と同じなので、これくらいであれば多分お行儀も悪くないはず。

これにより、以下のコマンドで、手元のリポジトリの一括更新を高速にできるようになりました。

% ghq list | ghq import -u -P

ただ、以下のように xargs を使う場合に比べて速いわけでもないので、もしかしたらrevertするかも知れません。

% ghq list | xargs -L1 -P6 -I@ ghq get -s -u @

ghq get --vcs=

https://github.com/motemen/ghq/pull/72

ghq get 時にVCSを明示的に指定できるようになりました。

ghq get --look

https://github.com/motemen/ghq/pull/112

ghq get 直後にcloneしたrepositoryのディレクトリ配下に移動できるようになりました。

repository ownewの補完にユーザーを使わない設定を可能に (k0kubun++)

https://github.com/motemen/ghq/pull/118

#81:When there is no slash in the argument, complement URL as $USER/repo で、 ghq get hoge などとした時に、 https://github.com/$USER/hoge からリポジトリを取得するようになりましたが、元の挙動のほうが好みだという意見もあり、設定で切り替えられるようになりました。

% git config --global ghq.completeUser false

上記のように、ghq.completeUserfalse を設定しておくと、リポジトリ名をユーザー名の位置に補完してくれます。例えば、 ghq get peco とすると https://github.com/peco/peco を取得します。

ghq list --vcs= (msh5++)

https://github.com/motemen/ghq/pull/155

ghq list --vcs オプションにより、VCSによる絞り込みが可能になりました。例えば以下のように一括git gcできるので便利です。

% ghq list --full-path --vcs=git | xargs -I@ git -C @ gc --aggressive --prune=now

bazaar(shigemk2++), fossilサポート

これで、go get が対応しているVCSをghqも一通りサポートしました。めでたい。

その他

まとめ

ということで是非ご利用ください。だいぶメンテして、テストも追加されて触りやすくなっているので、pull requestもお待ちしています。

今後はもう少しテスト追加するのと、チューニングなどに取り組みたい。具体的には ghq listをもう少し速くしたい。これはどなたか取り組んでくれても嬉しいです。

お知らせ

日頃お世話になっている皆様へのお知らせです。なるべく多くの方に直接お伝えしたかったのですが、この場でのお伝えになってしまった方には申し訳ありません。

はてなを退職します。4/17(水)が最終出社でした。所属は5/31(金)までです。

ずっとはてなで働きたいと思っていましたし、この絶好のタイミングで辞めてしまうのは勿体無いという気持ちもあります。ただ、次の挑戦に関して時間的な制約もあったため退職させてもらうことになりました。この詳細はまた別途お知らせできればと思っています。

2014年に現在のはてな東京オフィスの一人目のエンジニアとして入社し、その後、チーフエンジニアと、Mackerelのプロダクトオーナー(マネージャー)を兼務してきました。

チーフエンジニアとして組織に、Mackerelではプロダクトとビジネスに関わりました。入社当時、私一人だけだった東京オフィスのエンジニアも今では二桁になり、リリース直前で売上のなかったMackerelも今や事業の柱の一つです。組織とプロダクト両面に良いフェーズでトップレイヤーで携われるという、社内でも他にいない稀有で贅沢な体験をさせてもらいました。良い経験になりましたし成長もさせてもらいました。本当に感謝しています。

自分で退職を申し出ておきながら勝手なことを書きますが、まだ辞めたくない気持ちもありますし、まだ辞めることに実感が湧いていない部分もあります。

私はいなくなりますが、今後のはてなのエンジニア組織やMackerelに関しては心配していませんし、むしろ楽しみにしています。

チーフエンジニア陣には、はてな新卒から順調にキャリアを重ねてきた id:shiba_yu36 や、中途で経験豊富な id:onk が加わり、人が入れ替わりつつもスターエンジニア揃いの盤石な体制になっています。

Mackerelも順調に成長し、チームも拡大しています。開発チームはディレクターの id:daiksy 及びテックリードの id:astj のもと成熟したスクラムチームを形成していますし、ビジネスチームも完全に立ち上がり、開発チームと同じくらいの人数になりました。特に日本でおそらく一人目のCREである id:a-know はプロダクトに対する情熱と理解度が高い頼もしい存在です。

まだまだビジネスと開発の垣根のないスタートアップ感も維持しています。会社としても注力プロダクトであるため大きな支援もしてもらっています。ですので、はてなもMackerelもまだまだ面白いフェーズですし、エンジニアも積極的に採用しています。

社長の id:chris4403 さんは「はてなを長く働ける会社にしたい」ということをおっしゃっています。これはなかなか今のWeb業界からすると珍しい、チャレンジングな目標だと思います。だからこそ私自身もずっと働きたいと思っていました。

はてなは良い会社です。採用は厳選していて、善人しかいないので、性善説前提で動ける良さがあります。制度や福利厚生も配慮があってスタッフが働きやすい環境を整えようとしているし、今後もしていくでしょう。フリーランチの甲斐もあってか、社員同士の気心がしれていて仲がよく気持ちよく働けるのも良い点です。色々、次の会社の組織文化を形成していく上で見習いたいと思っています。

憧れの会社であるはてなで働けて幸せでしたし、今後も憧れの会社であり続けてほしいと思っています。Mackerelも僕の手を離れますが、今後どのようなプロダクトになっていくか楽しみにしています。

ウィッシュリスト

依存ライブラリのLICENSE同梱のためのgocreditsというツールを作った

https://github.com/Songmu/gocredits

Goのプロジェクトの依存ライブラリのLICENSEを抽出して、CREDITSという単一ファイルに書き出してくれるツールを作りました。もともとのアイデアは同僚の id:tarao によるものです。Go Modulesを使っているプロジェクトで以下のようにすれば、CREDITSファイルを書き出してくれます。出力されたCREDITSファイルは実行バイナリを配布する時に有用でしょう。

% gocredits -w .

Goは実行バイナリを簡単に配布できるので便利ですが、ソフトウェアを配布するときは、依存ライブラリのLICENSEを考慮する必要があります。具体的には恐らく依存ライブラリのライセンスのコピーを同梱する必要がありそうです。リンクだけではだめでコピーを含めた方が良さそうです。

例えば、MITライセンスには以下の記述があります。

The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.

BSDライセンスにも以下の記述があります。

Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

Goの場合、例えサードーパーティライブラリを使ってないとしても、Goの標準モジュールのライセンスの同梱が必要です。以下のissueでも議論されていて「ちゃんと同梱しましょう」と書かれている。

https://github.com/golang/go/issues/19893

この辺りは、スマホアプリの世界だとちゃんとしていて、ライセンス表示画面をちゃんと設けていたり、そのためのライブラリも揃っています。他にもChromeだとabout:creditsにアクセスすると、依存ライブラリのライセンス一覧を表示できたりします。

Goの世界だとあまりそういうツールがなさそうだったので、以前 id:tarao がfireworqを公開時に、ライセンス一覧をCREDITSという名前のファイルに書き出すスクリプトを作っていました。これは、glide.lockから依存を抽出して、vendor配下からLICENSEを集めてきています。

gocreditsはここからアイデアを拝借しつつ、Go Modulesに対応させたものです。$GOPATH/pkg/mod 以下から依存を取り出すようにしていますが、Go Moduleに非互換変更が入った場合動かなくなるかも知れません。なのでAlpha Qualityです。

Goで実行バイナリを配布する場合、アーカイブにこのCREDITSファイルを同梱すると良いのではないでしょうか。なんと、goxzはデフォルトでこのファイルの同梱に対応しています。

既に個人のツールでは使い始めています。是非ご利用ください。OSSのLICENSE周りにそこまで詳しいわけではないので何かあればご指摘ください。ゆくゆくは依存ライブラリのLICENSEの不備だったり、非互換検出などもおこなえるようになると嬉しいと考えています。

最近のGo Modulesプラクティス ~ ghqユーザーの場合も添えて

最近Go Modulesを使っていて、だいたいプラクティスが定まってきたのでまとめてみる。

個人的な結論

2つのモード

$GOPATH/src にプロジェクトを置いていると、今(Go 1.12)の標準動作はGOPATHモードになる。これは、$GOPATH/src 以下からサードパーティパッケージを読み込むこれまでのGoと同様の動作になるということ。

それ以外の場所では go mod コマンドを使ってGo Modulesを利用することができる。これをmodule-awareモードという。go.modgo.sum を使って依存ライブラリを管理する方式になる。これらのファイルはgo mod コマンドが大体いい感じに生成してくれるので別にファイルが増えて煩雑になるということはない。

module-awareモードの場合$GOPATH/pkg/mod 以下にパッケージのバージョンごとの実体が置かれるので、そこを眺めるとイメージが掴めるかも知れない。ただ、この位置は変更になる可能性があるし、そもそも普通に go mod を利用する上では意識する必要はない。

この、GOPATHモードとmodule-awareモードの切り替わりはデフォルト挙動であり、 $GO111MODULE 環境変数を変更することで変更可能。また次のGo 1.13からは、一律module-awareモードになる。

Go Modules所感

普通に使い物になるし、vendorディレクトリとか使わなくていいので便利。積極的に使っていきましょう。その場合一律Go 1.12以降を使うことが望ましいとは思います。

使い方は、最近オフィシャルブログに Using Go Modules と言う記事が上がっていて、これがイメージを掴むためには良いと思います。

https://blog.golang.org/using-go-modules

依存定義のための go.mod とロックのための go.sum という依存管理システムに良くある2ファイル構成です。ただ、goコマンドがことある事にそれらを更新するのでそこは少し驚くかも知れません。

多くが自動生成されるため、覚えることは余りありませんが、網羅的な情報は公式のWikiにまとまっています。

https://github.com/golang/go/wiki/Modules

別に $GOPATH/src からプロジェクトを動かす必要はない

$GOPATH 以下だと、前述の通りデフォルトでは go modが動かないが、明示的に $GO111MODULE=on するだけで動くようになる。どうせ次のGo 1.13では、$GOPAH/src だろうがなんだろうがデフォルトでgo modが動く。チュートリアルとかでは $GOPATH の外でやることが案内されてるけど、そっちのほうがチュートリアル的には混乱がなく明確なのでそうしているだけで、別に出なくてもいいという理解をしている。

それに、現在移行期であることから、ツールチェイン類がまだmodule-awareモードに対応しておらず、GOPATHモードの方が嬉しい局面もある。具体的にはgoimportsなど。なので、逆に積極的にGOPATHから出る理由もないと考えています。

今のところは、開発時はGOPATHモードで良くて、CI/CD周りは、Go Modulesに寄せるのが良いと考えている。今は、Makefileに export GO111MODULE=on ってべた書きしている。Dockerfileとかにも書くといいと思う。

https://github.com/Songmu/godzil/blob/master/Makefile#L8

ハマりどころや課題など

masterのHEADと最新のタグに乖離があるときに困る

Go Modulesは、そのパッケージのリポジトリにv1.2.3のようなsemverタグが打たれている場合は、masterのHEADではなく最新のsemverタグを取得する。これは良い仕様だと思う。

ただ、HEADと最新のタグの乖離が激しいときは困ることになる。実際、 github.com/octokit/go-octokit でめちゃくちゃハマった。このライブラリの場合、最新のv0.4.0タグとHEADで依存パッケージまでことなるので、普通に go get すると意図しない依存が書き出されて困ってしまった。

以下のように go get で指定するパッケージに @master を付与することで強制的にHEADに依存させることができるのでそれで凌いだ。

% GO111MODULE=on go get github.com/octokit/go-octokit@master

今後はライブラリ系のパッケージもsemverタグを付けていくのが望ましい。僕はビルドを提供したいツール類だけリリース時にタグを打っていたけど、ライブラリ系もちゃんと打っていきたい。godzilを作り始めた背景もそれ。

ちなみに、 go-octokitghch内部で使っているのですが、どうもメンテが止まっており、hub コマンド内部でも使われなくなってしまったので、go-github に乗り換えるのが良いかなーと思っています。

ツール類を go get すると go.mod が書き換わってしまう

module-awareモードの時に go get をすると、それがツール類であっても無条件に go.mod 及び go.sum を書き換えてしまう。例えば以下のように golint を入れたい場合とかに困る。

% go get golang.org/x/lint/golint

これは今のところ以下のように強制的にmodule-awareモードを切ることで凌いでいる。

% GO111MODULE=off go get golang/x/lint/golint

https://github.com/Songmu/godzil/blob/master/Makefile#L16

議論はされていて、 go get -b <pkg>@<version> のように -b オプションを設けるのが有力のようです。

https://github.com/golang/go/issues/30515

依存先モジュールの編集をしたい場合に困る

バグ修正だったり挙動確認の為にログを差し込んだりとかのために依存先を変更したくなること、なんだかんだであると思います。

GOPATHモードの場合、依存先のコードを書きかえて動作を確認して、そのままシームレスにpull requestを送ったりもできて便利なのですが、module-awareモードの場合はそうもいきません。

これも以下で議論されていますが、go.modを一時的に書き換えてreplaceディレクティブを使う方法や、go mod vendor でvendorを書き出してしまってそれを編集するなどのワークアラウンドが話されています。

https://github.com/golang/go/issues/27542

根本解決に関しては、優先度が下げられていて、方針も決まっていません。go.mod.local を認識させる案も提案されていますが、個人的にはそれはあまり良い方法だとは思わないし、採用もされないだろうなと思っています。

これに関しては、GOPATHモードの方がやりやすさがあるので、今のところ個人的に開発時にGOPATHモードを利用したいモチベーションの一つにもなっています。

上記issueでも議論されていますが、mini CPANのようなローカルモジュールindexとしての $GOPATH/src のわかりやすさが損なわれることについての対応策は今後の課題になりそうです。

完全な依存ツリーを作るために go mod tidy を使う

僕も最近まで勘違いしていたのですが、 go mod tidy は環境に関わらない完全な依存ツリーを構築するためのものと捉えたほうが良さそうです。「綺麗にする」「余計なものを消す」というイメージで使われていそうですが「整頓する」というのがイメージとしては正しそうです。

例えば、開発環境とは異なる特定OSのみ、例えばwindowsだけで利用されるパッケージを go.mod に追加するには go mod tidy を実行するだけです。go buildgo get は、 GOOS、GOARCH、ビルドタグなどを見てしまうのでgo.modへの最低限の依存の書き出ししかしませんが、go mod tidy の場合は、それらを見ることなしに、つまり全てのgoファイルを読み込んで依存ツリーを構築してくれるようです。

これは少し分かりづらい挙動ですが、仕様であり明確にドキュメントされるべきだと以下で述べられています。

https://github.com/golang/go/issues/27633#issuecomment-420545604

go getgo build では go.modにtest dependencyが追加されず、go test しないと追加されないことや、それなのに go mod tidy では追加されてしまうことを不思議に思っていたのですが、先程書いたように、 go getgo build ではその環境での実行に必要な必要最低限の依存抽出しかせず、 go mod tidy ではgoファイルの絞り込みを行わず全ての環境での依存を抽出してくれる仕様であるためだということがわかりました。

ghqを使っている場合

すでに書いたように $GOPATH/src から無理に出る必要はないのでこれまで通り ghq を使えば良いでしょう。逆に、Go 1.13以降は go get の挙動が完全に変わってしまうため、これまでと同様の配置ルールでリポジトリ管理したい場合は、 ghq get をより活用することになると思われる。

改めて個人的な結論

課題についてはGitHub issueを探せば大体議論が出てくるので便利。必要なやつを適宜追いかけておけばよいでしょう。

ということで使っていきましょう。

Devel::KYTProfが大幅にパワーアップしたぞ!

ということで、Devel::KYTProf 0.9991をリリースしました。

https://metacpan.org/pod/Devel::KYTProf

YAPC::Tokyoのfujiwaraさんの「Perlでも分散トレーシングしたい」というトークでDevel::KYTProfに褒められが発生していて、AWS X-Rayという現代のテクノロジーでDevel::KYTProfがパワーアップして華麗に復活するの面白いなーとか思ったので、ちょっと古かった点とかissueとかをカンファレンス駆動で一気に直した。やっぱカンファレンス翌日のhack day欲しくなりますね。

そこから少し間が空いてしまったのですが、onishiさんにコラボとcomaint付けてもらってリリースした次第。以下変更点など。

DBI Profilerを改善

https://github.com/onishi/perl5-devel-kytprof/pull/16

今回の目玉。

DBIのクエリ呼び出しを網羅してフックするのは実はかなり大変で、Devel::KYTProfも不十分だという積年の課題がありましたが、この度めでたく解決しました。

具体的には do, selectall_arrayref, selectrow_arrayref, selectrow_array辺りをMySQLでフックできてなかったのを対応しています。内部的にはDBIx::Tracerを使うようにしたので、以前よりわずかにオーバーヘッドがあるかもしれません。

それと同時にadd_prof の引数の末尾に $sampler_sub を渡せるようになりました。これはメソッド実行前に処理され、プロファイルをおこなうかどうかの真偽値を返すためのsubrefを渡せるものです。偽値を返した場合、メソッドが直ちに実行され、プロファイルは行われないようになっています。ただ、これはDBI Profiler改善のために無理やりつけた機能感が否めないため、今のところEXPERIMENTALな機能だと考えています。

Profilerをパッケージ定義できるように

https://github.com/onishi/perl5-devel-kytprof/pull/14

これも結構便利です。

すでに、Devel::KYTProf::Logger::XRay のようにLoggerは独自のパッケージに差し替え可能になっていますが、Profilerもユーザーが独自のパッケージを定義できるようになりました。

"Devel::KYTProf::Profiler::" . $target_pkg と言った名前空間でパッケージ定義します。例えば、組み込みの DBI だと Devel::KYTProf::Profier::DBI という具合です。

Profilerパッケージの作成は簡単で、そのパッケージに apply という関数を定義しておくだけです。これを、 Devel::KYTProf->apply_prof('DBI') のようにすれば読み込みができます。詳しくは組み込みのProfierの実装を見ればイメージが沸くでしょう。

組み込みのProfierは、 DBI, LWP::UserAgent, Cache::Memcached::Fast, MogileFS::Client, Furl::HTTP を提供しています。これらは今のところ全て自動で読み込まれますが、流石にMogileFSとかどうなのかと言う感じなので、変更する可能性はあります。

ということで、独自のProfilerを追加できるようになったので、是非作成してCPANizeして活用してみてください。Devel::KYTProf::Profiler::Redis::Fastとかあると良さそうですね!Redisは組み込みで欲しいくらいですが。

組み込みProfilerの自動読み込みをオフれるように

() 付きでuseすれば、何も組み込みProfilerを読み込まないようになりました。

use Devel::KYTProf ();
Devel::KYTProf->apply_prof('DBI');
Devel::KYTProf->apply_prof('Furl::HTTP');

などとすれば使いたいProfilerだけを選択的に読み込むことができます。

UNIVERSAL::require から Module::Load

https://github.com/onishi/perl5-devel-kytprof/pull/13

UNIVERSAL::require は一世を風靡しましたが、今はModule::Loadを使う方が良かろう、ということでスイッチしました。

Devel::KYTProf::Logger::XRay とかと組み合わせてトレーシング用途で本番で使うとなると、UNIVERSALにrequireが生えているのはやはりちょっと怖いので、というのもあります。

ただ、 Devel::KYTProfuse UNIVERSAL::require; されていることを期待しているコードを書いている場合は動かなくなる場合があるかも知れないのでその場合は別途明示的にコード内で、 use UNIVERSAL::require; してください。

Class::Data::Inheritance から Class::Data::Lite

https://github.com/onishi/perl5-devel-kytprof/pull/15

これは個人的な趣味ですが、 Class::Data::Inheritable が Inheritableである必要とか、@ISA 変更する必要とか無いよなーとかそういうのを思ったので、 Class::Data::Lite というのを新たに作って、そちらに切り替えました。つまり、Class::Accessor::Lite と同じような動機です。多少は効率良くなっているかも知れません。久々の新作CPANizeです。

https://metacpan.org/pod/Class::Data::Lite

まとめ

ということで是非お使いください。結構中身をゴリッといじったので、0.9991というバージョンで出しています。今後、多少非互換修正も加える可能性もありますが、様子を見てversion 1を出したいと考えています。

godzilというGoのオーサリングツールを作っている

https://github.com/Songmu/godzil

Goのモジュール作成と、リリースをサクッとできる君が欲しくて作っている。作りかけだけど、すでに便利に使ってはいます。インターフェースは結構変わる可能性はあります。名前の通りかなりMinillaインスパイアです。

モチベーションとしては以下。

今のところの使いかた

モジュールの作成

% godzil new github.com/Songmu/oreore

scaffoldingされて、git init && git add . される。あとは適当にremoteリポジトリを登録して開発してください。今のところGitHub or GHEで開発する前提になってます。

リリース

% godzil release

semver形式でリリースバージョンを聞かれるので入力します。以下のことをやってくれます。

逆にツールのビルドはおこなわないのでそこは適宜別の仕組みを利用する形です。リリースフックの仕組みなどは用意するかも知れません。

今後の展望

まだ、僕のオレオレツール感が強いのですが、試しに使って意見をいただけると嬉しいです。以下の機能追加などを考えています。

余談

前のエントリで、取り上げたstatikは、実はこのgodzilで使いたかったというのがあります。ちなみに、godzil内部で使っているScaffoding用のライブラリで gokoku というものも作っています。

https://github.com/Songmu/gokoku

あるファイルツリーを元に、それ配下のファイルをGoのテンプレートに見立てて、一律指定位置に書き出してくれるものです。

そのために gokoku.Tmpl っていうtypeを定義しています。今後は gokoku.Hojo というのも定義したいですね。…はい。

Goのバイナリへのファイル同梱はstatikで決まり

https://github.com/rakyll/statik

Goのバイナリにファイルを同梱するという誰もが欲しいはずのものがなかなか決定版がなく、go-bindataがメンテを終了し、go-assetsもいまいちメンテが滞っているので、statikを使うことにしました。作者のrakyllさんも実績のある方なので大丈夫でしょう。

statikも少しpull requestの取り込みが滞っていたのですが、試しにpull requestを送ってみたらちゃんと取り込まれたので大丈夫そう。

https://github.com/rakyll/statik/pull/61

元々、ファイル単体を取得する分には問題なかったのですが、ファイルシステムとして走査しようとするとうまく辿れない問題があり、このpull requestで修正しました。ですのでより安心して使えるでしょう。

http.FileSystem インターフェース

こういう、ファイルを同梱するやつは http.FileSystem インターフェースを満たすのがセオリーでstatikもそうなっています。ただ、 http.FileSystem.Open() での単体ファイル取り出しは実装されているものの、ディレクトリ走査のための http.File.ReadDir() が正しく実装されているものが案外少なかったりします。

ちなみに、go-assetsも、正しく実装されていなかったので、一応pull requestを投げてはいます。

https://github.com/jessevdk/go-assets/pull/9

おそらく、ファイル単体を引ければ十分で、ディレクトリを走査して使うケースがあまりないため正しく実装されていないことが多いのでしょう。

statikの良いところ

mTimeを固定する-mオプションがある

mTimeを固定できるため、ビルドのたびに差分がでたりしない

// Code generated... コメントを付けてくれる

https://golang.org/s/generatedcode のルールに則って、自動生成ファイルとして認識してもらえる。

内部データが単なるzipである

データサイズが小さくできるし、ライブラリ側が更新されたとしてもアセットデータの再作成が必要ない。データがシンプルかつ将来に渡って不変で良いというのは、見事な設計。

statik/fs.Walk() が便利

filepath.Walk() と似たようなインターフェースで、 http.FileSystem を走査できる statik/fs.Walk() が定義されておりこれが便利です。僕の場合これが使いたかったので、今回pull requestを送った次第。

http.FileSystem インターフェースが正しく実装されている

今回の修正で正しく実装されました。

ということで

Goでのファイル同梱には statik をメインで使っていこうと思います。決定版になってほしい!

YAPC::Tokyo 2019で報恩謝徳の話をしてベストスピーカーを頂きました

http://songmu.github.io/slides/yapc-tokyo-2019/#0

「技術力が無い人間がエモに逃げてはいけない」という個人的ポリシーを持っていて、あまりエモい話は登壇ではしないようにしてきたんだけど、今回は「報恩謝徳」がテーマということで、思いっきりエモい話をしてやろうということで申し込んだ。このテーマならベストスピーカー狙えるだろうとも思っていました。

しかし、トーク採択していただいたものの、実は40分のところを20分にしてくれないかという打診があり承諾していました。僕は比較的運営から近い位置にいるのでそういう打診をしやすかったのかも知れず、そういう相談をしてもらえるのは嬉しいし、登壇者が増えるのも良いことだとも思い快諾しました。

とは言え20分にまとめるのは難儀でした。登壇は多少は慣れているつもりですが、今回はかなり難産で、資料作りに苦労しました。

プレゼン作る時は、話したいことをまずは書き出しまくって、アウトラインに収束させてからスライドに起こすのだけど、今回はテーマがテーマなので話したいことが多すぎて爆発してしまって、そもそもアウトラインに収束させられずに苦労した。ベストスピーカーを狙っていた気負いも合ったのでしょう。

もっと話したいとか、感謝を述べたいこと、面白いエピソードとかいろいろあったのですが泣く泣く削りました。

社内的には広報チェック通したほうがいいんですが、今回は事前に広報の担当者に「今回人生の話しようと思ってるんですけど、全然資料できなくて困ってるんで広報チェック回せそうにありません」と断っておいて「いいですよ」とは言われていました。本当は良くないんだけど!

スライドも当初はKeynoteに起こす予定だったけど、これは間に合わんな、ということでいつも使ってるMarkdonwからスライド生成する自作のプレゼンツール(Perl製!)を使うことに。だから逆に勢いよく話せたのかも知れない。

ベストスピーカーを狙ってるんだったら前日とかに宣伝エントリなりツイートなりしたほうがいいんだけど、最後の最後まで内容も収束させられないし全然自信も持てなかったのでそういうこともできなかった。

前夜祭後に @tokuhiromと@xaicronと飲みに行って全員が「スライドできてねー」って話してて、まあなんというかいつものYAPCの光景と言う感じで逆に落ち着いてプレゼンストーリーも見えてきた。この時「そんむーさん自分が意識高くないと思ってるなら相当重症ですよ」ってxaicronに言われたのが面白くて妙に印象に残っている。

xaicronはその後徹夜で資料作ったらしいけど、僕は早朝に起きて、会場で仕上げをしてなんとなく話せるかな、という手応えを感じたのは以下のツイート時点。

正直ベストスピーカーは諦めていて、資料は殴り書きみたいなアウトラインの域を超えてなかったので、まあこれを20分で話しきれたら御の字だろうと思っていた。

とは言え、トークの冒頭で「他のスピーカーの方は慎み深い方が多いか、言ってる人少ないんですけど、ベストスピーカー投票お願いしますね」という軽口を叩けるくらいには自信を持って話すことはできた。話し終わったあとも、少し時間オーバーしちゃったけど、話せてよかったという満足感はありました。

その時点でもベストスピーカーは取れるとは思ってなかったんだけど、YAPCの運営でもあり同僚でもあるpapixが悪くて、ベストスピーカーの発表前に社内のSlackでなにか良いことが起こったことを仄めかす発言をして「これはもしかしたらベストスピーカー取ったかも知れないぞ」とか頭によぎってしまった。

これでベストスピーカーだったら泣いてしまうかも知れないけど壇上で泣かないようにしないとな、とか思ったらその時点でなんだか泣けてきてしまって、いや仄めかしたのはベストスピーカーの話じゃないかも知れないし、別の同僚のスピーカーの話かもしれないからこれで泣いてたら馬鹿だな、とか色々思いを巡らせていたら、果たして本当にベストスピーカーであり、結局壇上でめちゃくちゃ泣いてしまった。人間揺さぶられると弱い。

ということで、多くの同僚やコミュニティの仲間達の前で泣いてしまったのだけど、温かく受け止めていただいたのは本当にありがたかったです。

YAPC自体は盛り上がっててよかった。若い人も結構話してたし、ベストLTもベストスピーカー賞も3位までPerlの話だったのも個人的には良かったと感じています。結構仕事が忙しかったのでフルで楽しめるか微妙だったんだけど、結局誘惑に負けて前夜祭からフルで楽しんでしまった。楽しめてよかった。YAPCが相変わらず僕にとってのモチベーションの源泉の場の一つでもあるのだなとも感じた日でした。あとはtokuhiromのキーノートの最後のスライドに写真を載せてもらえていたのは密かに結構嬉しかった。

Homebrewで自作ツールを簡単にインストール可能にする

まとめを先に

用語

これらは自前で簡単に作ることができます。公式リポジトリに頑張ってpull requestを送ることもできますが、個人的なものであれば気軽に自前で作ってしまうことがおすすめです。

tapリポジトリの作成

前項で書いたとおり"homebrew-"プレフィクスを付けて命名すればOKです。僕の場合homebrew-tapという名前で作っています。

https://github.com/Songmu/homebrew-tap/

このリポジトリ上のディレクトリ直下、もしくは Formula/ ディレクトリ配下にFormulaを配置します。上記のリポジトリの場合、Formula/ 以下に配置しています。現状7個のツールを登録しています。

このtapを利用したい場合は"homebrew-"プレフィクスを除いた形で以下のように登録します。

% brew tap Songmu/tap

また、以下の様にダイレクトにtapの登録とツールのインストールをおこなうことも可能です。

% brew install Songmu/tap/maltmill

Formulaの作成

FormulaはrubyのDSLファイルです。他のツールのFormulaを参考に見よう見まねで書くこともできますが、ここでは maltmill というツールの紹介をします。

https://github.com/Songmu/maltmill

実は1年以上前に作っていて、作りかけ気味なのですが、使い物にはなるし、実際活用しているので紹介することにしました。

コンセプトとしては、GitHub Releasesに登録しているアーカイブから簡単にFormulaを新規作成したり、更新したりすることができるものです。

インストール

# brew
% brew install Songmu/tap/maltmill
# go get
% go get github.com/Songmu/maltmill/cmd/maltmill

Formulaの新規作成

前提として、GitHub Releasesにアーカイブがアップロードされている必要があります。例えば、以下のようにすれば、ghqのFormulaが作成されます。

% maltmill new -w motemen/ghq

この場合、Formulaはghq.rbというファイル名で作成されます。適宜調整して、tapリポジトリにcommitしてください。

Formulaの更新

GitHub Releasesに新しいリリースが登録されている場合、以下のようにFormulaファイルを指定することで更新することができます。

% maltmill -w Formula/ghq.rb

これは、versionとアーカイブurlの取得と書き換え、アーカイブのsha256の再計算とその書き換えも自動でおこなってくれます。あとは更新内容を確認して、リポジトリにcommitするだけです。それで新しいバージョンをbrewでインストールすることが可能になります。

まとめ

まだ荒削りな部分もありますが、是非ご利用ください。mackerel-agentも以下でtapを公開していますが、maltmillに乗り換えていければいいな、と思っています。

https://github.com/mackerelio/homebrew-mackerel-agent

命名は、ビール醸造関連が良いなと思って、ビール製造工程の最初に麦芽を挽くのに使うMalt Millからつけました。

horenso v0.9.0でより便利になりました

https://github.com/Songmu/horenso

v0.9.0なので、version 1を出す前のRC的な気持ちでいます。アップデートは以下のとおりです。

設定ファイル(yaml形式)サポート

設定ファイルは環境変数 $HORENSO_CONFIG にファイルパスを指定します。設定項目は大体コマンドラインオプションに対応していて以下のような具合です。

noticer: /path/to/basic-noticer
reporter:
- /path/to/basic-reporter

こういうのを保存しておいて、crontabに以下のように記述します。

HORENSO_CONFIG=/path/to/horenso.yaml
11 * * * * horenso          -- /path/to/your-batch1 ...
23 2 * * * horenso -t daily -- /path/to/your-batch2 ...
23 2 1 * * horenso -t monthly -r 'additional-reporter' -- /path/to/your-batch3 ...

従来は、README.mdに書いてある通り、ラップするシェルスクリプトを書くのが一つのパターンでしたが、この設定ファイルの場合もっと見通しがよくなります。

またジョブによっては追加のレポーターを指定したいなどもあると思いますが、それに関して個別に追加することも可能です。上の additional-reporter と指定してある様な形です。個別指定した場合であっても、設定ファイルに記載のnoticer及びreporterは動作します。

このあたりドキュメントまだなのでversion 1までには書きます…。

ログファイル指定オプション(--log/-l)

ログをファイルに出力することが可能になりました。--log=%Y%m%d.log となど指定します。strftimeフォーマットが利用可能なのもポイントです。

verboseログを出せるように(--verbose/-v/-vv)

horensoは主体であるバッチコマンドの動作を妨げないように、標準出力や標準エラー出力にはそのバッチコマンド自体の出力のみ流し、horenso自体のログは一切出さない様になっています。

しかしそれでは、reporterやnoticerの動作確認時に困るという声もあり、それは分かるということで、--vebose オプションを追加しました。--verbose は重ねることができます。

という挙動となっています。

タイムスタンプのフォーマット変更

horenso--timestamp/-T オプションで出力への自動タイムスタンプ付与が可能ですが、このフォーマットがRFC3339の小数点以下6桁固定長に変更になりました。

この変更にあたって、Songmu/timestamper というライブラリを外に切り出しました。これは便利かつ実装も面白いので後日また別でブログを書こうと思います。

type Report struct の型情報の非互換修正

horenso コマンド自体は大きな非互換修正はありませんが、horenso.Reporter をライブラリ的に自前のreporterなどで利用している場合はビルドが通らなくなってしまうため、修正をお願いします。Reporterのフィールドからなるべくポインターを減らす対応をおこないました。

その他、コードの修正や効率化、リファクタリングも何点か実施しました。

より便利になったhorensoをぜひご利用ください。