T-CREATOR

ESLint 運用ダッシュボード:SARIF/Code Scanning で違反推移を可視化

ESLint 運用ダッシュボード:SARIF/Code Scanning で違反推移を可視化

ESLint の静的解析結果を継続的にモニタリングし、プロジェクト全体のコード品質を可視化したいと考えたことはありませんか。

GitHub Actions で ESLint を実行するだけでは、その時点での違反は検出できますが、時系列での推移や傾向を把握することは難しいでしょう。そこで注目されているのが、SARIF(Static Analysis Results Interchange Format)と GitHub Code Scanning を組み合わせた運用ダッシュボードです。

本記事では、ESLint の実行結果を SARIF 形式で出力し、GitHub Code Scanning にアップロードすることで、違反の推移を継続的に可視化する方法を解説します。これにより、プロジェクトのコード品質を時系列で追跡し、改善の成果を数値で確認できるようになりますね。

背景

ESLint とコード品質管理の課題

ESLint は JavaScript や TypeScript プロジェクトにおいて、コードの品質を保つための必須ツールとなっています。しかし、従来の運用方法では以下のような課題がありました。

プロジェクトが成長するにつれて、ESLint の違反件数は増加傾向にあります。開発者は日々のコミットで新しいコードを追加しますが、既存の違反を修正する優先度は低くなりがちです。その結果、技術的負債が蓄積し、いつの間にか違反が数百件に達してしまうこともあるでしょう。

また、ESLint の実行結果はコンソールに出力されるだけで、過去の結果と比較することができません。「先月と比べて違反は減っているのか」「新しいルールを追加した効果はあったのか」といった疑問に答えるデータがないのです。

SARIF と Code Scanning の登場

こうした課題を解決するために、SARIF という標準化された静的解析結果のフォーマットが注目されています。SARIF は OASIS によって策定された JSON ベースの形式で、さまざまな静的解析ツールの結果を統一的に表現できます。

GitHub は Code Scanning という機能を提供しており、SARIF 形式の結果をアップロードすることで、セキュリティアラートやコード品質の問題を一元管理できるようになりました。これにより、ESLint の違反を GitHub のインターフェースで可視化し、時系列での推移を追跡できるのです。

以下の図は、従来の ESLint 運用と SARIF/Code Scanning を使った運用の違いを示しています。

mermaidflowchart TB
  subgraph legacy ["従来の運用"]
    dev1["開発者"] -->|コミット| ci1["CI/CD"];
    ci1 -->|ESLint 実行| result1["コンソール出力"];
    result1 -->|一時的| end1["結果は保存されない"];
  end

  subgraph modern ["SARIF/Code Scanning 運用"]
    dev2["開発者"] -->|コミット| ci2["CI/CD"];
    ci2 -->|ESLint 実行| sarif["SARIF 出力"];
    sarif -->|アップロード| github["GitHub<br/>Code Scanning"];
    github -->|可視化| dashboard["ダッシュボード"];
    dashboard -->|時系列分析| insights["改善の追跡"];
  end

従来の運用では結果が一時的なものでしたが、SARIF/Code Scanning を使うことで、継続的な可視化と分析が可能になります。

Code Scanning のメリット

Code Scanning を使うことで得られるメリットは以下の通りです。

#メリット説明
1時系列での推移確認違反件数の増減をグラフで確認できる
2PR ごとの影響分析Pull Request で新たに追加された違反を自動検出
3一元管理セキュリティアラートとコード品質を同じ場所で管理
4チーム共有誰でも最新の状況を確認できる
5アラート機能重要度の高い違反を通知できる

これらの機能により、コード品質の改善活動を計画的に進めることができるでしょう。

課題

ESLint 結果の継続的な追跡が困難

従来の ESLint 運用では、実行結果がコンソールログやテキストファイルとして出力されるだけでした。この方法では、以下のような問題が発生します。

まず、過去の結果との比較が手作業になってしまいます。「今月の違反件数は先月より増えたのか減ったのか」を知るには、過去のログを探して手動で集計する必要があるでしょう。開発チームが忙しい中で、このような作業を継続することは現実的ではありません。

