Kesinの知見置き場

知見を共有していきたいじゃないですか

textlintの結果をプルリクにコメントしてもらう(danger-textlintの紹介)

danger-textlintというdangerプラグインを公開しました!今回はその紹介エントリです。

dangerについて

そもそもdanger自体の知名度がまだまだだと思うので簡単に紹介したいと思います。

dangerはpull requestのコードレビューを助けるためのツールです。
dangerはpull requestのデータにrubyから簡単にアクセスするためのインターフェースと、pull requestにコメントするためのインターフェースを提供してくれます。
例えばdangerを使うとタイトルに[WIP]が含まれる場合は警告のコメントを出してマージできないようにする、ということが簡単に行えます。

textlintについて

textlintはコードの代わりに文章を対象としたlintツールです。最近、紹介エントリを見る機会が増えたのでだいぶ知名度が上がったと思います。

textlintのルールはeslintのようにそれぞれプラグインとなっており、自由にカスタムできることが特長です。 日本語でミスしやすいルールが数多く用意されており、出自からかエンジニアが書く文章のためのルールも充実しています。

個人的にはブログを書くときにtextlintはもはや手放せない存在です。また会社で社外向けブログを書くときはチームメンバーにレビューをしてもらうのですが、日本語のミスが残っていると肝心の内容よりも表現の指摘に終止しがちです。 自動赤ペン先生として事前にtextlintでチェックしておくことでレビューが生産的になります。

danger-textlintについて

dangerのプラグインは既にたくさんあり、その中にはlintの結果をプルリクの中でコメントとして付けてくれるものがあります。danger-textlintはそれらと同じようにtextlintのlint結果をコメントしてくれます。

実際にこのブログを書いているプルリクに対して実行した様子です。こんな感じでtextlintによる日本語のチェック結果をコメントしてくれます。

f:id:Kesin:20180217213341p:plain

導入方法

導入方法はREADMEにまとまめました。他のDangerプラグインと同様にgemをインストールしてDangerfileに以下を追加します。

# デフォルトだとtextlintの結果が1つでも違反しているとマージできなくなる
# max_severity = "warn" を設定するとDangerはコメントするだけに留めてマージまでは禁止しない
textlint.max_severity = "warn"

# 最低限これが必要
textlint.lint

加えて、そもそもtextlintを動かすためには当然nodejsが動く環境とtextlint自体のインストールも必要です。 danger-textlintはnpm i --globalオプションでインストールされても動くように作ってありますが、dangerはCIで動作させる前提のツールなので普通にnpm install textlintでローカルに保存した方が無難です。
node_modulesを.gitignoreに追加するのも忘れないようにしましょう。

CIの設定は普通にdangerを実行するだけですが、textlintを動かすのにnodejsが必要なのでruby + nodeの環境が必要です。自分はCircleCIを使うことが多いのでCircleCIのサンプルを載せておきます。

version: 2
jobs:
  build:
    docker:
       - image: circleci/ruby:2.4.1-node-browsers
    working_directory: ~/repo
    steps:
      - checkout

      # ruby dependencies
      - restore_cache:
          keys:
          - v1-dependencies-{{ checksum "Gemfile.lock" }}
          # fallback to using the latest cache if no exact match is found
          - v1-dependencies-

      - run:
          name: bundle install
          command: |
            bundle install --jobs=4 --retry=3 --path vendor/bundle

      - save_cache:
          paths:
            - ./vendor/bundle
          key: v1-dependencies-{{ checksum "Gemfile.lock" }}

      # npm dependencies
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}

      - run:
          name: npm install
          command: npm install

      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules

      - run:
          name: danger
          command: |
            bundle exec danger

なぜ作ったのか

自分が働いているチームではブログを書く環境がめちゃめちゃ整備されていて、そこら辺のプロダクトよりもPR周りの自動化が整備されているんじゃないかというほどです。 その中でDangerも使われているのですが、最近導入されたtextlintはDangerプラグインが存在しなかったのでフローにうまく統合できておらず、textlintに怒られたときは直接CIのコンソールから内容を確認しているという状況でした。

lintの結果はエディタで見るなら分かりやすいのですが、コンソールだと非常に確認しにくいです。CIが落ちたらローカルで再実行して確認すれば済む話ではあるのですが、プルリク上でそのままコメントしてくれた方が楽だし、他のレビュアーもただlintが落ちたという結果だけではなくてどの行でどのような間違えがあったのかを確認できるというメリットがあります。

textlintと同じnodejsで動くeslintのプラグインであるdanger-eslintが既に存在していたので参考にしつつ、サクッと作ってみました。

danger-checkstyle_formatについて

実はdanger-textlintを作る前にそもそも既に存在するdangerプラグインで実現できないか調べていて、その中でdanger-checkstyle_formatというプラグインを見つけました。 これはcheckstyleという形式のxmlを読み込んでDangerにコメントしてもらう汎用的に使えるプラグインです。textlintは--formatオプションで豊富な出力フォーマットを選択可能になっていて、checkstyle形式で出力することも可能です。

なので最初はtextlintが出力したcheckstyle形式のxmlをdanger-checkstyle_formatに食わせるという方法を試してみて、一応自分がやりたかったことは実現できました。ただ、textlintは1つでも違反があると終了ステータスが1になってしまうという仕様になっていて、CircleCIがtextlintで止まってしまうのでDangerの実行までステップが進まないという問題がありました。

その問題は一応こんな感じでtextlintの終了ステータスを握りつぶしてしまうことで回避することはできました。

run:
  name: textlint
  command: |
    set +e
    npm run textlint -- -f checkstyle -o textlint-result.xml
    echo 0

が、ちょっとハックな感じだったのでそれならと自分でDangerプラグインを作ってみました。 プラグイン化することで中間ファイルであるxmlも生成されなくなったし、Dangerfileだけみれば何をしているか一目瞭然になったので、なかなかよかったと思います。

まとめ

というわけでdanger-textlintの紹介でした。
既にGithubのプルリクでレビューをもらいながらブログを書ける環境の方はDangerとdanger-textlintを、まだプルリク運用できていない場合はまずは次の記事からGithubにでもリポジトリを用意して快適な執筆フローを整えてみてはいかがでしょうか。