GitHub ActionsのProblem Matchersでtextlintの指摘を表示する

## :book: あらまし

弊社LIFULLではQiita以外に[LIFULL Creators Blog](https://www.lifull.blog/)でも社外発信を行っています。

このブログの運用フローを考えるにあたって、`.md` ファイルに対してPRを作ったタイミングで[textlint](https://github.com/textlint/textlint)をかけて修正を促すということに取り組みました。

その中で、GitHub Actionsを使っていて便利だと感じた[Problem Matchers](https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md)について書きます。

検証に利用した[ソースコードはこちら](https://github.com/nanakenashi/textlint-test)に公開しています。

## :beginner: 最初に設定した内容

GitHub Actionsのワークフローにおいて、`.md` ファイルを含むPRが作成されたときにはNode.jsのバージョン14でtextlintを実行するように設定しています。

```yml:.github/workflows/main.yml
name: Exec textlint
on:
  pull_request:
    types:
      - opened
      - synchronize
    paths:
      - "articles/**.md"
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout sources
        uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - name: Setup Node.js environment
        uses: actions/setup-node@v2.1.2
        with:
          node-version: "14"
      - name: Install dependencies
        run: npm ci
      - name: Exec textlint
        run: |
          git diff origin/${GITHUB_BASE_REF}..origin/${GITHUB_HEAD_REF} \
            --diff-filter=AM \
            --name-only -- '*.md' \
            | xargs npm run lint
```

参考: [GitHub Actions を用いて Pull-Request で shellcheck をする方法](https://qiita.com/osakiy/items/27d5382e41107de482a4)

textlintの設定は記事の本筋ではありませんが、いくつかそれらしいものを利用しています。

```json:.textlintrc
{
  "rules": {
    "preset-japanese": true,
    "ja-no-abusage": true,
    "ja-no-mixed-period": true,
    "ja-unnatural-alphabet": true,
    "prh": {
      "rulePaths": [
        "node_modules/prh/prh-rules/media/WEB+DB_PRESS.yml"
      ]
    }
  }
}
```

これらを設定したうえでPRを作成するとワークフローは正常に動きます。
![2020-12-08-23-01-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/4c2b039c-064a-81a3-997c-b519226080cb.png)
何もやってないのにインラインコメントまで入っている！賢い！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/bc7a29f9-0dcd-647a-660f-fa82da92ddc7.png)
ただしなんだかチェック項目が偏っていそう…？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/c9fbc2bf-ea05-7697-fc2a-96ae5d7e1622.png)
ローカルで実行するともっとたくさんあるので、どうやらなんだかおかしそうです。
![sample_md_—_textlint-test__Workspace_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/25ded4a6-7592-6dd7-5423-aef1eaba31a5.png)

## :thought_balloon: 動作の解釈

### 自動でインラインコメントが入る理由

正確には一部の結果がAnnotationsに書き出されています。私の疑問はこちらの[記事](https://blog.shibayu36.org/entry/2020/11/09/173000)を拝見して解消しました。

- 出力から正規表現でパターンを抜き出して表示する機能 -> [Problem Matchers](https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md)
- [.githubディレクトリ](https://github.com/actions/setup-node/tree/main/.github)以下に定義ファイルが存在
- アクション（setup-node）の中でeslintの設定を有効化

つまり、setup-nodeアクションを利用することでeslintの結果をアノテーションに表示する機能が有効になり、それがtextlintの出力も拾ってしまっているようです。

ちなみに[textlint](https://github.com/textlint/textlint)も自分がeslintに似ていることを自覚しています。

>The pluggable linting tool for text and markdown.
textlint is similar to ESLint, but it's for use with natural language.

### textlintの指摘内容を部分的に拾う理由

[eslint向きの正規表現](https://github.com/actions/setup-node/blob/main/.github/eslint-stylish.json)でマッチする箇所だけが表示されるようです。下記のような記述がされており、textlintの指摘で「✓」が入る場合は考慮されず対象から外れるように見えます。

```eslint-stylish.json
"regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$"
```

※ textlintではautofixが可能な場合に「✔」が表示されます

## :bulb: textlint用の設定追加

すべての指摘内容を表示できるように、textlint用のProblem Matchersを設定します。

### Problem Matchers

[.github/textlint-matcher.json](https://github.com/nanakenashi/textlint-test/blob/main/.github/textlint-matcher.json)を作成します。[eslintの設定](https://github.com/actions/setup-node/blob/main/.github/eslint-stylish.json)とほとんど変わらないので、コピペさせていただいたうえで少し書き換えています。

#### owner

Problem Matchersを無効にしたり差し替える場合に利用するIDです。今回は使いませんがユニークっぽいものにしておきます。

```.github/textlint-matcher.json
"owner": "textlint-matcher"
```

#### regexp

「✔」と直後の半角スペースを考慮するため `✓*\\s*` を追記します。

```.github/textlint-matcher.json
"regexp": "^\\s+(\\d+):(\\d+)\\s+✓*\\s*(error|warning|info)\\s+(.*)\\s\\s+(.*)$"
```

#### 補足

[ドキュメント](https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md#single-line-matchers)を呼んでも下記の記述の意味がさっぱりわからなかったのですが、`regexp` においてどの箇所からその要素が取得できるかを示しているようです。

```.github/textlint-matcher.json
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
```

例: 行数は1ブロック目の `(\\d+)`、重大度は3ブロック目の `(error|warning|info)` から取得できる）

### ワークフローからの利用

[echoで出力する](https://github.com/actions/toolkit/blob/master/docs/commands.md#problem-matchers)ことで設定ができます。

```.github/workflows/main.yml
- name: Enable problem matcher
  run: echo "::add-matcher::.github/textlint-matcher.json"
```

### 動作確認

「✔」が入っていた箇所もアノテーションに表示されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/7d58b7f9-4809-5f6c-9cb4-448c8a8b050a.png)
ただし、textlintのルールによっては表示の形式が変わるため、下記の17行目のようにうまく抽出できない箇所もありました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43116/ebe11683-9afa-5d00-099b-34e706f00c93.png)
運用するためにはもう少し改良が必要そうです。

## :warning: 注意

[ワークフロー内で生成できるアノテーションの数には上限がある](https://github.community/t/maximum-number-of-annotations-that-can-be-created-using-github-actions-logging-commands/16991)ため、ここですべての指摘を表示しきるのは難しいかもしれません。私はここでアノテーションを利用しない[reviewdog](https://github.com/reviewdog/reviewdog)を利用する方針に切り替えました。

## :end: まとめ

textlintの指摘表示に取り組んでみました。が、Problem Matchersはもう少し出力の量が限られるユースケースの方がより良く利用できそうです。

ちなみにこの記事にはtextlintをかけていませんが、読みづらい箇所はありませんでしたか。明日以降もLIFULLアドベントカレンダーは続きますので、ご期待ください！