次に、違反の種類や重要度ごとの分析ができません。ESLint のルールには、セキュリティに関わる重要なものから、コーディングスタイルに関する軽微なものまで様々あります。しかし、コンソール出力だけでは、どのルールの違反が増えているのか、どこから対策すべきなのかが見えにくいのです。

チーム全体での品質意識の共有

コード品質の改善は、特定の開発者だけが取り組むものではなく、チーム全体で意識を共有する必要があります。しかし、ESLint の結果が各開発者のローカル環境や CI ログにしか存在しない場合、以下のような課題があります。

プロジェクトマネージャーやテックリードが、現在のコード品質の状況を把握しづらいことです。「このプロジェクトのコード品質は良好なのか」「リファクタリングの優先度は高いのか」といった判断に必要な情報が不足してしまいます。

また、新しく参加したメンバーが、プロジェクトのコード品質の歴史や現状を理解することも難しくなります。品質改善の取り組みが可視化されていないため、過去の努力や改善の成果が伝わらないのです。

以下の図は、ESLint 運用における課題を整理したものです。

mermaidflowchart LR
    problem1["過去との比較困難"] -->|結果| issue1["改善効果が<br/>見えない"]
    problem2["違反の分類不足"] -->|結果| issue2["優先度が<br/>不明確"]
    problem3["情報の分散"] -->|結果| issue3["チーム共有<br/>できない"]

    issue1 --> goal["継続的な<br/>可視化が必要"]
    issue2 --> goal
    issue3 --> goal

これらの課題を解決するには、ESLint の結果を構造化されたフォーマットで保存し、継続的に分析できる仕組みが必要です。

GitHub Actions での結果保存の限界

GitHub Actions で ESLint を実行している場合、アーティファクトとして結果を保存することは可能です。しかし、この方法にも以下の制限があります。

アーティファクトは一定期間後に自動削除されるため、長期的なトレンド分析には向きません。GitHub の設定にもよりますが、デフォルトでは 90 日後にアーティファクトが削除されてしまうでしょう。

また、アーティファクトをダウンロードして手動で分析する必要があり、手間がかかります。毎週の品質レビューミーティングの前に、誰かがファイルをダウンロードして Excel で集計する、といった運用は持続可能ではありませんね。

さらに、Pull Request との紐付けが弱く、「この PR で何件の違反が増えたのか」を自動的に判定することが困難です。レビュアーは変更内容と ESLint の結果を別々に確認しなければならず、レビューの負担が増加してしまいます。

解決策

SARIF 形式での ESLint 結果出力

SARIF(Static Analysis Results Interchange Format)は、静的解析ツールの結果を標準化された形式で表現するための JSON ベースのフォーマットです。OASIS が策定した仕様で、多くの静的解析ツールがサポートしています。

ESLint で SARIF 形式の出力を行うには、専用のフォーマッターを使用します。@microsoft​/​eslint-formatter-sarif というパッケージが公式に提供されており、これを使うことで簡単に SARIF 形式の結果を生成できるでしょう。

以下の図は、ESLint から SARIF、そして Code Scanning までのデータフローを示しています。

mermaidflowchart LR
    code["ソースコード"] -->|解析| eslint["ESLint"]
    eslint -->|SARIF<br/>フォーマッター| sarif["SARIF ファイル<br/>(JSON)"]
    sarif -->|アップロード| scanning["GitHub<br/>Code Scanning"]
    scanning -->|可視化| ui["Web UI<br/>ダッシュボード"]
    scanning -->|API| analytics["データ分析<br/>ツール"]

SARIF 形式にすることで、GitHub だけでなく、他のツールでも結果を活用できるようになります。

SARIF フォーマッターのインストール

まず、プロジェクトに SARIF フォーマッターをインストールしましょう。

bashyarn add -D @microsoft/eslint-formatter-sarif

このパッケージは開発時のみ使用するため、-D オプションで devDependencies に追加します。インストール後は、package.json に依存関係が追加されているか確認してください。

ESLint の実行と SARIF 出力

次に、ESLint を実行して SARIF 形式で結果を出力するコマンドを作成します。

json{
  "scripts": {
    "lint": "eslint .",
    "lint:sarif": "eslint . --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif"
  }
}

