🔗

GitHub Actions 上で Markuplint のエラーログを検出してジョブを止める方法

2024/02/07に公開

Markuplint を初めて使ってみた。
存在はしっていたものの、実際には使ったことがなかったので検証してみました。

この記事は、markuplintの検出されたログをGithub Actionsでエラー検知する方法を紹介します。

https://markuplint.dev/ja/

結論

先に結論から述べます。

https://github.com/reviewdog/reviewdog

普段の開発では、reviewdog というツールを使いレポートをチェックしてジョブを失敗させています。
しかし、reviewdog のサポートには、markuplintは含まれていません。
ジョブを失敗させるため自作スクリプトを用意することにしました。

自作したシェルスクリプトは、以下のコードです。

https://github.com/redamoon/markuplint-github-actions-vue/blob/main/markuplint-check.sh

検証リポジトリ

ブログ記事用に検証リポジトリを用意しました。

https://github.com/redamoon/markuplint-github-actions-vue/tree/main

はじめに

まずはじめに環境を用意するところから始めていきます。
Vue.js のプロジェクトが多く担当しているので、本記事では Vue.js を前提に進めます。

  • Node.js 20.11.0
  • Vue.js 3系
  • Markuplint 4系

導入時期で変わりますが、導入時は Markuplint 3系を利用していました。
しかし、 *.vue ファイルに対して --fix オプションを使うと HTML 以外の部分が削除されてしまうという問題がありました。

https://x.com/cloud10designs/status/1753354754124874057?s=20

タイミングよく 4系がリリースされたため 4系を利用することにしました。

https://x.com/cloud10designs/status/1754031695312101752?s=20

本題

ここから本題の GitHub Actions でのエラー検知方法を紹介します。
まずは、Vue.js プロジェクトに Markuplint して導入します。

Vue.jsでプロジェクトを作成

Vue.jsの公式ドキュメント通りに create vue@latest コマンドでプロジェクトを作成します。

npm create vue@latest

Vue.jsのプロジェクトを作成します。

Markuplint の導入

公式のドキュメント に設定ファイルを生成するCLIが用意されています。

npx markuplint --init を実行すると、対話式で設定ファイルを生成することができます。

  • template: Vue // Vueを選択
  • May I install them automatically: true // 自動でインストールする
  • customize rules: false // Customルールは検証のため false(必要に応じて設定します)
  • import the recommended config: true // 推奨設定をインポートする
npx markuplint --init

? Which do you use template engines? …
✔ React (JSX)
✔ Vue
✔ Svelte
✔ SvelteKit
✔ Astro
✔ Pug
✔ PHP
✔ Smarty
✔ eRuby
✔ EJS
✔ Mustache/Handlebars
✔ Nunjucks
✔ liquid (Shopify)

? May I install them automatically? (y/N) true

? Do you customize rules? (y/N)false
? Does it import the recommended config? (y/N)true

インストールされた ルート配下に .markuplintrc が生成されます。

{
  "extends": [
    "markuplint:recommended"
  ]
}

次に、HTML環境以外で利用する(.vueファイル)ため、Vueのparserとspecsをインストールします。公式ドキュメントが用意されています。

この2つをインストールすることで、独自の構文がある場合に対応することができます。

npm install -D @markuplint/vue-parser @markuplint/vue-spec

インストール後、 .markuplintrc にparserとspecsを追記します。

{
  "extends": [
    "markuplint:recommended"
  ],
  "specs": {
    "\\.vue$": "@markuplint/vue-spec",
  },
  "parser": {
    "\\.vue$": "@markuplint/vue-parser",
  }
}

package.jsonにスクリプトを追加

package.jsonに markuplintを実行できるコマンドを追加します。

ここでのコマンドは、src ディレクトリ配下の *.vue ファイルを対象に --fix コマンドで対象ファイルの修正および --config 設計ファイルのパスを定義します。
-f でJSONを選択することで、エラー結果をJSONで出力することができます。

