Travis CIでtextlintの指摘をPull Requestのレビューコメントとして書き込む

[textlintをTravis CIで動かして継続的に文章をチェックする - Qiita](http://qiita.com/azu/items/e36501d25593d008f6ac "textlintをTravis CIで動かして継続的に文章をチェックする - Qiita")

の続き的な話です。

[textlint](http://textlint.github.io/ "textlint")のチェックが失敗したら、Pull Requestのコメントとして書いてくれるものを作る仕組みの話です。

![image](https://monosnap.com/file/pQVyj41nnz6dFNmTRPcogz1mXZ81Qf.png)

一般にLint系をCIで走らせると、どこで失敗したのかを確認しにくくCIまで見に行くのは面倒だし分かりにくいです。
GitHubではレビューコメントで該当行にコメントを書けるので、直接そこへ指摘のコメントある方が良いです。

[Hound](https://houndci.com/ "Hound")がまさにそういうサービスですが、任意のツールには対応していません。

[Saddler](https://github.com/packsaddle/ruby-saddler "Saddler")というツールが同じことをTravis CIやCircle CIから行う事ができます。

- [変更したファイルにrubocopやjscsを実行して pull requestに自動でコメントする – Saddler - checkstyle to anywhere](http://packsaddle.org/articles/saddler-overview/)
- [packsaddle/ruby-saddler](https://github.com/packsaddle/ruby-saddler)


## .travis.ymlの設定

普通のCIの設定は[textlintをTravis CIで動かして継続的に文章をチェックする - Qiita](http://qiita.com/azu/items/e36501d25593d008f6ac "textlintをTravis CIで動かして継続的に文章をチェックする - Qiita")にある通りなので、そこまでは既にできているという前提です。

レビューコメントを書き込むタイミングは、テスト失敗(Lint失敗)した時なので、`.travis.yml`の`after_failure`にレビューコメントを書き込むシェルスクリプトを追加します。

```yaml
sudo: false
language: node_js
node_js: "stable"
after_failure:
  - ./.travis/review-textlint.sh
```

Rubyが必要なので、`language: ruby`をベースにする場合は以下のように設定できます。

```yaml
sudo: false
language: ruby
rvm:
- '2.2'
env:
  - NODE_JS_VERSION='6'
install:
  - nvm install $NODE_JS_VERSION
  - nvm use $NODE_JS_VERSION
  - npm install
script:
  - npm test
after_failure:
  - ./.travis/review-textlint.sh
```

Travis CIにはデフォルトでrubyが入ってる言語は多いので、明示的にrubyを入れなくても動くとは思います。

### GitHubのTokenの設定

Travis CIから[Saddler](https://github.com/packsaddle/ruby-saddler "Saddler")がレビューコメントを書くためには、GitHub Tokenの設定が必要です。

`GITHUB_ACCESS_TOKEN=xxx` という環境変数が設定されていれば自動的に利用できます。

[Personal Access Tokens](https://github.com/settings/tokens "Personal Access Tokens")から`repo`の権限を含んだTokenを発行して、コマンドラインから`travis`コマンドで次のように設定すれば環境変数が定義されます。

```shell-session
travis env set GITHUB_ACCESS_TOKEN xxx
```

また、Travis CIの該当リポジトリの設定画面からも設定することができます

![setting](https://monosnap.com/file/zeT5gcxdBfYGUjvmRw2cWSuPFIYOh8.png)

### review-textlint.sh

`review-textlint.sh`では、そのPull Requestで変更したファイルのみ[textlint](http://textlint.github.io/ "textlint")でLintしてその結果をPull Requestに書き込んでいます。

Travis CIでは`git clone`がshallowであるため、改めて他のブランチなどを取得する作業が必要です(Circle CIでは不要です)。
これをしないと `git diff` で差分のファイル名取得が上手くできないと思います。

- [bash - GIT fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree in Travis - Stack Overflow](http://stackoverflow.com/questions/37303969/git-fatal-ambiguous-argument-head-unknown-revision-or-path-not-in-the-workin "bash - GIT fatal: ambiguous argument &#39;HEAD&#39;: unknown revision or path not in the working tree in Travis - Stack Overflow")

また、Pull Request時以外は実行しないように `"$TRAVIS_PULL_REQUEST"`で分岐しています。(Circle CIでは `CI_PULL_REQUEST` を見る必要があります)

```sh
#!/bin/bash

# Test if pull request
if [ "$TRAVIS_PULL_REQUEST" = "false" ] || [ -z "$TRAVIS_PULL_REQUEST" ]; then
  echo 'not pull request.'
  exit 0
fi

# Fetch other branch
# To avoid ambiguous argument
# http://stackoverflow.com/questions/37303969/git-fatal-ambiguous-argument-head-unknown-revision-or-path-not-in-the-workin
if [ "$TRAVIS" == "true" ]; then
  #resolving `detached HEAD` by attaching HEAD to the `TRAVIS_FROM_BRANCH` branch
  TRAVIS_FROM_BRANCH="travis_from_branch"
  git branch $TRAVIS_FROM_BRANCH
  git checkout $TRAVIS_FROM_BRANCH

  #fetching `TRAVIS_BRANCH` branch
  git fetch origin $TRAVIS_BRANCH
  git checkout -qf FETCH_HEAD
  git branch $TRAVIS_BRANCH
  git checkout $TRAVIS_BRANCH

  #switch to `TRAVIS_FROM_BRANCH`
  git checkout $TRAVIS_FROM_BRANCH
fi

# Install saddler
# https://github.com/packsaddle/ruby-saddler
# Need secret env: `GITHUB_ACCESS_TOKEN=xxx`
gem install --no-document checkstyle_filter-git saddler saddler-reporter-github

# Diff Target Branch
# diff HEAD...target
# http://stackoverflow.com/questions/3161204/find-the-parent-branch-of-a-git-branch
# http://qiita.com/upinetree/items/0b74b08b64442f0a89b9
declare diffBranchName=$(git show-branch | grep '*' | grep -v "$(git rev-parse --abbrev-ref HEAD)" | head -1 | awk -F'[]~^[]' '{print $2}')

# filter files and lint
echo "${diffBranchName}...HEAD"
echo "textlint -> review_comments"
git diff --name-only --diff-filter=ACMR ${diffBranchName} \
| grep -a '.*.md$' \
| xargs $(npm bin)/textlint -f checkstyle \
| checkstyle_filter-git diff ${diffBranchName} \
| saddler report \
    --require saddler/reporter/github \
    --reporter Saddler::Reporter::Github::PullRequestReviewComment
```

後はこのファイルを `.travis/review-textlint.sh` に置いて、実行できるように`chmod +x .travis/review-textlint.sh`します。

これでレビューコメントとしてLint結果を書き込む事ができるようになりました。

![image](https://monosnap.com/file/pQVyj41nnz6dFNmTRPcogz1mXZ81Qf.png)


## 参考プロジェクト

- [azu/JavaScript-Plugin-Architecture: JavaScriptプラグインアーキテクチャの本](https://github.com/azu/JavaScript-Plugin-Architecture "azu/JavaScript-Plugin-Architecture: JavaScriptプラグインアーキテクチャの本")
- [Review Test by azu · Pull Request #118 · azu/JavaScript-Plugin-Architecture](https://github.com/azu/JavaScript-Plugin-Architecture/pull/118 "Review Test by azu · Pull Request #118 · azu/JavaScript-Plugin-Architecture")