mcpField
S
sonarqube-quality-gate-playbook

sonarqube-quality-gate-playbook

github.com/agustinalbonicoテスト・品質052 回インストール7 週間前に更新
リポジトリ

sonarqube-quality-gate-playbook について

モノレポのテスト、カバレッジ、不具合、セキュリティ課題を管理するチーム向けの SonarQube 品質ゲート手順書です。小さな単位で優先度順に対応し、スキャン結果とカバレッジの証拠で進捗を確認します。

主な機能

  1. 01新規コードの問題を優先
  2. 02lcov のパス不整合を診断・修正
  3. 03テストとカバレッジの方針策定
  4. 04不具合・セキュリティ課題・コードスメルを段階的に修正
  5. 05ゲート状態と証拠で完了を確認

活用シーン

  1. 01SonarQube とローカルのカバレッジ差を調査
  2. 02動作を検証するバックエンド・フロントエンドテストの追加
  3. 03CI の品質ゲートを追跡し結果を報告

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/agustinalbonico/ai-customizations --skill sonarqube-quality-gate-playbook

インストール後、Claude Code で/sonarqube-quality-gate-playbookを入力して読み込まれたか確認します。

詳細

SonarQube Quality Gate プレイブック

影響が実際にあり、リスクの低い作業を優先し、このフローを小さく検証可能な単位で繰り返し適用します。

必須入力

