production.log

株式会社リブセンスでエンジニアをやっている星直史のブログです。

Re:VIEWで書いた文章の校正をCircleCIとtextlintでGitHubのPRに自動コメントする仕組み

概要

技術書典でRe:VIEWを使っていたものの、文章を書き、textlintを回すというCI環境を整えることができませんでした。また、このCI環境は、ブログ執筆においても有効であるため、このタイミングで構築することにしました。

今回はRe:VIEWで書いた文章の校正をCircleCIとtextlintでGitHubのPRに自動コメントする仕組みを作ったのでその紹介をします。

仕様🖋

仕様は下記の通りです。

  • Re:VIEWで書くファイルの拡張子である.reをこれをサポートすること
  • Markdownで書くファイルの拡張子である.mdをサポートすること
  • master以外のブランチにpushした場合にCircleCIでtextlintが実行されること
  • textlintで警告が出た場合は、GitHubのPRに自動でコメントすること

f:id:watasihasitujidesu:20181013193515p:plain

構築手順👨‍💻

textlintの導入

まず最初にローカルでtextlintが実行できるようにします。 textlintはnpmで入れていきます。

$ node -v
v8.11.3

$ npm -v
6.3.0

続いて、textlintのインストールと、textlintで使うルールのインストールを行います。

$ npm init -y
$ npm install --save-dev textlint textlint-rule-max-ten

上記で入れるtextlint-rule-max-tenは「一つの文で出現する読点の数をチェックする」ルールです。

次に、textlintのデフォルトでは、Re:VIEWの拡張子である.reに対応していないため、プラグインを入れる必要があります。

$ npm install --save-dev textlint-plugin-review

最後にtextlintの設定ファイルで、ルールとプラグインを有効にします。

{
  "rules": {
    "max-ten": true
  },
    "plugins": [
      "review"
    ]
}

この状態でtextlintを実行すると下記のような結果となります。

$ ./node_modules/.bin/textlint sample.re

/Users/naoshihoshi/repo/sample.re
  1:11  error  一つの文で""3つ以上使用しています  max-ten

✖ 1 problem (1 error, 0 warnings)

今回は便宜上、textlint-rule-max-tenだけの紹介ですが、そのほかにも様々なルールが存在するので、お好みで設定していくのが良いと思います。

CircleCIの設定

CircleCIは2017年夏あたりにバージョン2.0がリリースされましたので、今回はそれで作っていきます。

バージョン2.0からは.circleci/config.ymlの書き方が変わりましたが、公式のドキュメントがとてもわかりやすかったです。
個人的には、後述するconfig.ymlの紹介で全体感を見た後にドキュメントを読むとより理解が進みやすくなると思います。

circleci.com

.circleci/config.yml

バージョン2.0からはDockerが使えるようになります。また、CircleCIが用意しているイメージも各言語揃っています。 今回、GitHubへのコメントにRubyのgemを使うので、Rubyのイメージを使用します。

version: 2
jobs:
  build:
    docker:
      - image: circleci/ruby:2.4.1-node-browsers
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          command: npm install
      - run:
          command: ./.circleci/review-textlint.sh

最終行で指定している./.circleci/review-textlint.shでtextlintの実行とGitHubのPRヘコメントを実行します。

textlintの実行とGitHubのPRヘコメント

まず、GitHubへの操作をするためのアクセストークンを取得し、CircleCIに設定をします。

GitHubのアクセストークンを取得

GitHubにアクセスし、Settings > Developer settings > Personal access tokensページに遷移し、Generate new tokenでアクセストークンを取得します。

このトークンは次で使うのでメモっておいてください。また、このトークンは、この画面を離れると二度と表示されなくなります。

GitHubのアクセストークンをCircleCIに設定

CircleCIにアクセスし、Settings > ${ユーザー名} > ${リポジトリ名} > Environment Variablesに遷移しAdd Variableボタンを押下します。
すると、モーダルが表示されるので、下記の通り入力します。

  • Name: GitHub_ACCESS_TOKEN
  • Value: GitHubで作ったアクセストークン

f:id:watasihasitujidesu:20181013190945p:plain

これで準備が整いました。最後にスクリプトを書いていきます。

.circleci/review-textlint.sh

#!/bin/bash

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

REMOTE_MASTER_BRANCH="remotes/origin/master"

gem install --no-document findbugs_translate_checkstyle_format checkstyle_filter-git saddler saddler-reporter-GitHub

# filter files and lint
echo "Put textlint review comments to GitHub"

declare diffFiles=$(git diff ${REMOTE_MASTER_BRANCH} --diff-filter=ACMR --name-only | grep -a '\.[re$|md$]')
echo ${diffFiles}

if [ -n "$diffFiles" ]; then
  echo ${diffFiles} | xargs ./node_modules/.bin/textlint -f checkstyle \
  | sed -e 's/\"/\'/g' \
  | checkstyle_filter-git diff ${REMOTE_MASTER_BRANCH} \
  | saddler report \
      --require saddler/reporter/github \
      --reporter Saddler::Reporter::Github::PullRequestReviewComment
  exit 1
fi

それでは処理をざっくり説明していきます。

GitHubのPRの存在確認
まず、GitHub上にPRがない場合はコメント先がないため、処理をスキップする条件分岐を書いています。

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

gem install
次にgem installを実行します。 今回は、GitHubへのコメントはSaddlerを使って行います。

gem install --no-document findbugs_translate_checkstyle_format checkstyle_filter-git saddler saddler-reporter-GitHub

textlint対象ファイル抽出
この処理は、textlintの対象ファイルを抽出するコマンドです。 リモートのmasterと、CircleCIで回しているリポジトリのdiffを取得し、拡張子が.reと.mdのファイルだけを抜き出しています。

declare diffFiles=$(git diff ${REMOTE_MASTER_BRANCH} --diff-filter=ACMR --name-only | grep -a '\.[re$|md$]')
echo ${diffFiles}

GitHubへコメント送信
最後に、textlintを実行した結果をXML化し、GitHubのPRへコメント送信処理を行います。
エラーがある場合は、XMLのエスケープ処理を行なった上で、GitHubへ送信します。
具体的な処理はSanddlerにお任せしています。

if [ -n "$lintResult" ]; then
  echo ${lintResult} | sed -e 's/\"/\'/g' \
  | checkstyle_filter-git diff ${REMOTE_MASTER_BRANCH} \
  | saddler report \
      --require saddler/reporter/GitHub \
      --reporter Saddler::Reporter::GitHub::PullRequestReviewComment
  exit 1
fi

処理は以上です。

動作確認🚨

まず、適当なリポジトリにブランチを切ります。
次に、.reか.mdファイルを作成します。ファイルの中身は読点を3つ以上含んだ文を書いておきます。

例)

読点が、3つ以上の、文にしておくと、GitHubに、コメントが、つくはず。

最後に、そのファイルをpushします。

CircleCIの確認

f:id:watasihasitujidesu:20181013192617p:plain

良い感じに落ちてますね!

GitHubのPRコメントの確認

f:id:watasihasitujidesu:20181013192716p:plain

ばっちりですね👏

まとめ✨

今回は、CircleCIとGitHubのPRを用いて、textlintを実行、コメントしてくれる環境を作りました。
執筆していたときは、都度textlintを実行していたのが本当にめんどくさかったです...。
今回構築してみて、比較的簡単に構築することができたので、これから執筆活動をする方は、まずこのCI環境を作ってから執筆することをお勧めします!