package.json の scripts セクションに上記のように追加することで、通常の lint コマンドとは別に SARIF 出力用のコマンドを用意できます。--format オプションでフォーマッターを指定し、--output-file で出力先のファイル名を指定しますね。

実行すると、プロジェクトのルートディレクトリに eslint-results.sarif という JSON ファイルが生成されます。このファイルには、すべての ESLint 違反が構造化されたデータとして含まれているでしょう。

SARIF ファイルの構造理解

生成された SARIF ファイルの構造を見てみましょう。主要な要素を理解することで、後の活用がスムーズになります。

json{
  "version": "2.1.0",
  "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "ESLint",
          "informationUri": "https://eslint.org",
          "rules": []
        }
      },
      "results": []
    }
  ]
}

SARIF ファイルのルート構造です。version は SARIF 仕様のバージョンを示し、現在は 2.1.0 が最新です。runs 配列には、解析実行の結果が格納されます。

json{
  "tool": {
    "driver": {
      "name": "ESLint",
      "version": "8.57.0",
      "informationUri": "https://eslint.org",
      "rules": [
        {
          "id": "no-unused-vars",
          "shortDescription": {
            "text": "Disallow unused variables"
          },
          "helpUri": "https://eslint.org/docs/rules/no-unused-vars",
          "properties": {
            "category": "Variables"
          }
        }
      ]
    }
  }
}

tool セクションには、使用した静的解析ツール(この場合は ESLint)の情報が含まれます。rules 配列には、適用されたすべてのルールの定義が格納されており、各ルールの説明やドキュメントへのリンクが含まれていますね。

json{
  "results": [
    {
      "ruleId": "no-unused-vars",
      "level": "warning",
      "message": {
        "text": "'unused' is defined but never used."
      },
      "locations": [
        {
          "physicalLocation": {
            "artifactLocation": {
              "uri": "src/utils/helper.ts"
            },
            "region": {
              "startLine": 5,
              "startColumn": 7
            }
          }
        }
      ]
    }
  ]
}

results 配列には、実際に検出された違反が格納されます。各違反には、ルール ID、重要度レベル、メッセージ、そして違反が発生したファイルと行番号が含まれています。この情報により、開発者は正確に問題の場所を特定できるでしょう。

GitHub Actions ワークフローの設定

SARIF ファイルを GitHub Code Scanning にアップロードするための GitHub Actions ワークフローを作成します。プロジェクトの .github​/​workflows ディレクトリに新しいファイルを作成しましょう。

yamlname: ESLint SARIF Analysis

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

ワークフローの基本設定です。maindevelop ブランチへのプッシュ、およびこれらのブランチへの Pull Request で自動実行されます。プロジェクトのブランチ戦略に合わせて調整してください。

yamljobs:
  eslint:
    name: Run ESLint with SARIF
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

ジョブの定義です。permissions セクションで、コードの読み取りとセキュリティイベントの書き込み権限を設定します。Code Scanning に結果をアップロードするには、security-events: write 権限が必須ですね。

yamlsteps:
  - name: Checkout code
    uses: actions/checkout@v4

  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: '20'
      cache: 'yarn'

最初のステップでリポジトリをチェックアウトし、次に Node.js 環境をセットアップします。cache: 'yarn' を指定することで、依存関係のインストールが高速化されるでしょう。

yaml- name: Install dependencies
  run: yarn install --frozen-lockfile

- name: Run ESLint
  run: yarn lint:sarif
  continue-on-error: true

依存関係をインストールした後、先ほど作成した lint:sarif コマンドを実行します。continue-on-error: true を設定することで、ESLint で違反が見つかってもワークフローを続行できます。これにより、違反がある状態でも SARIF ファイルをアップロードできますね。

yaml- name: Upload SARIF file
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: eslint-results.sarif
    category: eslint

最後に、生成された SARIF ファイルを GitHub Code Scanning にアップロードします。github​/​codeql-action​/​upload-sarif アクションを使用し、category パラメータで「eslint」というカテゴリを指定しています。これにより、複数の静的解析ツールの結果を区別できるでしょう。

Code Scanning での結果確認

ワークフローが正常に実行されると、GitHub リポジトリの「Security」タブに Code Scanning の結果が表示されます。確認手順を見ていきましょう。

