Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 55 additions & 15 deletions .claude/commands/repo-maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,42 @@ fi
- 🔧 scheduled-maintenance.yml を配置しました
- ⚠️ `CLAUDE_CODE_OAUTH_TOKEN` シークレットの設定が必要です

### 3.5.0.2 Quality Gate Fallback Consistency Check

ブランチ保護で `Quality Gate` を必須チェックに登録している場合、対応する fallback ワークフローが配置されているか確認する。

**背景:**
`setup-team-protection.sh` はブランチ保護に `Quality Gate` を必須として登録するが、ci.yml が `paths` フィルタでスキップされたり、ci.yml 自体が無い / Dependabot や `claude[bot]` の GITHUB_TOKEN による push でワークフローが発火しなかった場合、`Quality Gate` チェックが報告されず、PR が `Expected — Waiting for status to be reported` のまま blocked になる。
この状態を 3 時間ごとの n8n `resolveGitConflict` 監視が「CI 失敗」として誤検出し、`@claude PRがbaseにマージする際にCIが落ちています` を誤投稿する原因となっていた。

**確認ロジック:**

```bash
REQUIRED_CHECKS=""
if gh api "repos/{owner}/{repo}/branches/main/protection/required_status_checks" >/dev/null 2>&1; then
REQUIRED_CHECKS=$(gh api "repos/{owner}/{repo}/branches/main/protection/required_status_checks" \
--jq '.contexts // [] | join(",")' 2>/dev/null || echo "")
fi
Comment on lines +982 to +985

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the file
find . -name "repo-maintenance.md" -type f

Repository: keito4/config

Length of output: 96


🏁 Script executed:

# Once found, read the relevant section around lines 982-985
head -985 .claude/commands/repo-maintenance.md | tail -20

Repository: keito4/config

Length of output: 1008


🏁 Script executed:

# Search for references to protected branches throughout the file
rg "main|master|pre-production|production" .claude/commands/repo-maintenance.md -n | head -30

Repository: keito4/config

Length of output: 2593


🏁 Script executed:

# Check the PR objective section
rg -A 5 -B 2 "Objective|objective|PR objective" .claude/commands/repo-maintenance.md | head -50

Repository: keito4/config

Length of output: 39


🏁 Script executed:

# Look for the fallback workflow section
rg -A 10 "quality-gate-fallback|fallback.*workflow" .claude/commands/repo-maintenance.md | head -50

Repository: keito4/config

Length of output: 2308


🏁 Script executed:

# Check if there's any other logic handling multiple branches for Quality Gate
rg "Quality Gate|required_status_checks" .claude/commands/repo-maintenance.md -B 3 -A 3

Repository: keito4/config

Length of output: 5022


🏁 Script executed:

# Verify the logic flow - check what happens after REQUIRED_CHECKS is set
sed -n '982,1010p' .claude/commands/repo-maintenance.md

Repository: keito4/config

Length of output: 1259


🏁 Script executed:

# Check if default branch is used anywhere else in the script
rg "defaultBranchRef|default.*branch|DEFAULT_BRANCH" .claude/commands/repo-maintenance.md

Repository: keito4/config

Length of output: 39


Hardcoding main creates inconsistency with multi-branch protection logic.

The Quality Gate check only inspects branches/main, but the script's PROTECT_BRANCHES logic supports main, pre-production, and production for Next.js projects. When Quality Gate is registered as required on pre-production or production, the hardcoded check misses them, incorrectly determining no fallback is needed and allowing the PR to block on "Expected — Waiting for status to be reported."

Dynamic branch detection is needed to align with the protection scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/commands/repo-maintenance.md around lines 982 - 985, The check
hardcodes "branches/main" so REQUIRED_CHECKS misses protections on other
branches; update the logic that calls gh api
"repos/{owner}/{repo}/branches/main/protection/required_status_checks" to
iterate the branch names in the existing PROTECT_BRANCHES (or otherwise-detected
branch list used for Next.js: main, pre-production, production) and aggregate
required contexts into REQUIRED_CHECKS; for each branch name call gh api
".../branches/{branch}/protection/required_status_checks" (or skip if API 404),
join/merge the .contexts results and fallback to empty if none so the subsequent
Quality Gate logic correctly detects required checks across all protected
branches.


NEEDS_FALLBACK=false
if echo "$REQUIRED_CHECKS" | grep -q "Quality Gate"; then
# quality-gate-fallback.yml が無い場合は配置を提案
if [ ! -f ".github/workflows/quality-gate-fallback.yml" ]; then
NEEDS_FALLBACK=true
fi
fi
```