最初のサイクルを始める前に、次の入力を定義します。

  • SONAR_HOST_URL(例:http://127.0.0.1:9000)
  • 分析権限を持つ SONAR_TOKEN
  • SONAR_PROJECT_KEY と、任意で SONAR_PROJECT_NAME
  • SONAR_NEW_CODE_REFERENCE_BRANCH(デフォルト:main)
  • モノレポのパス:apps/backend、apps/frontend、packages/*
  • 各アプリ(backend/frontend)のカバレッジ付きテストコマンド
  • 各アプリの lcov.info レポートのパス
  • Sonar 設定ファイル(sonar-project.properties)または CI の同等のパラメーター
  • カバレッジと分析の除外パターン
  • Quality Gate のしきい値(カバレッジ、バグ、脆弱性、コードスメル、重複)
  • 破損させてはならないビルド/パイプラインコマンド

優先順位付けの戦略

次の順序で作業します。

  1. まず New Code
  2. 新しいバグと脆弱性
  3. TO_REVIEW 状態の Security Hotspots
  4. 重要なモジュールと PR で変更されたモジュールのカバレッジ
  5. 新しい Code Smells(重大度の高いものを優先)
  6. 活発な領域の重複と技術的負債
  7. 過去からの負債(スプリントの目標に影響しない場合のみ)
影響度のヒューリスティック

各 Issue またはファイルについて、次のように簡単に優先度を計算します。

priorityScore = (isNewCode*100) + (isBugOrVuln*80) + (isHotspotToReview*70) + severityWeight + (criticalPath*20) + (coverageGap*10) - (estimatedEffort*5)

severityWeight の値は、blocker 40、critical 30、major 20、minor 10 とします。

カバレッジの水増しを防ぐルール

動作を検証せず、パーセンテージを上げるだけのテストは認めません。

  • 新しいテストでは、正常系、異常系、境界条件をすべてカバーする必要があります
  • スナップショットだけを唯一のアサーションにすることは禁止です
  • ロジックのないコードに対する些細なテストは避けます
  • 実際のバグがあっても失敗しないテストは、価値あるカバレッジとは見なしません

スキルのパイプライン(ステップごと)

カバレッジの不一致に関するランブック(再利用可能なケース)

SonarQube のカバレッジがローカルより大幅に低い場合は、このランブックを使用します。

典型的な症状
  • ローカル(Jest/Vitest):カバレッジが高い(例:>90%)
  • SonarQube:カバレッジが大幅に低い(例:40~60%)
よくある根本原因
  1. Windows で生成された lcov.info の SF: パスに、バックスラッシュ(\\)が含まれている。
  2. Linux/コンテナ上で実行される SonarQube が、\\ を含むパスをマッピングできない。
  3. 共有パッケージのカバレッジが低い(例:packages/utils)ため、全体の値が引き下げられている。
簡易診断
  1. 各アプリ/パッケージの lcov.info で、先頭にある SF: エントリーを確認します。
  2. sonar-scanner のログで、マッピングできなかった、または無視されたカバレッジファイルがないか確認します。
  3. モジュール(backend/frontend/packages)ごとのカバレッジを比較し、外れ値を特定します。

推奨コマンド:

Get-Content "apps\\backend\\coverage\\lcov.info" | Select-String "^SF:" | Select-Object -First 10
Get-Content "apps\\frontend\\coverage\\lcov.info" | Select-String "^SF:" | Select-Object -First 10

SF:src\\app.ts のようなパスがある場合は、スキャンの前に正規化します。

推奨する修正方法
  1. sonar-scanner の前に、すべての lcov.info のパスをフォワードスラッシュ(/)に正規化します。
  2. カバレッジ付きテストとスキャンを再実行します。
  3. 全体の値が偏らないよう、カバレッジ率が最も低いパッケージ/モジュールのカバレッジを上げます。

ポータブルな例(scripts/fix-lcov-paths.js):

const fs = require('node:fs');

const reports = [
  'apps/backend/coverage/lcov.info',
  'apps/frontend/coverage/lcov.info',
  'packages/utils/coverage/lcov.info'
];

for (const reportPath of reports) {
  if (!fs.existsSync(reportPath)) continue;
  const content = fs.readFileSync(reportPath, 'utf8');
  const normalized = content.replace(/\\\\/g, '/');
  fs.writeFileSync(reportPath, normalized);
}

CI のガードレール(バックスラッシュが残っている場合は失敗させる):

if grep -q 'SF:.*\\\\' apps/backend/coverage/lcov.info; then
  echo "ERROR: Backslashes encontrados en lcov.info"
  exit 1
fi
ステップ 1:現在のギャップを検出する
  1. backend/frontend と、関連する packages でカバレッジ付きテストを実行します
  2. SonarQube 分析を実行します
  3. メトリクスと新しい Issue を収集し、優先順位を付けます

一般的なコマンド:

bun run --cwd apps/backend test -- --coverage
bun run --cwd apps/frontend test -- --coverage

sonar-scanner `
  -Dsonar.host.url=$env:SONAR_HOST_URL `
  -Dsonar.token=$env:SONAR_TOKEN `
  -Dsonar.projectKey=$env:SONAR_PROJECT_KEY `
  -Dsonar.qualitygate.wait=true

curl -4 -s "$env:SONAR_HOST_URL/api/measures/component?component=$env:SONAR_PROJECT_KEY&metricKeys=coverage,new_coverage,bugs,new_bugs,vulnerabilities,new_vulnerabilities,code_smells,new_code_smells,duplicated_lines_density,new_duplicated_lines_density" -u "$env:SONAR_TOKEN:"

curl -4 -s "$env:SONAR_HOST_URL/api/issues/search?componentKeys=$env:SONAR_PROJECT_KEY&resolved=false&inNewCodePeriod=true&types=BUG,VULNERABILITY,CODE_SMELL&ps=500" -u "$env:SONAR_TOKEN:"

curl -4 -s "$env:SONAR_HOST_URL/api/hotspots/search?projectKey=$env:SONAR_PROJECT_KEY&status=TO_REVIEW&ps=500" -u "$env:SONAR_TOKEN:"
ステップ 2:Sonar 設定を作成または更新する

モノレポの実際のパスを使って、ルートに sonar-project.properties を作成または更新します。

ステップ 3:実際のカバレッジを確実に読み込む
  • lcov.info が存在することを確認します
  • すべてのパスを含めて sonar.javascript.lcov.reportPaths を設定します
  • スキャナーのログに、カバレッジが見つからないことを示す警告がないことを確認します
  • SF: 行で / が使われ、\\ が使われていないことを確認します
  • Windows/Linux 混在環境の場合は、スキャン前にパスの正規化を実行します
  • 関連する共有パッケージでもカバレッジがレポートされていることを確認します(アプリだけでなく)
ステップ 4:テストを一貫して実行する
  • ローカルと CI で同じコマンドとフラグを使用する
  • カバレッジの成果物を公開する
  • 環境間でランナーやフラグを混在させない
ステップ 5:小さなバッチで反復する
  • バッチ A:新規のバグ/脆弱性/ホットスポット
  • バッチ B:New Code のカバレッジ
  • バッチ C:新規の Code Smells
  • バッチ D:変更箇所の重複/技術的負債

ガイド付きテスト生成(バックエンドとフロントエンド)

1)カバレッジのないファイルを特定する

  • 主な情報源:SonarQube(New Code で未カバー行があるファイル)
  • 副次的な情報源:lcov.info と PR で変更されたファイル
git diff --name-only origin/main...HEAD | Where-Object { $_ -match '\.(ts|tsx)$' }

2)適切なテストタイプを選ぶ

  • Unit:純粋なロジックと依存関係の少ない関数
  • Integration(バックエンド):コントローラー+サービス+モック化したリポジトリ
  • Component(フロントエンド):インタラクション、状態、アクセシビリティ
  • E2E:クリティカルなフローのみ。Unit/Component の代替として使用しない

3)モック戦略

  • DB:フェイクリポジトリまたは隔離されたテスト用データベース
  • 外部 HTTP:決定論的なモック(実際のサービスへのリクエストは行わない)
  • 時刻/乱数:時計と乱数値を制御する
  • 実際の欠陥を隠す過剰なモックは避ける

4)テストの最低限の品質基準

  • Arrange/Act/Assert の構造が明確
  • 想定される動作を意味的にアサートする
  • 正常系+異常系+境界値のケースを必須とする
  • テストが安定しており、フレーキーでない
  • 動作に基づいた名前を付ける

5)命名と構成

  • バックエンド:*.spec.ts
  • フロントエンド:*.test.tsx
  • 推奨パターン:
    • should <resultado> when <condicion>
    • returns <error> when <input invalido>

Code Smells、バグ、セキュリティの修正

大規模なスタイル変更は行わず、影響範囲を絞った修正を適用します。

カテゴリ別チェックリスト:

  • 複雑さ:関数を抽出し、early return を使用して、ネストを減らす
  • 重複:ユーティリティを統合し、コピー&ペーストを避ける
  • Nullability:明示的なガードと安全なデフォルト値を設定する
  • エラー処理:空の catch を避け、コンテキストを付けて伝播させる
  • リーク/リソース:タイマー、サブスクリプション、接続を閉じる
  • Regex/DoS:壊滅的なパターンを避け、入力を制限する
  • 安全でない依存関係:脆弱なライブラリを更新する
  • サニタイズ:入力を検証・サニタイズし、XSS/インジェクションを避ける
  • Security Hotspots:各項目の解決内容を確認し、記録する

除外ルールとトレードオフ

許容される除外(理由を記載すること):

  • ロジックのない **/index.ts
  • 分岐のない単純な DTO または型
  • 生成コード(**/generated/**)
  • ビジネスロジックのない最小限のブートストラップ

許容されない除外:

  • ロジックを含むサービス/ユースケース/コントローラー
  • 分岐を含むフックやバリデーター
  • 認証/認可コード
  • 品質を改善せず、割合を達成するためだけの除外

ルール:各除外について、技術的な理由を記録する必要があります。

完了の定義とメトリクス

証拠がある場合に限り、サイクルの完了を宣言します。

  • Quality Gate の状態が PASSED
  • new_coverage >= 80
  • new_bugs = 0
  • new_vulnerabilities = 0
  • new_code_smells = 0
  • new_security_hotspots_reviewed = 100%
  • 重複と技術的負債がゲートのしきい値内
  • ビルドとパイプラインが成功

添付が必要な最低限の証拠:

  1. sonar.qualitygate.wait=true を指定した sonar-scanner の出力
  2. メトリクス/イシュー/ホットスポット API の出力
  3. フォルダー別のカバレッジ(backend/frontend/packages)
  4. 解決したイシューの概要(修正前/修正後)

設定スニペット

sonar-project.properties
sonar.projectKey=my-org_my-monorepo
sonar.projectName=my-monorepo
sonar.sourceEncoding=UTF-8

sonar.sources=apps/backend/src,apps/frontend/src,packages
sonar.tests=apps/backend,apps/frontend,packages
sonar.test.inclusions=**/*.spec.ts,**/*.test.ts,**/*.test.tsx,**/*.spec.tsx

sonar.javascript.lcov.reportPaths=apps/backend/coverage/lcov.info,apps/frontend/coverage/lcov.info
sonar.newCode.referenceBranch=main

sonar.exclusions=**/dist/**,**/build/**,**/node_modules/**,**/*.d.ts
sonar.coverage.exclusions=**/index.ts,**/*.dto.ts,**/generated/**,**/*.stories.tsx
Jest backend(例)
import type { Config } from 'jest';

const config: Config = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  collectCoverage: true,
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html'],
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.dto.ts',
    '!src/**/index.ts',
    '!src/**/generated/**'
  ]
};

