Goの勉強を兼ねてGitHubのPull RequestをCheckoutするCLIツールを作った

最近、年度末の忙しさにかまけてインプット+アウトプットが殆ど出来ていませんでした。
リハビリがてら、書きたいと思いつつ中々書く機会が無かったGoを使い、GitHubのPull RequestをローカルにCheckoutする為のCLIツールを作ってみました。

作ったもの

DEMO

tsuyoshiwada/git-prout: Checkout pull request locally with Golang.

インストール

macOS の場合はHomebrewを使ってインストール可能です。

$ brew tap tsuyoshiwada/git-prout
$ brew install git-prout

それ以外の場合はリリースページの中から必要なファイルを落としてきて、PATHの通ったところに配置し、git-prout にリネームします。

Goを使用する場合は以下。

$ go get -u github.com/tsuyoshiwada/git-prout

使い方

基本の使い方は以下。

$ git-prout [<options>] <number>

<number> にPull Requestの番号を指定するだけです。

フォーク元のリポジトリを参照

例えばフォークしたリポジトリで作業して、upstream など、フォーク元リポジトリにあるPull Requestをローカルで動かしてみたい時なんかは、--remote(-r) オプションでリモートの指定を追加します。

$ git-prout --remote upstream 123

Gitサブコマンドとして動作

git-** というコマンド名にしているので、インストール後、そのままGitのサブコマンドとして動作します。

$ git prout 123

参考: 便利な「git-サブコマンド」を作成する - Qiita


他にも --force オプションがあったりしますが、基本的な使い方は以上となります。

実装について

GitHub の Pull Request をローカルに取り込む方法は、探せば無限に出てくると思います。

正直、コマンドにしたら2行程度の内容なので作ったツールの優位性はそれほど無いかなと思います。

しかし、Go初心者がさっと何か作る〜リリースまで経験するためには丁度良さそうな規模に思えたので作ってみた次第です。

以下はコード書いてリリースするまでのメモ書きです。

depを使った依存解決

普段はフロントエンド周りを触ることが多いので、npm を使って依存関係の解決をしています。
Go ではそこらへんどうするの? と思っていたところ dep という依存関係解決のツールがあるようなので使ってみました。

まだ不完全なプロジェクトみたいですが、今回のツールで使用する分には問題なかったです。

Alpha. Functionality is known to be broken, missing or incomplete.

kingpinを使ったCLI

各種フラグ+コマンドライン引数のパーサーには kingpin を使用しています。採用理由は以下。

  • 標準パッケージの flag に近い使用感
    • そのため、学習コストが低そう
    • 他のCLIパーサーに比べ、やり過ぎ感もなく丁度良く感じた
  • flag とは違い、引数もフラグと同様に扱えた
    • Go初心者には有り難い…

しかし、kingpin は少し情報が少なく感じました。ただ、困ったらコードを追えるような規模だったのであまり問題ないかもしれません。

以下参考になりました。

テスト

申し訳程度にテストコードは書いていますが、「肝心の Pull Request を fetch する」、というメインの処理周りのテストが書けていません…。(書き方分からず保留)
ここらへん、次回何か作るときの課題になりそうです。カバレッジを出したりしてみたい。

初めてGoでテストコードを書くにあたって、「そういえば assert 無いな??」ってなりましたが、以下のページを読むことでスッキリしました。

また、CLIのテストを書く際の設計指針として、以下大変参考になりました。

ポイントは io.Writer をメインの処理に渡す部分になりますが、kingpin を使っている場合は以下のようにして渡すことが出来ました。

// errStream io.Writer
app.ErrorWriter(errStream)
app.UsageWriter(errStream)

// 同様の機能提供として `Writer(w io.Writer)` もあるが非推奨

gox+ghrを使ったリリース

一番やってみたかったリリース部分。
CIに TravisCI を使用しているので、今回はそこからリリースさせることにしました。

  • GitHub上で、repo への操作権限を付けた Personal access token を発行
  • TravisCI に GITHUB_TOKEN で登録

あとは .travis.yml を以下のように設定するだけで、クロスコンパイル+自動リリースが出来ました。

language: go
sudo: false
go:
  - 1.8
  - tip
before_install:
  - go get github.com/golang/dep/...
  - go get golang.org/x/tools/cmd/cover
  - go get github.com/mitchellh/gox
  - go get github.com/tcnksm/ghr
install:
  - $GOPATH/bin/dep ensure
script:
  - go test -v
branches:
  only:
    - master
after_success:
  - gox -output "dist/{{.OS}}_{{.Arch}}_{{.Dir}}"
  - ghr --username tsuyoshiwada --token $GITHUB_TOKEN --replace `grep 'Version =' version.go | sed -E 's/.*"(.+)"$$/\1/'` dist/

ghr に渡すバージョンは version.go で定義した Version 定数の値を拾って指定しました。

おわりに

慣れない部分(構文含め…)が多く、単純な機能なのにリリースまでなんだかんだで1週間くらいかかってしまいました…。もう少しさくっと作れるように定期的にGoで遊んでみたいなと思います。
次はAPIサーバをGoで書いて、デプロイまでやってみたい。

Older Post SlackとVimとメモ管理と私