詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

Wercker で Go のプロジェクトをクロスコンパイルし、GitHub にリリースする

f:id:motemen:20140626234933p:plain

ghq をメンテナンスするにあたっていくつか無料の CI サービスを試してみたのですが、今回は Wercker を使うことにしました。いろいろ試行錯誤した結果表題のことがなんとか実現できた(ghq のリリースは GitHub 上 にあります)ので、ハマりポイントと共にこのエントリで紹介します。

Wercker

Wercker は CI サービスのひとつで、ビルド環境やデプロイの 1 ステップを box および step という形でユーザが公開・共有し、それらを組み合わせることで CI の設定をシンプルにしようとしているところが特徴です。

ghq の wercker.yml を見てもらえばだいたい雰囲気は分かると思いますが、大まかに言って “box”, “build”, “deploy” の 3 項目をプロジェクトごとに設定します。

box はビルドが走る環境です。OS や、テストに必要なパッケージなどがインストールされている、多くのユーザで共通のものが使用できる環境です。Go が使えるとか、MySQL が使えるとかです。

GitHub に push したタイミングで、build に指定した step が順次実行され、これらが完遂するとビルドは成功となります。step は box のように公開・共有されているものが沢山あるので、これを使うこともできますし、自前で用意することもできます。

成功したビルドは、デプロイすることができます。ウェブや CLIツールからデプロイターゲットを選び、実行すると、deploy に指定したステップに従って、build の成果物を使ってデプロイを行います。特定のブランチのビルドが成功した場合に自動的にデプロイさせることも可能です。

box も step も、GitHubリポジトリに置いて、それ自体を wercker でビルド・デプロイすることで他のユーザに公開できるようになってます。ここが面白いところですね。

ghq の場合

Wercker によって、GitHub のリリースに成果物を添付する方法が紹介されています(Deploying your assets to GitHub Releases)が、これをそのまま利用できたわけではなく、紆余曲折あって今のような wercker.yml になってます。

現在の HEAD からリリースするタグを自動的に決める

先のエントリで紹介されている step-github-create-release では tag というパラメータが必須で、ビルド中の環境変数もしくは設定ファイルでリリース対象のタグを指定する必要がありました。最初はこれを fork し、tag が設定されていなければ直近のタグを使用する、としていたのですが、最終的にこの機能は出番がなくて、fork した意味はありませんでした……。というのも、ビルドして出来上がった成果物をデプロイするフェーズで .git ディレクトリが失われてしまうので、タグの情報を復元できなくなってしまったからでした。現在はビルドフェーズでタグ情報を取得して、デプロイに渡すようにしています。

step-github-create-release は body というパラメータでリリースの本文を作ることができますが、これは JSON の文字列としてエンコードされている必要があるので、自動生成するのは難しいです(ステップはシェルスクリプトで書かれているので……。ソースコードを読めば同情はできる)。リリース本文 CHANGELOG.md から手でコピペしてしまっている。

ロスコンパイル

Wercker が提供している box-golang はクロスコンパイルをサポートしていない(多分)ので、これを可能にする box を作成する必要がありました。最初に gox をインストールした box-golang-gox を作ったのですが、GitHub のリリースはファイルに階層を与えられないので、同じ名前のバイナリを複数添付することができず、ghq_darwin_amd64 のような名前でバイナリを添付するしかありませんでした。

そのあとで @mattn_jp さんに goxc の存在を教えてもらい、これを使ってアーカイブファイルに固めることで、バイナリ自身の名前を変えずにリリース可能になりました。現在はこの box-golang-goxc を使っています。goxc はけっこう多機能なので、クロスビルドだけできればいい方は gox を使えばよさそうです。

自動デプロイ(できない)