リポジトリのトップページから「Security」タブをクリックし、左サイドバーの「Code scanning」を選択します。すると、検出されたアラート(ESLint 違反)の一覧が表示されるはずです。

アラート一覧では、以下の情報を確認できます。

#項目説明
1ルール名違反した ESLint ルール(例:no-unused-vars)
2重要度Error、Warning、Note のいずれか
3ファイルパス違反が発生したファイルの場所
4ステータスOpen(未解決)または Closed(解決済み)
5ブランチ違反が検出されたブランチ

各アラートをクリックすると、詳細情報が表示されます。違反が発生したコードの該当行がハイライトされ、ESLint ルールの説明やドキュメントへのリンクも確認できるでしょう。

Pull Request での自動チェック

Code Scanning の最も便利な機能の一つが、Pull Request での自動チェックです。新しい PR を作成すると、その PR で追加された違反が自動的に検出され、コメントとして表示されます。

以下の図は、Pull Request でのチェックフローを示しています。

mermaidsequenceDiagram
    participant Dev as 開発者
    participant PR as Pull Request
    participant GHA as GitHub Actions
    participant CS as Code Scanning

    Dev->>PR: コードをプッシュ
    PR->>GHA: ワークフローをトリガー
    GHA->>GHA: ESLint 実行
    GHA->>CS: SARIF アップロード
    CS->>CS: 前回結果と比較
    CS->>PR: 新規違反を<br/>コメント
    PR->>Dev: レビュー要請

開発者が Pull Request を作成すると、自動的に ESLint が実行され、新たに追加された違反がコメントとして通知されます。

PR のチェック結果では、以下の情報が表示されます。

  • 新たに追加された違反の件数
  • 修正された違反の件数
  • 違反の詳細(ファイル名、行番号、ルール名)

レビュアーはこの情報を見ることで、PR がコード品質に与える影響を即座に把握できます。もし重要な違反が追加されている場合は、マージ前に修正を依頼できるでしょう。

時系列での推移確認

Code Scanning の大きなメリットは、違反の時系列推移を確認できることです。「Trends」タブでは、プロジェクト全体の違反件数の変化をグラフで表示できます。

グラフでは以下の情報を確認できます。

  • 過去 30 日、90 日、1 年間の違反件数推移
  • ルールごとの違反件数
  • ブランチごとの違反件数
  • 重要度別の違反分布

この情報を活用することで、以下のような分析が可能になりますね。

リファクタリングの効果測定:先月実施したリファクタリングで、実際にどれだけ違反が減ったのかを定量的に確認できます。

新ルール導入の影響確認:新しい ESLint ルールを追加した際に、どれくらいの違反が新たに検出されたかを把握できるでしょう。

チーム間の比較:複数のチームやプロジェクトで Code Scanning を使っている場合、コード品質を横断的に比較できます。

具体例

実際のプロジェクトでの導入ステップ

ここでは、実際のプロジェクトに SARIF/Code Scanning を導入する手順を、具体的なコマンドとともに説明します。Next.js + TypeScript のプロジェクトを例に進めていきましょう。

まず、プロジェクトのディレクトリに移動し、必要なパッケージをインストールします。

bashcd your-nextjs-project
yarn add -D @microsoft/eslint-formatter-sarif

インストールが完了したら、package.json が正しく更新されているか確認してください。以下のように devDependencies に追加されているはずです。

json{
  "devDependencies": {
    "@microsoft/eslint-formatter-sarif": "^3.1.0",
    "eslint": "^8.57.0",
    "eslint-config-next": "^14.2.0"
  }
}

次に、package.json の scripts セクションに SARIF 出力用のコマンドを追加します。

json{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:sarif": "next lint --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif || true"
  }
}

|| true を追加することで、ESLint で違反が見つかっても終了コード 0 を返すようにしています。これにより、CI/CD パイプラインでエラーとして扱われることを防げますね。

ローカル環境で動作確認をしてみましょう。

bashyarn lint:sarif

実行すると、プロジェクトルートに eslint-results.sarif ファイルが生成されます。このファイルの内容を確認してみてください。

bashcat eslint-results.sarif | head -n 30

正しく生成されていれば、JSON 形式で ESLint の結果が含まれているはずです。

GitHub Actions ワークフローファイルの作成

