発端
お仕事で、時々以下のような需要がありました。
- インストーラで配置したファイルやディレクトリが意図した構成になっているか確認したい
- 上記に加えて個々のファイルが意図したバージョンになっているかハッシュ値で確認したい
デプロイ先の評価環境に別途ツールをインストールせずにチェックできるツールが欲しいなぁと思い、
Go言語でgckdirというツールを作りました٩( 'ω' )و
各プラットフォーム向けの最新バイナリは、こちらからダウンロードしてくださいm(_ _)m
こんなツールです
以下のような特徴があります。
- 正解のハッシュテーブルファイル(*.json)を生成できる
- 生成したハッシュテーブルファイル(*.json)とツールだけコピーすれば、デプロイ先でチェックができる
- 評価環境のレジストリやパッケージマネージャに影響を与えることなくチェックができる
- チェック結果がFailなら0以外のExitCodeが返る
- スクリプト内で使用する際に、判定結果によって条件分岐ができる
- チェック結果をレポートhtmlとして出力できる
- 結果確認がちょっとだけしやすくなる☆
- ハッシュ値の違いは無視して、ファイルとディレクトリの構成だけチェックできる
- 開発序盤から中盤の「ファイルなどの配置だけチェックしたい」時期に使うイメージ
- 困ったら
-h
オプションでいい感じのヘルプが表示されます
ハッシュテーブル生成
以下のようにサブコマンドにgenerate
またはgen
を指定することで正解ファイルを生成します。
$ ./gckdir gen (正解のディレクトリ) (ハッシュテーブルファイル名.json)
デプロイ先でチェック
以下のようにサブコマンドにverify
またはver
を指定することで、正解ファイルやディレクトリと対象のディレクトリとを比較検証します。
$ ./gckdir ver (ハッシュテーブルファイル名.json) (対象ディレクトリ)
ver
コマンドに--report
オプションをつければBootstrapで少し見やすくしたレポートhtmlを生成します。
$ ./gckdir ver --report --output (レポートファイル名.html) (ハッシュテーブルファイル名.json) (対象ディレクトリ)
レポートファイルの名前を指定する時は--output
オプションに続けて名前を指定して実行してください。指定しない場合は自動的に日時からResult_{YYYYMMDD}-{hhmmss}.{nanosecond}.html
という感じの名前で保存されます。
さらに、ver
コマンドに--open
オプションをつけて実行すれば、規定のブラウザでレポートhtmlを開きます。(デスクトップ環境のみ)
「まだモジュールは差し替わるけど、ファイルやディレクトリの配置がうまくいってるかだけ確認したい」というときのために--no-hv
オプションも用意しています。
$ ./gckdir ver --no-hv (ハッシュテーブルファイル名.json) (対象ディレクトリ)
困ったら-h
サブコマンド
「えっと、どんなサブコマンドがあるんだっけ?」、そんなときには-h
オプションをつけて実行してください。
$ ./gckdir -h
サブコマンドのオプション
「あれ、このサブコマンドのオプションは・・?」、そんなときにもサブコマンドの後に-h
をつけて実行してください。
$ ./gckdir verify -h
謝辞
Gitみたいにサブコマンドとオプションでいろいろな使い方ができるcliツールを簡単に作れたのは、ひとえに@tcnksmさんのgcliのおかげです。この場をお借りしてお礼申し上げます。
gcli
いや、ほんとうに素晴らしいんです、gcli。めちゃくちゃ感動したのでもうちょっとお話させてください。
例えばgenerate
とverify
サブコマンドをもったclitest
というツールを作ろうと思ったら、以下を実行するだけでcliとしての大部分が自動生成されます。(もっと詳しい情報はGitHub公式ページのREADMEをご覧ください。)
$ gcli new -c generate -c verify clitest
「よし作ろう」となった瞬間にgcliでスケルトンを生成して、次の瞬間にはコマンドの中身の実装に専念できます。これからGo言語でcliを作るすべての人にオススメしたいツールです。
os.Exitとrun関数
今回作ったgckdirツールでも使っている小技ですが、main
を下記のように実装してrun
関数が適切なExitCodeを返すことで、run
関数でのdefer
をしっかり実行した後でExitすることができます。os.Exit
はdefer
を実行せずに即時終了してしまうため、このハック結構重要です。
func main() {
os.Exit(run(os.Args))
}
さらにExitCodeをiota
を使ったconst
で定義しておくと、C#などでの0始まりのenum
型のように使えて便利です。
const (
// ExitCodeOK means that this app successfully finished to run.
ExitCodeOK int = iota
// ExitCodeVerificationFailed means that the Verification has ended with errors.
ExitCodeVerificationFailed
// ExitCodeFailed means that this app failed to run. Please check the error message.
ExitCodeFailed
...
)
このrun
関数を用いる技は、@tcnksmさんがみんなのGo言語[現場で使える実践テクニック]の4章で書いておられる「コマンドラインツールを作る」という記事を読んで習得しました。私のようにちゃんとしたcliを初めて作る方は、こちらも読みながら開発をすすめるととってもはかどるかと思います。