デプロイ先でのファイルハッシュ値とディレクトリ構成をチェックしたい、そんな時に使えるツールをGo言語で作りました！

# 発端

お仕事で、時々以下のような需要がありました。

* インストーラで配置したファイルやディレクトリが意図した構成になっているか確認したい
* 上記に加えて個々のファイルが意図したバージョンになっているかハッシュ値で確認したい

デプロイ先の評価環境に別途ツールをインストールせずにチェックできるツールが欲しいなぁと思い、
Go言語で[gckdir](https://github.com/KemoKemo/gckdir)というツールを作りました٩( 'ω' )و

各プラットフォーム向けの最新バイナリは、[こちら](https://github.com/KemoKemo/gckdir/releases/latest)からダウンロードしてくださいm(_ _)m

# こんなツールです

以下のような特徴があります。

* 正解のハッシュテーブルファイル（*.json）を生成できる
* 生成したハッシュテーブルファイル（*.json）とツールだけコピーすれば、デプロイ先でチェックができる
    * 評価環境のレジストリやパッケージマネージャに影響を与えることなくチェックができる
* チェック結果がFailなら0以外のExitCodeが返る
    * スクリプト内で使用する際に、判定結果によって条件分岐ができる
* チェック結果をレポートhtmlとして出力できる
    * 結果確認がちょっとだけしやすくなる☆
* ハッシュ値の違いは無視して、ファイルとディレクトリの構成だけチェックできる
    * 開発序盤から中盤の「ファイルなどの配置だけチェックしたい」時期に使うイメージ
* 困ったら`-h`オプションでいい感じのヘルプが表示されます

## ハッシュテーブル生成

以下のようにサブコマンドに`generate`または`gen`を指定することで正解ファイルを生成します。

```bash
$ ./gckdir gen (正解のディレクトリ) (ハッシュテーブルファイル名.json)
```

## デプロイ先でチェック

以下のようにサブコマンドに`verify`または`ver`を指定することで、正解ファイルやディレクトリと対象のディレクトリとを比較検証します。

```bash
$ ./gckdir ver (ハッシュテーブルファイル名.json) (対象ディレクトリ)
```

`ver`コマンドに`--report`オプションをつければBootstrapで少し見やすくしたレポートhtmlを生成します。

```bash
$ ./gckdir ver --report --output (レポートファイル名.html) (ハッシュテーブルファイル名.json) (対象ディレクトリ)
```

レポートファイルの名前を指定する時は`--output`オプションに続けて名前を指定して実行してください。指定しない場合は自動的に日時から`Result_{YYYYMMDD}-{hhmmss}.{nanosecond}.html`という感じの名前で保存されます。

![](https://github.com/KemoKemo/gckdir/raw/master/images/verification_report.png)

さらに、`ver`コマンドに`--open`オプションをつけて実行すれば、規定のブラウザでレポートhtmlを開きます。（デスクトップ環境のみ）

![](https://github.com/KemoKemo/gckdir/raw/master/images/open_animation.gif)

「まだモジュールは差し替わるけど、ファイルやディレクトリの配置がうまくいってるかだけ確認したい」というときのために`--no-hv`オプションも用意しています。

```bash
$ ./gckdir ver --no-hv (ハッシュテーブルファイル名.json) (対象ディレクトリ)
```

## 困ったら`-h`

### サブコマンド

「えっと、どんなサブコマンドがあるんだっけ？」、そんなときには`-h`オプションをつけて実行してください。

```bash
$ ./gckdir -h
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/166225/f2e356a4-9eb6-8754-0ec0-03e0a7e66cc8.png)

### サブコマンドのオプション

「あれ、このサブコマンドのオプションは・・？」、そんなときにもサブコマンドの後に`-h`をつけて実行してください。

```bash
$ ./gckdir verify -h
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/166225/1d52cce6-7f2a-054a-2169-f78719178dac.png)

# 謝辞

Gitみたいにサブコマンドとオプションでいろいろな使い方ができるcliツールを簡単に作れたのは、ひとえに[@tcnksm](https://github.com/tcnksm)さんの[gcli](https://github.com/tcnksm/gcli)のおかげです。この場をお借りしてお礼申し上げます。

## gcli

いや、ほんとうに素晴らしいんです、[gcli](https://github.com/tcnksm/gcli)。めちゃくちゃ感動したのでもうちょっとお話させてください。

例えば`generate`と`verify`サブコマンドをもった`clitest`というツールを作ろうと思ったら、以下を実行するだけでcliとしての大部分が自動生成されます。（もっと詳しい情報はGitHub公式ページのREADMEをご覧ください。）

```bash
$ gcli new -c generate -c verify clitest
```

「よし作ろう」となった瞬間にgcliでスケルトンを生成して、次の瞬間にはコマンドの中身の実装に専念できます。これからGo言語でcliを作るすべての人にオススメしたいツールです。

## os.Exitとrun関数

今回作ったgckdirツールでも使っている小技ですが、`main`を下記のように実装して`run`関数が適切なExitCodeを返すことで、`run`関数での`defer`をしっかり実行した後でExitすることができます。`os.Exit`は`defer`を実行せずに即時終了してしまうため、このハック結構重要です。

```go
func main() {
	os.Exit(run(os.Args))
}
```

さらにExitCodeを`iota`を使った`const`で定義しておくと、C#などでの0始まりの`enum`型のように使えて便利です。

```go
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](https://github.com/tcnksm)さんが[みんなのGo言語[現場で使える実践テクニック]](http://gihyo.jp/book/2016/978-4-7741-8392-3)の4章で書いておられる「コマンドラインツールを作る」という記事を読んで習得しました。私のようにちゃんとしたcliを初めて作る方は、こちらも読みながら開発をすすめるととってもはかどるかと思います。