次に、GitHub Actions のワークフローファイルを作成します。.github​/​workflows ディレクトリが存在しない場合は作成してください。

bashmkdir -p .github/workflows

新しいワークフローファイルを作成します。

bashtouch .github/workflows/eslint-sarif.yml

以下の内容を eslint-sarif.yml に記述します。実際のプロジェクト環境に合わせて調整が必要な部分もあるでしょう。

yamlname: ESLint Code Scanning

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop
  schedule:
    # 毎週月曜日の午前9時(JST)に定期実行
    - cron: '0 0 * * 1'

jobs:
  eslint-scan:
    name: ESLint SARIF Analysis
    runs-on: ubuntu-latest

    permissions:
      contents: read
      security-events: write
      actions: read

基本的なワークフロー設定です。プッシュと Pull Request に加えて、schedule でも定期実行を設定しています。これにより、開発が停滞している場合でも週次でコード品質を確認できますね。

yamlsteps:
  - name: Checkout repository
    uses: actions/checkout@v4
    with:
      fetch-depth: 0

  - name: Setup Node.js environment
    uses: actions/setup-node@v4
    with:
      node-version: '20'
      cache: 'yarn'

リポジトリのチェックアウトと Node.js 環境のセットアップです。fetch-depth: 0 を指定することで、すべてのコミット履歴を取得します。これは、後で差分分析を行う際に役立つでしょう。

yaml- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: |
      node_modules
      .yarn/cache
    key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
    restore-keys: |
      ${{ runner.os }}-yarn-

- name: Install dependencies
  run: yarn install --frozen-lockfile

依存関係のキャッシュとインストールです。actions​/​cache を使うことで、2 回目以降の実行が大幅に高速化されます。プロジェクトの規模にもよりますが、数分単位で時間短縮できることもあるでしょう。

yaml- name: Run ESLint with SARIF output
  run: yarn lint:sarif
  continue-on-error: true

- name: Check SARIF file exists
  id: sarif-check
  run: |
    if [ -f eslint-results.sarif ]; then
      echo "exists=true" >> $GITHUB_OUTPUT
    else
      echo "exists=false" >> $GITHUB_OUTPUT
      echo "::warning::SARIF file was not generated"
    fi

ESLint を実行し、SARIF ファイルの存在を確認します。ファイルが生成されなかった場合は警告を出力しますが、ワークフローは続行します。この確認ステップにより、後続の処理でエラーが発生することを防げますね。

yaml- name: Upload SARIF to GitHub Code Scanning
  if: steps.sarif-check.outputs.exists == 'true'
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: eslint-results.sarif
    category: eslint
    wait-for-processing: true

SARIF ファイルを GitHub Code Scanning にアップロードします。wait-for-processing: true を設定することで、アップロード処理が完了するまで待機します。これにより、Pull Request のチェックステータスが正しく更新されるでしょう。

yaml- name: Upload SARIF as artifact
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: eslint-sarif-results
    path: eslint-results.sarif
    retention-days: 30

最後に、SARIF ファイルをアーティファクトとしてもアップロードします。これにより、後でファイルをダウンロードして詳細な分析を行うことができますね。retention-days で保存期間を 30 日に設定しています。

ワークフローのコミットとプッシュ

作成したワークフローファイルをリポジトリにコミットしてプッシュします。

bashgit add .github/workflows/eslint-sarif.yml
git add package.json
git commit -m "feat: Add ESLint SARIF Code Scanning workflow"
git push origin main

プッシュすると、自動的にワークフローが実行されます。GitHub リポジトリの「Actions」タブで実行状況を確認できるでしょう。

Code Scanning 結果の確認と活用

ワークフローが成功したら、Code Scanning の結果を確認してみましょう。GitHub リポジトリの「Security」タブから「Code scanning」を選択します。

初回実行では、プロジェクト内のすべての ESLint 違反がアラートとして表示されます。違反が多い場合は、数百件のアラートが表示されることもあるでしょう。驚く必要はありません。まずは現状を把握することが重要です。

アラートはフィルタリング機能を使って整理できます。以下のフィルタが利用可能です。

textis:open              # 未解決のアラートのみ
is:closed            # 解決済みのアラートのみ
severity:error       # エラーレベルのみ
severity:warning     # 警告レベルのみ
rule:no-unused-vars  # 特定のルールのみ
branch:main          # 特定のブランチのみ

