☠️

Problem Matcherの登録とGithub Annotationへの反映

2024/09/09に公開

loglint というエラーログから正規表現で該当部分を検出する仕組みを以前に作ったのだけど、Github Actionsで同様の仕組みがあることを知ったので調べたメモ。

概要

Problem Matcher

  • GitHub Actionsで実行したログからエラー部分を正規表現で抽出する仕組み
  • 下記JSONをプロジェクト内に配置して、Github Actionのログに ::add-matcher::hoge.json をprintしておけばいい.

具体例

NextJSのビルドログのmatcherを試してみた。

検出したいサンプルログ。

./src/lib/sample.ts
Error: 
  x Expression expected
   ,-[/home/runner/work/sample-problem-matcher/sample-problem-matcher/src/lib/sample.ts:1:1]
 1 | export function calc(a: number, b: number): number {
 2 |   return a ^^ b;
   :             ^
 3 | }
   `----

Caused by:
Error:     Syntax Error

実際のログにはterminalのcolorエスケープシーケンスが挿入されているので、下記の感じ。

./src/lib/sample.ts
Error:
  ESC[31mxESC[0m Expression expected
   ,-[ESC[36;1;4m/Users/mattak/github/mattak/sample-problem-matcher/src/lib/sample.tsESC[0m:1:1]
 ESC[2m1ESC[0m | export function calc(a: number, b: number): number {
 ESC[2m2ESC[0m |   return a *** b;
   : ESC[31;1m             ^ESC[0m
 ESC[2m3ESC[0m | }
   `----

Caused by:
    Syntax Error

sample-matcher.json

.github/sample-matcher.json
{
  "problemMatcher": [
    {
      "owner": "next-build",
      "severity": "error",
      "pattern": [
        {
          "regexp": "^(\\./.+)$",
          "file": 1
        },
        {
          "regexp": "^\\s*Error:\\s*$"
        },
        {
          "regexp": "^\\s+\\S*x\\S* (.+)$",
          "message": 1
        },
        {
          "regexp": "\\[(.+):(\\d+):(\\d+)\\]$",
          "line": 2,
          "column": 3
        },
        {
          "regexp": "^\\s*Caused by:\\s*$"
        },
        {
          "regexp": "^\\s+(.+)$",
          "code": 1
        }
      ]
    }
  ]
}

定義ファイルの仕組み

  1. pattern内に検出したいログ1行に相当する部分を記述する
  2. 複数行に渡る場合はjson array内に複数記述する
  3. regexpにログ検出する正規表現を記述する
  4. 正規表現のgroup matchした各indexと対応付ける感じで、message,file,line,column,code等を記述する

あとは、actionsのworkflow定義に ::add-matcher::sample-matcher.json を追記する.

https://github.com/mattak/sample-problem-matcher/actions/runs/10730281535/workflow

.github/workflows/deploy.yml
name: Deploy Next.js Static Site to GitHub Pages

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # リポジトリをチェックアウト
      - name: Checkout repository
        uses: actions/checkout@v4

      # Node.js をセットアップ
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      # パッケージをインストール
      - name: Install dependencies
        run: npm install

      # problem-matcher
      - name: Add NextJS problem matcher
        run: echo "::add-matcher::.github/sample-matcher.json"

      # ビルド
      - name: Build the project
        run: npm run build

      # デプロイ
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./out

結果

Actionsのログ

Actionsのログ

ActionsのAnnotation に対象部分が検出されている

Actions Summary
Detail of Annotations

リンク先に飛ぶと対象コードをハイライトできている
Actions Summary

その他

手元で実験する方法がわからなかったので、rubyで標準入力から正規表現の
マッチ部分を検証するコードを書いた。

#!/usr/bin/env ruby

regexes =
  [
    Regexp.new("^(\\./.+)$"),
    Regexp.new("^\\s*Error:\\s*$"),
    Regexp.new("^\\s+\\S*x\\S* (.+)$"),
    Regexp.new("\\[(.+):(\\d+):(\\d+)\\]$"),
    Regexp.new("^\\s*Caused by:\\s*$"),
    Regexp.new("^\\s+(.+)$"),
  ]

lines = STDIN.map {|x| x}
i = 0
lines.each_with_index do |line, n|
  break if i >= regexes.size
  if m = regexes[i].match(line)
    puts "** MATCH\t#{n}\t#{m.captures}"
    i+=1
  end
end

あと、Custom Action作るならr7kamuraさんのコードがシンプルで参考になった.
::add-matcher::をconsole.logするだけでシンプル..!

参考

Discussion