詳しいコマンドは、公式のコマンドインターフェイスを参照してください。

{
  "scripts": {
    "html:fix": "markuplint -f JSON src/**/*.vue --fix --config .markuplintrc"
  }
}

試してみる

試しにコマンドが実行され、エラーログを出力できるか確認をします。

https://github.com/redamoon/markuplint-github-actions-vue/pull/3/files#diff-e17ea97189bf2c22f00b4906c6ccd156a96060cc8fdeccd3a4ee23bfa2a758adR10

ここでは、試しに以下のようなHTMLコードを用意しておきました。
ボタンタグにdivタグを入れることでエラーを発生させます。

<button><div>エラーを出す</div></button>

npm run html:fix を実行すると、以下のようなエラーログが出力されます。

[
  {
    "severity": "error",
    "message": "このコンテキストでは、要素「button」に要素「div」を含めることはできません",
    "line": 10,
    "col": 13,
    "raw": "<div>",
    "ruleId": "permitted-contents",
    "filePath": "XXXX/src/components/HelloWorld.vue"
  }
]

このように対象ファイルを横断的に検証し、エラーログを出力することができます。

GitHub Actions のワークフローを作成

GitHub Actions の サンプルジョブを用意しました。
PRをトリガーに Markuplint を実行し、エラーログがあればジョブを失敗させることができます。

name: lint
on:
  pull_request:
    branches: [ main, develop ]
jobs:
  lint:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '20.11.0'
      - name: npm install
        run: npm install
      - name: Run Markuplint
        run: |
          ./markuplint-check.sh

最後の行で、 ./markuplint-check.sh を実行しエラーを出力した場合にジョブが失敗するようにしました。

  1. npm run html:fix を実行してエラーログを出力
  2. sed で1行目から4行目を削除
  3. 整形したログを temp_results.json に1時保存
  4. [] でない場合はエラーとしてジョブを失敗させる、それ以外は Success: No issues found by markuplint. を出力
#!/bin/bash

# markuplint を実行して結果を一時ファイルに保存
# sed を使用して最初の4行を削除
npm run html:fix | sed '1,4d' > temp_results.json

# 結果ファイルが空でないかつ、"[]" ではない場合、エラーがあると見なす
if [ -s temp_results.json ] && [ "$(cat temp_results.json)" != "[]" ]; then
    echo "Error: markuplint found issues."
    # エラー詳細をログに出力
    cat temp_results.json | jq '.[] | "\(.filePath):\(.line):\(.message)"' | while read line; do
        echo "Error: $line"
    done
    # 一時ファイルを削除
    rm temp_results.json
    # 非ゼロの終了コードでスクリプトを終了
    exit 1
else
    echo "Success: No issues found by markuplint."
    # 一時ファイルを削除
    rm temp_results.json
    # スクリプトを正常終了
    exit 0
fi

先ほどの検証コードで利用した buttonタグにdivタグを挿入して PR を作成します。
エラーを起こしたPRは以下のジョブです。

https://github.com/redamoon/markuplint-github-actions-vue/actions/runs/7801339258/job/21276189560#step:5:7

エラーが検知され、ジョブが失敗していることが確認できます。

まとめ

このように、GitHub Actions で Markuplintのエラー出力に応じて ジョブを失敗させるためには 出力結果の状態に応じて行う形が必要です。
初めて使ったので、やり方が違うかもしれませんが自分はこの実装で対応してみました。

使用感として、reviewdog でもサポートされると嬉しいですが、出力結果のフォーマットでファイルで保存できるコマンドがあると嬉しいと感じました。
( sed は、拡張性の観点から個人的には避けたい)

個人的にMarkuplintに欲しい機能

  • format type file コマンドでエラーログだけをファイル出力できるコマンド

GitHub Actions でエラー検知してジョブを失敗させたい場合の参考として読んでいただけると幸いです。

株式会社HAMWORKS

Discussion