例えば、エラーレベルの違反のみを確認したい場合は、検索ボックスに is:open severity:error と入力します。これにより、優先度の高い問題から対処できますね。

違反の解決とクローズ

実際に違反を修正してコミットすると、Code Scanning が自動的に変更を検出し、該当するアラートをクローズします。この仕組みを確認してみましょう。

例えば、以下のような未使用変数の違反があるとします。

typescript// src/utils/formatter.ts
export function formatDate(date: Date): string {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const unused = 'This variable is not used'; // ESLint: no-unused-vars

  return `${year}-${month}-${day}`;
}

この違反を修正します。

typescript// src/utils/formatter.ts
export function formatDate(date: Date): string {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();

  return `${year}-${month}-${day}`;
}

修正をコミットしてプッシュします。

bashgit add src/utils/formatter.ts
git commit -m "fix: Remove unused variable in formatDate"
git push origin main

ワークフローが再実行されると、Code Scanning は新しい SARIF ファイルを前回の結果と比較します。違反が修正されていることを検出すると、該当するアラートを自動的にクローズするでしょう。

Pull Request での活用例

実際の開発フローで最も効果を発揮するのが、Pull Request での活用です。新機能を開発する際の例を見てみましょう。

新しいブランチを作成し、機能を実装します。

bashgit checkout -b feature/user-profile

新しいコンポーネントを作成します。

typescript// src/components/UserProfile.tsx
import React from 'react';

interface UserProfileProps {
  name: string;
  email: string;
  age: number;
}

export const UserProfile: React.FC<UserProfileProps> = ({
  name,
  email,
}) => {
  const greeting = 'Hello'; // ESLint: no-unused-vars

  return (
    <div>
      <h2>{name}</h2>
      <p>{email}</p>
      {/* age プロパティを使用していない: ESLint: no-unused-vars */}
    </div>
  );
};

このコードには、意図的に 2 つの ESLint 違反を含めています。変更をコミットして Pull Request を作成しましょう。

bashgit add src/components/UserProfile.tsx
git commit -m "feat: Add UserProfile component"
git push origin feature/user-profile

GitHub で Pull Request を作成すると、数分後に Code Scanning のチェックが実行されます。PR のチェック欄に「Code scanning results / eslint」という項目が表示され、新たに追加された違反が報告されるでしょう。

PR の「Files changed」タブでは、違反が発生している行に直接アノテーションが表示されます。レビュアーはコードレビューと同時に、コード品質の問題も確認できるため、レビューの効率が向上しますね。

違反を修正してプッシュすると、PR のチェックステータスが自動的に更新されます。

typescript// src/components/UserProfile.tsx(修正版)
import React from 'react';

interface UserProfileProps {
  name: string;
  email: string;
  age: number;
}

export const UserProfile: React.FC<UserProfileProps> = ({
  name,
  email,
  age,
}) => {
  return (
    <div>
      <h2>{name}</h2>
      <p>{email}</p>
      <p>Age: {age}</p>
    </div>
  );
};

修正をプッシュします。

bashgit add src/components/UserProfile.tsx
git commit -m "fix: Use all props in UserProfile component"
git push origin feature/user-profile

ワークフローが再実行され、違反が解決されたことが確認されると、PR のチェックがパスします。これで安心してマージできるでしょう。

カスタムルールセットの適用

プロジェクトの要件に応じて、特定のルールセットを Code Scanning でも追跡したい場合があります。例えば、セキュリティ関連のルールのみを重点的に監視する設定を見てみましょう。

まず、セキュリティ関連のルールを集めた ESLint 設定ファイルを作成します。

javascript// .eslintrc.security.js
module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:security/recommended',
  ],
  plugins: ['security'],
  rules: {
    'security/detect-object-injection': 'error',
    'security/detect-non-literal-regexp': 'error',
    'security/detect-unsafe-regex': 'error',
    'security/detect-buffer-noassert': 'error',
    'security/detect-eval-with-expression': 'error',
    'security/detect-no-csrf-before-method-override':
      'error',
  },
};

セキュリティに特化した ESLint 設定です。eslint-plugin-security を使用しているため、事前にインストールが必要です。