export default config;
Vitest frontend(例)
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'jsdom',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov', 'html'],
      reportsDirectory: './coverage',
      include: ['src/**/*.{ts,tsx}'],
      exclude: ['src/**/*.stories.tsx', 'src/**/index.ts']
    }
  }
});
GitHub Actions(例)
name: quality-gate

on:
  pull_request:
  push:
    branches: [main]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: oven-sh/setup-bun@v2
      - run: bun install --frozen-lockfile

      - name: Backend tests with coverage
        run: bun run --cwd apps/backend test -- --coverage

      - name: Frontend tests with coverage
        run: bun run --cwd apps/frontend test -- --coverage

      - name: Normalize lcov paths (Windows/Linux safe)
        run: node scripts/fix-lcov-paths.js

      - name: Validate lcov path format
        run: |
          if grep -q 'SF:.*\\\\' apps/backend/coverage/lcov.info; then
            echo "ERROR: Backslashes encontrados en backend lcov.info"
            exit 1
          fi

      - name: Build
        run: bun run build

      - name: SonarQube Scan
        env:
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: |
          sonar-scanner \
            -Dsonar.host.url=$SONAR_HOST_URL \
            -Dsonar.token=$SONAR_TOKEN \
            -Dsonar.qualitygate.wait=true
GitLab CI(例)
stages:
  - test
  - quality

