git-secretsとhuskyを同時に使用する

# はじめに

機密情報をコミットしないように[git-secrets](https://github.com/awslabs/git-secrets)の設定をしようとしたところ、既にprecommit用のNode.jsライブラリ[husky](https://github.com/typicode/husky)がインストールされていたため、コンフリクトしてgit-secretsの設定ができませんでした。

どっちとも使いたかったので、それぞれ動くように工夫してみました。

2019/07/10 追記
会社の @aki77 さんが、もっと良い方法を見つけてくれたので、
そっちを「方法1」として追記しました！

# 方法1: husky内でgit-secretsを呼び出す
gitのhooks内では、デフォルトのままhuskyだけが呼ばれるようにしておき、
huskyでのチェック項目の1つとして、git-secretsを呼び出します。
[lint-staged](https://github.com/okonet/lint-staged)も併用します。

## git-secretsをグローバルにインストール
```shell-session
brew update
brew install git-secrets
git secrets --register-aws --global
```

このあと、プロジェクト内で**`git secrets --install`はしないように！**

## husky,lint-stagedをプロジェクトにインストール
```shell-session
yarn add --dev husky lint-staged
```

## husky内でgit-secretsを動かすように設定

```json:package.json
{
  "scripts": {
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "*": [
      "git secrets --scan"
    ],
    "*.{js}": [
      "eslint --fix",
      "git add"
    ]
  }
}
```


やっている意味はこんな感じ。

* JSだろうが何だろうが、まず全ファイルを`git-secrets`でチェック
* それがOKなら、JSファイルだけ`ESLint`でチェック

あとは必要に応じて
`js`拡張子の`eslint --fix`の部分を変更したり、
`js`以外の拡張子のルールを追加したり、
よしなにやっちゃいましょう :v:


# 方法2: hooksスクリプトを2種類順番に実行

最初に書いてた、自分でスクリプトをマージする方法です。
gitのhooks内で、git-secretsとhuskyを順番に呼び出します。

他のprecommit用ライブラリにも応用できるような汎用的な方法ですが、
git cloneのたびに手動で設定する必要があったり、
いつのまにか設定が壊れちゃう可能性もあるので、今は方法1に移行しました。

## git-secretsをグローバルにインストール
```shell-session
brew update
brew install git-secrets
git secrets --register-aws --global
```

## git-secretsをプロジェクトで有効化

```shell-session
cd path/to/dir
git secrets --install
✓ Installed commit-msg hook to .git/hooks/commit-msg
✓ Installed pre-commit hook to .git/hooks/pre-commit
✓ Installed prepare-commit-msg hook to .git/hooks/prepare-commit-msg
```

## git-secretsが作ったhookスクリプトをサブフォルダ作って移動
```shell-session
mkdir .git/hooks/git-secrets
mv \
.git/hooks/commit-msg \
.git/hooks/pre-commit \
.git/hooks/prepare-commit-msg \
.git/hooks/git-secrets/
```

## huskyをプロジェクトにインストール
```shell-session
yarn add --dev husky
```

## huskyが作ったhookスクリプトをサブフォルダ作って移動
```shell-session
mkdir .git/hooks/husky
mv \
.git/hooks/commit-msg \
.git/hooks/pre-commit \
.git/hooks/prepare-commit-msg \
.git/hooks/husky/
```

## git-secretsとhuskyを順番に呼ぶhookスクリプト作成

```bash
#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR=$(cd $(dirname $0);pwd)

${SCRIPT_DIR}/git-secrets/${SCRIPT_NAME} "$@" && \
${SCRIPT_DIR}/husky/${SCRIPT_NAME} "$@"

exit $?
```

3ファイル全て上記の内容にしておけばOK

* .git/hooks/commit-msg
* .git/hooks/pre-commit
* .git/hooks/prepare-commit-msg 

## 実行権限の付与
```shell-session
chmod a+x \
.git/hooks/commit-msg \
.git/hooks/pre-commit \
.git/hooks/prepare-commit-msg
```

これで、コミット時は以下の順番で動いてくれます

1. git-secretsでチェック、ダメならエラー終了
2. huskyでチェック、ダメならエラー終了
3. 両方OKならコミット実行

## 注意点
git-secretsのhookスクリプトはシンプルなので良いんですけど、 **huskyのhookスクリプトはファイル名を変更すると動作しない**ので注意してください。

最初はサブフォルダ作らずに`.git/hooks/commit-msg.husky`みたいに変名して呼ぼうとしてたんですが、失敗しました。
hookスクリプト内でこんな風に「自分のファイル名をそのまんま別プログラムの引数に渡す」仕組みなので、そこが崩れるとおかしくなっちゃうんです。

```bash:pre-commit
hookName=`basename "$0"`
# 中略
  node_modules/run-node/run-node $scriptPath $hookName "$gitParams"
```
https://github.com/typicode/husky/blob/v1.1.2/src/installer/getScript.ts#L22-L40

なので「サブフォルダを切って、その中に同じファイル名で配置」してあげる必要があります。

# 参考サイト
[コミット前に Lint を強制するなら lint-staged が便利](https://qiita.com/ybiquitous/items/553479cfcb2cee124ae0)
[クラウド破産しないように git-secrets を使う](https://qiita.com/pottava/items/4c602c97aacf10c058f1)
[bashスクリプトのおかれているディレクトリの完全なパスを取得したい](https://qiita.com/t_cyrill/items/eb010de0fa40924bf31f)
[Bashの便利な構文だがよく忘れてしまうものの備忘録#$*, $@, "$@" の違い](https://qiita.com/Ping/items/57fd75465dfada76e633#---%E3%81%AE%E9%81%95%E3%81%84)

ではまた〜。