bashyarn add -D eslint-plugin-security

次に、セキュリティスキャン専用のスクリプトを追加します。

json{
  "scripts": {
    "lint": "next lint",
    "lint:sarif": "next lint --format @microsoft/eslint-formatter-sarif --output-file eslint-results.sarif || true",
    "lint:security": "eslint . --config .eslintrc.security.js --format @microsoft/eslint-formatter-sarif --output-file eslint-security.sarif || true"
  }
}

ワークフローファイルに、セキュリティスキャン用のジョブを追加します。

yamljobs:
  eslint-scan:
    # 既存の通常スキャン
    # ...

  eslint-security-scan:
    name: ESLint Security Analysis
    runs-on: ubuntu-latest

    permissions:
      contents: read
      security-events: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Run security-focused ESLint
        run: yarn lint:security
        continue-on-error: true

      - name: Upload security SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: eslint-security.sarif
          category: eslint-security

セキュリティスキャン専用のジョブです。categoryeslint-security とすることで、通常の ESLint スキャンと区別できます。Code Scanning の画面では、カテゴリごとにアラートをフィルタリングできるため、セキュリティに特化した問題を素早く確認できるでしょう。

違反件数のメトリクス取得

Code Scanning の API を使用すれば、違反件数をプログラマティックに取得し、他のダッシュボードツールと連携できます。簡単なスクリプトを作成してみましょう。

typescript// scripts/get-code-scanning-metrics.ts
import { Octokit } from '@octokit/rest';

interface ScanningAlert {
  number: number;
  rule: {
    id: string;
    severity: string;
  };
  state: string;
  created_at: string;
}

interface Metrics {
  total: number;
  open: number;
  closed: number;
  bySeverity: {
    error: number;
    warning: number;
    note: number;
  };
  byRule: Record<string, number>;
}

型定義です。Code Scanning API から返されるアラート情報と、集計後のメトリクスの型を定義しています。

typescriptasync function getCodeScanningMetrics(
  owner: string,
  repo: string,
  token: string
): Promise<Metrics> {
  const octokit = new Octokit({ auth: token });

  // Code Scanning アラートを取得
  const { data: alerts } = await octokit.codeScanning.listAlertsForRepo({
    owner,
    repo,
    tool_name: 'ESLint',
    per_page: 100,
  });

GitHub API を使って Code Scanning のアラートを取得します。tool_name で ESLint のアラートのみをフィルタリングしています。

typescript  // メトリクスを集計
  const metrics: Metrics = {
    total: alerts.length,
    open: 0,
    closed: 0,
    bySeverity: {
      error: 0,
      warning: 0,
      note: 0,
    },
    byRule: {},
  };

  for (const alert of alerts as ScanningAlert[]) {
    // ステータスごとにカウント
    if (alert.state === 'open') {
      metrics.open++;
    } else {
      metrics.closed++;
    }

    // 重要度ごとにカウント
    const severity = alert.rule.severity.toLowerCase() as keyof typeof metrics.bySeverity;
    if (severity in metrics.bySeverity) {
      metrics.bySeverity[severity]++;
    }

    // ルールごとにカウント
    const ruleId = alert.rule.id;
    metrics.byRule[ruleId] = (metrics.byRule[ruleId] || 0) + 1;
  }

  return metrics;
}

取得したアラートをループ処理し、ステータス、重要度、ルールごとに集計します。この情報をダッシュボードに表示したり、Slack に通知したりできるでしょう。

typescript// 使用例
async function main() {
  const owner = 'your-org';
  const repo = 'your-repo';
  const token = process.env.GITHUB_TOKEN || '';

  const metrics = await getCodeScanningMetrics(
    owner,
    repo,
    token
  );

  console.log('=== ESLint Code Scanning Metrics ===');
  console.log(`Total alerts: ${metrics.total}`);
  console.log(`Open: ${metrics.open}`);
  console.log(`Closed: ${metrics.closed}`);
  console.log('\nBy Severity:');
  console.log(`  Error: ${metrics.bySeverity.error}`);
  console.log(`  Warning: ${metrics.bySeverity.warning}`);
  console.log(`  Note: ${metrics.bySeverity.note}`);
  console.log('\nTop 5 Rules:');

  const topRules = Object.entries(metrics.byRule)
    .sort(([, a], [, b]) => b - a)
    .slice(0, 5);

  for (const [rule, count] of topRules) {
    console.log(`  ${rule}: ${count}`);
  }
}