variables:
  GIT_DEPTH: "0"

test_and_build:
  stage: test
  image: oven/bun:1
  script:
    - bun install --frozen-lockfile
    - bun run --cwd apps/backend test -- --coverage
    - bun run --cwd apps/frontend test -- --coverage
    - node scripts/fix-lcov-paths.js
    - bun run build
  artifacts:
    when: always
    paths:
      - apps/backend/coverage/
      - apps/frontend/coverage/

sonarqube:
  stage: quality
  image: sonarsource/sonar-scanner-cli:latest
  dependencies:
    - test_and_build
  script:
    - sonar-scanner -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.token=$SONAR_TOKEN -Dsonar.qualitygate.wait=true
  allow_failure: false

スキルのREADME

実行時には次の簡易サマリーを使用してください。

  1. カバレッジ付きでテストを実行する
  2. ゲートの完了を待って sonar-scanner を実行する
  3. New Code -> Bugs/Vulns -> Hotspots -> Coverage -> Smells の順に優先する
  4. 小さな単位で反復し、証拠を添付する

基本コマンド:

  • bun run --cwd apps/backend test -- --coverage
  • bun run --cwd apps/frontend test -- --coverage
  • sonar-scanner -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.token=$SONAR_TOKEN -Dsonar.qualitygate.wait=true

PR チェックリスト

  • 新たな Bugs や Vulnerabilities がない
  • 新たな Code Smells がない
  • New Coverage >= 80%
  • 新たな Hotspots をレビュー済み
  • 正当な理由のない除外がない
  • 正常系、異常系、境界値のテストがある
  • ビルドと CI が成功している
  • Sonar の証拠を添付している
  • lcov.info の SF: 行にバックスラッシュがない
  • 共有 Packages のカバレッジに大きな不足がない

すぐに貼り付けて使えるコマンドのテンプレート

$env:SONAR_HOST_URL="http://127.0.0.1:9000"
$env:SONAR_TOKEN="<token>"
$env:SONAR_PROJECT_KEY="<project-key>"

bun run --cwd apps/backend test -- --coverage
bun run --cwd apps/frontend test -- --coverage
bun run build

sonar-scanner `
  -Dsonar.host.url=$env:SONAR_HOST_URL `
  -Dsonar.token=$env:SONAR_TOKEN `
  -Dsonar.projectKey=$env:SONAR_PROJECT_KEY `
  -Dsonar.qualitygate.wait=true

curl -4 -s "$env:SONAR_HOST_URL/api/measures/component?component=$env:SONAR_PROJECT_KEY&metricKeys=new_coverage,new_bugs,new_vulnerabilities,new_code_smells,new_duplicated_lines_density" -u "$env:SONAR_TOKEN:"

不明点がある場合に調整するデフォルト値

  • New Code のブランチ:main
  • New Coverage のしきい値:80
  • new code の最大重複率:3%
  • coverage exclusions:ロジックのないファイルのみ
  • バッチ戦略:重要なカテゴリごとに PR を 1 件

コメント