当然、ここまで出来たら「master にタグを切って push したら自動でリリースされる」といきたいものですが、今のところ綺麗な実現はできていません。wercker の自動デプロイはブランチ名に対してしか設定できないので、とりあえず master のコミットを全部デプロイすることにしています。当該のコミットにちょうど対応するタグが存在しないときにはリリース先のタグがないので、変なリリースが作られることもありません(デプロイもなぜか失敗扱いにならないので、気にしてない)。

あと気をつけないといけないのが、GitHub に push すると即座にビルドが走るので、先にタグを push しておかないとビルド中にタグを発見できず、タグを切ったつもりのコミットもデプロイされないことがある点です。仕方ないので master にタグを切ったあとは先にタグを push しています。

おわり

タグを契機にした自動デプロイは措いておくとして、とりあえずこんな感じで ghq はリリースされています。Wercker はまだβだそうなので、今後の発展に期待しつつ使ってみるのもいいんじゃないでしょうか。

git-browse-remote 0.1.0 を公開しました & Vim からいい感じに GitHub を開く

git-browse-remote | RubyGems.org | your community gem host

git-browse-remote は、コマンドラインからいい感じに(リビジョンを指定したりファイルを指定したり)リポジトリのウェブサイトを開いてくれるツールです。

入れていた変更でまだ RubyGems に上げてなかったぶんをリリースしただけなんですが……。今回の変更で行番号(-L)に範囲が指定できるようになりました。

% git browse-remote -L 5,10 -- README.md

https://github.com/motemen/git-browse-remote/blob/master/README.md#L5-10 を開いてくれます。--stdout を指定すれば標準出力に URL を書き出すだけ、ってこともできます。

おまけで便利な Vim 設定を紹介。

command! -nargs=* -range GitBrowseRemote !git browse-remote --rev -L<line1>,<line2> <f-args> -- %

V などでビジュアル選択した状態で :GitBrowseRemote すると、その範囲を選択した URL をブラウザで開いてくれます。また、--rev オプションを指定することで、ブランチではなく SHA1 によって URL を構成します。他人に共有するときはこのように、後から見ても内容の変わらない URL にしておくのが便利ですよね。

GitHub のリポジトリの description/website をワークスペースから更新する

f:id:motemen:20140603191822p:plain

☝ この部分です。新しいリポジトリを作ったとき、ここの空欄が主張してきてちょっと気になりますよね。かといってブラウザでいちいち入力するのもだるい。エディタで書きたい。

ところで .git/description ってファイルを見たことありますか? ものの本には

the description file is only used by the GitWeb program, so don’t worry about those. Git - Plumbing and Porcelain

とあります。中身はこんなの。

% cat .git/description 
Unnamed repository; edit this file 'description' to name the repository.

ずっとこの内容のまま更新されることもないだろうファイルです。なのでこいつを GitHubリポジトリ情報のためのファイルとして使ってやろうって話です。

motemen/git-hub-sync-repo-info · GitHub

この簡単なスクリプトで、GitHubリポジトリ情報(description および website)とローカルのファイル(.git/description および .git/homepage)とを同期させることができます。API では homepage というキーになってるので URL が格納されるのは .git/homepage です。これらのファイルはもちろんバージョン管理されないファイルです。ファイルの内容が空かデフォルトのままの場合はリモートの情報がファイルに格納され、それ以外の場合はファイルの内容が GitHub の側に反映されます。

% echo "Synchronize .git/description and .git/homepage files with GitHub repository's info" > .git/description 
% git hub-sync-repo-info
# description:
#   local  = Synchronize .git/description and .git/homepage files with GitHub repository's info
#   remote = 
# homepage:
#   local  = 
#   remote = http://www.example.com/
Updated .git/homepage: [http://www.example.com/]
Updated remote description: [Synchronize .git/description and .git/homepage files with GitHub repository's info]

GitHubAPI トークンを設定しない場合は GitHub の更新ができません。個人用のトークンを作成してgit config --global hub-sync-repo-info.token TOKEN で設定してください。

簡単ですね。どうぞご利用ください。

はてなで一緒に働きませんか?