main().catch(console.error);

スクリプトの実行例です。環境変数 GITHUB_TOKEN に GitHub のパーソナルアクセストークンを設定して実行すると、現在のメトリクスがコンソールに出力されます。

このスクリプトを定期実行するように設定すれば、週次レポートの作成や、Slack への自動通知などに活用できるでしょう。

Slack 通知との連携

Code Scanning の結果を Slack に通知する仕組みを作ってみましょう。GitHub Actions のワークフローに通知ステップを追加します。

yaml- name: Get alert summary
  id: alert-summary
  if: always()
  run: |
    # SARIF ファイルから違反件数を集計
    if [ -f eslint-results.sarif ]; then
      total=$(jq '.runs[0].results | length' eslint-results.sarif)
      errors=$(jq '[.runs[0].results[] | select(.level == "error")] | length' eslint-results.sarif)
      warnings=$(jq '[.runs[0].results[] | select(.level == "warning")] | length' eslint-results.sarif)

      echo "total=$total" >> $GITHUB_OUTPUT
      echo "errors=$errors" >> $GITHUB_OUTPUT
      echo "warnings=$warnings" >> $GITHUB_OUTPUT
    fi

SARIF ファイルから違反件数を集計するステップです。jq コマンドを使用して JSON をパースし、レベルごとの件数を計算しています。

yaml- name: Notify Slack
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "ESLint Code Scanning Results",
        "blocks": [
          {
            "type": "header",
            "text": {
              "type": "plain_text",
              "text": "📊 ESLint Scanning Complete"
            }
          },
          {
            "type": "section",
            "fields": [
              {
                "type": "mrkdwn",
                "text": "*Repository:*\n${{ github.repository }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Branch:*\n${{ github.ref_name }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Total Issues:*\n${{ steps.alert-summary.outputs.total }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Errors:*\n${{ steps.alert-summary.outputs.errors }}"
              },
              {
                "type": "mrkdwn",
                "text": "*Warnings:*\n${{ steps.alert-summary.outputs.warnings }}"
              }
            ]
          },
          {
            "type": "actions",
            "elements": [
              {
                "type": "button",
                "text": {
                  "type": "plain_text",
                  "text": "View Details"
                },
                "url": "${{ github.server_url }}/${{ github.repository }}/security/code-scanning"
              }
            ]
          }
        ]
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Slack への通知ステップです。main ブランチへのプッシュ時のみ通知されます。Slack Webhook URL はリポジトリのシークレットに設定しておく必要がありますね。

通知メッセージには、リポジトリ名、ブランチ名、違反件数、そして Code Scanning の詳細ページへのリンクが含まれます。チームメンバーは Slack から直接 GitHub の詳細ページにアクセスでき、素早く状況を確認できるでしょう。

まとめ

ESLint の実行結果を SARIF 形式で出力し、GitHub Code Scanning にアップロードすることで、コード品質の継続的な可視化が実現できました。

従来のコンソール出力だけでは把握できなかった違反の時系列推移や、Pull Request での自動チェックにより、チーム全体でコード品質を意識する文化が醸成されるでしょう。Code Scanning のダッシュボードは、マネージャーやテックリードにとっても、プロジェクトの健全性を一目で確認できる強力なツールとなります。

導入のハードルは決して高くありません。SARIF フォーマッターのインストールと GitHub Actions ワークフローの設定だけで、すぐに運用を開始できます。本記事で紹介した具体例を参考に、ぜひあなたのプロジェクトでも SARIF/Code Scanning を活用してみてください。

図解を含めた記事構成により、初心者の方でも理解しやすい内容になったかと思います。Code Scanning の Trends 機能を使えば、コード品質改善の成果を可視化し、チームのモチベーション向上にもつながるでしょう。

継続的なコード品質管理は、技術的負債の蓄積を防ぎ、長期的なプロジェクトの成功に欠かせません。SARIF と Code Scanning を活用して、より良いコード品質を目指していきましょう。

関連リンク