**結果パターン:**

| 状態 | 対応 |
| ------------------------------------- | ---------------------------------------------------- |
| Quality Gate 必須 + fallback 配置済み | ✅ スキップ |
| Quality Gate 必須 + fallback 未配置 | ⚠️ → full mode で `quality-gate-fallback.yml` を配置 |
| Quality Gate 非必須 | ⏭️ スキップ |

`MODE` が `full` かつ未配置の場合は section 3.22 のマネージドファイル同期で自動配置される(`templates/workflows/quality-gate-fallback.yml`)。

### 3.5.1 CI Workflow Template Sync Check

`templates/workflows/` のテンプレートと `.github/workflows/` の実ファイルを比較し、乖離を検出:
Expand Down Expand Up @@ -2242,21 +2278,24 @@ fi

**同期対象ファイルの分類:**

| カテゴリ | ファイル | 同期ポリシー |
| ------------ | ------------------------------------------- | ---------------------------- |
| マネージド | `.github/workflows/claude.yml` | 常に config の最新版で上書き |
| マネージド | `.github/workflows/claude-code-review.yml` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/block_git_no_verify.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/pre_git_quality_gates.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/post_git_push_ci.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/post_commit_adr_reminder.py` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/development-standards.md` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/git-conventions.md` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/release-types.md` | 常に config の最新版で上書き |
| テンプレート | `.github/workflows/security.yml` | 差分表示 → 確認後に上書き |
| テンプレート | `.github/workflows/ci.yml` | 差分表示 → 確認後に上書き |
| テンプレート | `.github/ISSUE_TEMPLATE/*` | 欠落ファイルのみ追加 |
| テンプレート | `.github/pull_request_template.md` | 欠落時のみ追加 |
| カテゴリ | ファイル | 同期ポリシー |
| ------------ | --------------------------------------------- | ---------------------------- |
| マネージド | `.github/workflows/claude.yml` | 常に config の最新版で上書き |
| マネージド | `.github/workflows/claude-code-review.yml` | 常に config の最新版で上書き |
| マネージド | `.github/workflows/quality-gate-fallback.yml` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/block_git_no_verify.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/pre_git_quality_gates.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/post_git_push_ci.py` | 常に config の最新版で上書き |
| マネージド | `.claude/hooks/post_commit_adr_reminder.py` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/development-standards.md` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/git-conventions.md` | 常に config の最新版で上書き |
| マネージド | `.claude/rules/release-types.md` | 常に config の最新版で上書き |
| テンプレート | `.github/workflows/security.yml` | 差分表示 → 確認後に上書き |
| テンプレート | `.github/workflows/ci.yml` | 差分表示 → 確認後に上書き |
| テンプレート | `.github/ISSUE_TEMPLATE/*` | 欠落ファイルのみ追加 |
| テンプレート | `.github/pull_request_template.md` | 欠落時のみ追加 |

`quality-gate-fallback.yml` は `setup-team-protection.sh` がブランチ保護に登録する `Quality Gate` 必須チェックとセットで配布する。ci.yml が paths フィルタ等でスキップされた場合に PR が `Expected — Waiting for status to be reported` のまま blocked にならないよう、Pass を emit する役割を持つ。

**マネージドファイル**: config リポジトリが正規のソースであり、プロジェクト側でカスタマイズしない前提のファイル。
**テンプレートファイル**: プロジェクト固有のカスタマイズが入る可能性があるため、差分確認を挟む。
Expand Down Expand Up @@ -2294,6 +2333,7 @@ fi
MANAGED_FILES=(
".github/workflows/claude.yml"
".github/workflows/claude-code-review.yml"
".github/workflows/quality-gate-fallback.yml"
".claude/hooks/block_git_no_verify.py"
".claude/hooks/pre_git_quality_gates.py"
".claude/hooks/post_git_push_ci.py"
Expand Down
40 changes: 23 additions & 17 deletions .github/workflows/quality-gate-fallback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:

permissions:
contents: read
actions: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-fallback
Expand All @@ -28,26 +29,31 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
head_sha: context.sha,
per_page: 20,
});
try {
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
head_sha: context.sha,
per_page: 20,
});

const ciRun = runs.workflow_runs.find(
r => r.name === 'CI' && r.id !== context.runId
);
const ciRun = runs.workflow_runs.find(
r => r.name === 'CI' && r.id !== context.runId
);

if (ciRun && ciRun.status !== 'completed') {
core.info(`CI workflow is running (${ciRun.html_url}), this fallback is not needed.`);
if (ciRun && ciRun.status !== 'completed') {
core.info(`CI workflow is running (${ciRun.html_url}), this fallback is not needed.`);
core.setOutput('ci_running', 'true');
} else if (ciRun && ciRun.conclusion === 'success') {
core.info(`CI workflow already succeeded (${ciRun.html_url}).`);
core.setOutput('ci_running', 'true');
} else {
core.info('CI workflow did not run for this commit. Providing fallback Quality Gate.');
core.setOutput('ci_running', 'false');
}
} catch (err) {
core.warning(`Could not query workflow runs (${err.status || err.message}). Skipping fallback.`);
core.setOutput('ci_running', 'true');
} else if (ciRun && ciRun.conclusion === 'success') {
core.info(`CI workflow already succeeded (${ciRun.html_url}).`);
core.setOutput('ci_running', 'true');
} else {
core.info('CI workflow did not run for this commit. Providing fallback Quality Gate.');
core.setOutput('ci_running', 'false');
}

- name: Pass (CI skipped)
Expand Down
84 changes: 84 additions & 0 deletions templates/workflows/quality-gate-fallback.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Quality Gate Fallback
#
# Required Status Check "Quality Gate" を必ず報告するための fallback ワークフロー。
#
# 必要な背景:
# - setup-team-protection.sh はブランチ保護に "Quality Gate" を必須チェックとして登録する
# - ci.yml の Quality Gate ジョブが paths フィルタでスキップされた場合や、
# ci.yml 自体が存在しない / 走らない場合、PR は永遠に
# "Expected — Waiting for status to be reported" のまま blocked になる
# - この fallback は ci.yml の実行可否をチェックし、走っていなければ Pass を emit する
#
# 動作:
# - ci.yml が走っている / 成功している場合 → 何もしない (本体の Quality Gate が優先)
# - ci.yml が走っていない場合 → Pass で Quality Gate を emit
#
# 使い方:
# .github/workflows/quality-gate-fallback.yml にコピーして配置
# setup-team-protection.sh で "Quality Gate" を必須チェックに設定する場合は必須
#
# 既知の制限:
# - GITHUB_TOKEN で push されたコミット (claude[bot] / dependabot[bot] が
# GITHUB_TOKEN で押した場合など) では `pull_request` イベントが発火しないため、
# この fallback も実行されない。その場合は別途 PAT 経由の push か
# workflow_dispatch / repository_dispatch での再トリガーが必要。
#
name: CI Fallback

on:
pull_request:
branches: [main, master, pre-production, production]

permissions:
contents: read
Comment thread
coderabbitai[bot] marked this conversation as resolved.
actions: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-fallback
cancel-in-progress: true

jobs:
quality-gate:
name: Quality Gate
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: Check if CI workflow ran
id: check
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
try {
const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
head_sha: context.sha,
per_page: 20,
});

const ciRun = runs.workflow_runs.find(
r => r.name === 'CI' && r.id !== context.runId
);

if (ciRun && ciRun.status !== 'completed') {
core.info(`CI workflow is running (${ciRun.html_url}), this fallback is not needed.`);
core.setOutput('ci_running', 'true');
} else if (ciRun && ciRun.conclusion === 'success') {
core.info(`CI workflow already succeeded (${ciRun.html_url}).`);
core.setOutput('ci_running', 'true');
} else {
core.info('CI workflow did not run for this commit. Providing fallback Quality Gate.');
core.setOutput('ci_running', 'false');
}
} catch (err) {
// 権限不足 (actions: read 未付与) や一時的な API 障害でクエリ失敗時は
// 安全側に倒す。"CI 実行中扱い" にして fallback の Pass emit をスキップし、
// 本体の Quality Gate (ci.yml) の結果を待つ。Required check が
// 未報告のままになる可能性はあるが、誤った Pass / Fail よりは安全。
core.warning(`Could not query workflow runs (${err.status || err.message}). Skipping fallback.`);
core.setOutput('ci_running', 'true');
}

- name: Pass (CI skipped)
if: steps.check.outputs.ci_running == 'false'
run: echo "Quality Gate passed (CI skipped — no code changes detected or workflow disabled)."
Loading