Skip to content

feat(ci): ワークフロー検査をリポジトリ横断で実行できるようにする - #1075

Merged
keito4 merged 4 commits into
mainfrom
feat/fleet-workflow-guards
Aug 4, 2026
Merged

feat(ci): ワークフロー検査をリポジトリ横断で実行できるようにする#1075
keito4 merged 4 commits into
mainfrom
feat/fleet-workflow-guards

Conversation

@keito4

@keito4 keito4 commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Closes #1074

Note

PR #1073 の commit を含みます。行継続の誤検知を直さないまま横断実行すると正常な calendar_alerm が違反として出るため、この PR は #1073 の上に積んでいます。base はフル CI を通すため main にしています。#1073 を先にマージすれば、この PR の差分は fleet 分だけに縮みます。どちらの順でマージしても問題ありません。

Why

ワークフロー検査は config 内でしか動かない。下流リポジトリには script/repo-maintenance.shscheduled-maintenance.yml も配布されておらず、他のリポジトリで同種の不具合が起きても検知できない。

実際、検査を実リポジトリへ向けたところ config 内では見つからなかった誤検知#1072)が判明した。横断実行は再発防止だけでなく検査自体の品質にも効く。

What

検査は bash と awk だけで動き、対象リポジトリのワークフロー YAML を読むだけなので、各リポジトリへスクリプトを配布せず config から一括で走らせられる。検査を直せば次回の実行から全リポジトリへ反映される。

追加 内容
script/fleet-workflow-guards.sh 直近 push されたリポジトリを自動検出し、4検査を実行して結果を集計する
.github/workflows/fleet-workflow-guards.yml 週次(月曜 07:00 JST)+ 手動実行。days / repos を指定可能

方針

  • 走査は読み取りのみ。対象リポジトリへ Issue や PR は作らない(テストで固定)
  • 1リポジトリで違反が出ても走査を打ち切らない。全違反を集計してから落ちる。1件直すたびに次が出る往復を避けるため
  • 他リポジトリを読むため GITHUB_TOKEN ではなく CLAUDE_PAT を使う(GITHUB_TOKEN は自リポジトリしか見えない)
  • 配布方式(各リポジトリへ script を同期)ではなく中央集約にしたのは、同期対象ファイルが増えず、検査の修正が各リポジトリでの同期 PR マージを待たずに反映されるため。sync 対象外の private-config / agentdeck-streamdeck-plugin なども自動でカバーできる

How to test

実リポジトリ13件に対して本番同等で実行:

✓ config              ✓ intent-gate-android   ✓ private-config
✓ Claude-Usage-Tracker ✓ calendar_alerm       ✓ agentdeck-streamdeck-plugin
✓ personal-dashboard  ✓ ohana                 ✓ extensions
✓ effectuation        ✓ arduino-playground    ✓ notion-mac-task-widget
✓ scale_out
✓ No workflow guard violations across 13 repositories
exit=0

raycast-extensions は最終 push が 90 日超のため対象外(仕様どおり)。

検出側: 行継続の修正前に同じ仕組みで走らせたところ、calendar_alerm の release-please.yml を名指しで報告した(それが #1072 の発見経緯)。

  • Unit (Jest): 945 tests, 0 failures(新規 8 件の Red → Green 確認済み)
  • Integration (BATS): 310 tests, 0 failures(ワークフロー数の固定値を 15→16 へ更新)
  • actionlint / lint / format / shellcheck: すべて pass

レビュー対応

CodeRabbit / Codex / CI の shellcheck から計 5 件の指摘。いずれも横断走査が黙って「違反なし」を報告する、または想定外の範囲へ広がるもので、すべて修正した。

指摘 内容
違反判定を ⚠ 行の grep で行っていた check_artifact_retentionoutput::warning ではなく素の file: message を出すため、非ゼロ終了しているのに違反が消えて clean と報告されていた(再現確認済み)。依存不足など違反以外の失敗も同様に握り潰していた → 終了ステータスを正とする
SC2015 (A && B || C) cd の失敗を || true が握り潰し「違反なし」と報告。実際この構造でテストが空振りした → チェックアウトが無い場合は violation として集計し not scanned と残す
--days 未検証 date が失敗すると cutoff が空になり jq の比較が常に真 → 全リポジトリへ黙って広がる → 正の整数のみ受け付ける
リポジトリ名未検証 ../.. のような名前は dest が作業ディレクトリの外を指し、rm -rf がそこへ及ぶ → 英数字とドット・ハイフン・アンダースコアのみ受け付ける

テストのスタブも「空リポジトリ」と「走査できなかった」を区別できるよう直した。

Unit 951 / Integration 310 / lint / format / shellcheck / actionlint すべて pass。実リポジトリ 5 件での再実行も clean。

Risk

  • CLAUDE_PAT が必要。未設定・失効時は clone が失敗し、該当リポジトリは ⚠️ clone failed として記録され走査は継続する(全体は落ちない)。
  • 週次実行で違反が見つかると config のワークフローが赤くなる。これは意図した挙動(「サマリを出して失敗させる」方針)。現時点で13リポジトリすべて clean のため、導入直後に赤くなることはない。
  • 対象リポジトリを自動検出するため、新しいリポジトリは自動で走査対象に入る。手動実行時は repos 入力で明示指定もできる。
  • リポジトリは --depth 1 --no-tags の浅いクローンで、ランナー上の一時領域にのみ展開する。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added scheduled and manually triggered scanning of workflow configurations across multiple repositories.
    • Added options to limit scans by repository and recency, with aggregated results and Markdown summaries.
  • Bug Fixes
    • Improved detection of repository context in multi-line workflow commands.
  • Documentation
    • Documented the new workflow guard scanner and updated workflow inventory details.
  • Tests
    • Added comprehensive coverage for scanning, reporting, discovery, failures, and multi-line commands.

keito4 and others added 2 commits August 4, 2026 21:13
check_gh_repo_context は gh コマンドと同じ行にある --repo しか見ていなかった。
シェルの行継続で書かれた正常なワークフローを誤検知する。

keito4/calendar_alerm の release-please.yml が実際にこの書き方で、直近6実行は
すべて success。PR #1071 でこの検査は CI をブロックするようになったため、
誤検知は正常動作しているリポジトリの PR を止める。

行継続を1つの論理行へ結合してから判定する。継続行があってもリポジトリ指定が
無ければ検出することをテストで固定した。

複数リポジトリのワークフローに対して実行して発見した。偽陽性を消したうえで
既知の実バグ4件を引き続き検出できることを再実証済み。

Closes #1072

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
検査は bash と awk だけで動き、対象リポジトリのワークフロー YAML を読むだけなので、
各リポジトリへスクリプトを配布せず config から一括で走らせられる。検査を直せば
次回の実行から全リポジトリへ反映される。

script/fleet-workflow-guards.sh を追加し、直近 push されたリポジトリを自動検出して
4検査を実行する。1リポジトリで違反が出ても走査は打ち切らず、全違反を集計してから
落ちる。結果は Job Summary へリポジトリ別のテーブルで出す。

走査は読み取りのみ。対象リポジトリへ Issue や PR は作らない。他リポジトリを読むため
GITHUB_TOKEN ではなく CLAUDE_PAT を使う。

実リポジトリ13件に対して実行し、全て clean であることを確認済み。
raycast-extensions は最終 push が90日超のため対象外(仕様どおり)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Fleet workflow guards

Layer / File(s) Summary
Continued-line repository context parsing
script/lib/repo_maintenance_checks.sh, test/repo-maintenance-gh-guards.test.js
The guard now evaluates gh commands across backslash-continued lines. Tests cover valid and missing repository context.
Cross-repository scan orchestration
script/fleet-workflow-guards.sh, test/fleet-workflow-guards.test.js
The new script discovers or accepts repositories, clones them, runs workflow guards, aggregates violations, and writes summaries.
Scheduled workflow integration and documentation
.github/workflows/fleet-workflow-guards.yml, script/README.md, AGENTS.md, test/integration/workflows.bats, test/fleet-workflow-guards.test.js
GitHub Actions runs the scanner weekly or manually. Documentation and workflow-count tests reflect the new workflow.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

  • Issue 1072: The continued-line parser and tests address the reported false positive for split gh commands.

Possibly related PRs

  • keito4/config#1068: Extends the same check_gh_repo_context guard with continued-command support.
  • keito4/config#1071: Introduces workflow-guard functionality reused by the fleet scanner.
  • keito4/config#194: Shares workflow integration coverage in test/integration/workflows.bats.

Suggested labels: released

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement cross-repository scanning, scheduled and manual execution, summaries, PAT access, continued scanning, and required tests for issue #1074.
Out of Scope Changes check ✅ Passed The documentation, workflow-count update, line-continuation fix, and related tests support the cross-repository workflow inspection objectives.
Title check ✅ Passed The title describes the main feature: enabling workflow checks to run across repositories. It accurately reflects the changeset's primary objective.
Description check ✅ Passed The description includes all required template sections: Summary, Why, What, How to test, Checklist, and Related. All sections are substantially completed with technical details and verification results.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fleet-workflow-guards

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @keito4's task in 2m 58s —— View job


タスク進捗

  • コンテキスト収集
  • 変更ファイルのレビュー(script/fleet-workflow-guards.sh, workflow yml, tests, AGENTS.md/README diff)
  • フィードバック投稿

概要

script/fleet-workflow-guards.shset -euo pipefail の徹底、guard 単位で継続走査する設計(1リポジトリの失敗で打ち切らない)、.github/workflows/fleet-workflow-guards.yml の read-only 保証(gh issue create / gh pr create を含まないことをテストで固定)など、PR説明どおりの方針が実装・テストに反映されています。Unit/Integration ともにテスト網羅度が高く、良い実装だと思います。以下は minor〜recommendation レベルの指摘です(blocking な issue は見つかりませんでした)。

Security

  • [minor] script/fleet-workflow-guards.sh:106-107dest="$WORK_DIR/$repo" の直後に rm -rf "$dest" を実行しますが、$repo の値(--repos 手動指定時、または将来 discover_repos の出力)に対するパス文字(/, .. など)のバリデーションがありません。workflow_dispatchrepos 入力はリポジトリへの書き込み権限を持つユーザーのみがトリガーできるため実害は低いですが、防御的に [[ "$repo" =~ ^[A-Za-z0-9._-]+$ ]] || { output::warning "$repo: invalid repo name; skipped"; continue; } のようなチェックを入れておくと、rm -rf が意図しないパスに向かうリスクを完全に排除できます。
    Fix this →

Quality

  • [minor] script/fleet-workflow-guards.sh:123-126 — サマリ表の行組み立てで $line(guard の警告メッセージ)をそのまま | | ... | セルへ埋め込んでいます。メッセージに | が含まれると Markdown テーブルの見た目が崩れます。既存の sed パイプに s/|/\\|/g を足すだけで防げます。現状の guard メッセージには | は出てこないため実害は小さいですが、将来 guard が増えたときの落とし穴になり得ます。
  • [recommendation] script/fleet-workflow-guards.sh:104-113 — 各リポジトリの一時クローンは次回同名リポジトリを処理する直前(同ループの rm -rf "$dest")にのみ削除されます。discovery 対象から外れたリポジトリの旧クローンは $WORK_DIR に残り続けるため、CI(ランナーが毎回使い捨て)では問題ありませんが、ローカルで繰り返し実行する場合は .context/fleet/ にディスクが溜まります。main() 冒頭で rm -rf "$WORK_DIR" してから mkdir -p するだけで解決できます。
  • [recommendation] scan_repo() は 1 リポジトリにつき repo-maintenance.sh を guard 数分(4回)起動します。現状の規模(13リポジトリ)では問題になりませんが、対象リポジトリが増えるとプロセス起動オーバーヘッドが積み上がる可能性があります。将来的に複数 --check-* を一度に受け付けられるようにすると軽減できますが、いまは対応不要です。

Performance / Accessibility

  • 該当なし(CI スクリプトであり、レンダリングされるのは Markdown サマリのみ)。

AI Residuals

  • モック残骸・スキップされたテスト・ハードコードされた秘密情報は見つかりませんでした。test/fleet-workflow-guards.test.jsgh スタブはテスト専用の意図的なモックで問題ありません。

Test Coverage

  • test/fleet-workflow-guards.test.js は正常系・違反検知・複数リポジトリでの継続走査・discovery・空リポジトリ・GITHUB_STEP_SUMMARY 出力・ワークフロー自体の contract(PAT 使用・read-only)まで幅広くカバーしており、十分な網羅度です。
  • test/integration/workflows.bats のワークフロー数固定値更新(15→16)も整合しています。

その他確認事項

  • .github/workflows/fleet-workflow-guards.yml:13 の cron 0 22 * * 0(UTC)はコメントどおり JST 月曜 07:00 に一致しており、意図とズレはありません。
  • permissions: {} をトップレベルに置き、job 側で contents: read のみを付与する最小権限構成は適切です。

Branch: feat/fleet-workflow-guardsfix/gh-guard-line-continuation

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 081e569163

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread script/fleet-workflow-guards.sh Outdated

for guard in "${GUARDS[@]}"; do
# 1 つ落ちても残りの検査を続ける。まとめて直せるようにするため。
findings+="$(cd "$dest" && "$SCRIPT_DIR/repo-maintenance.sh" "$guard" 2>&1 | grep -E '^(⚠|.*\[1;33m)' || true)"$'\n'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve artifact-retention guard failures

When a scanned workflow omits retention-days or sets it above 30, check_artifact_retention emits a plain file: ... diagnostic rather than an output::warning line. This grep therefore discards the diagnostic, while || true also discards the guard's nonzero status, causing the repository and the overall fleet scan to be reported as clean even though one of the four advertised guards failed.

Useful? React with 👍 / 👎.

Comment on lines +106 to +107
dest="$WORK_DIR/$repo"
rm -rf "$dest"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject repository names that escape the work directory

When --repos receives an invalid name containing traversal components, such as ../.., dest resolves outside WORK_DIR and this unconditional rm -rf can delete the repository checkout or other parent data before gh rejects the name. Because the script is documented for direct local use and the workflow exposes repos as free-form input, validate repository names before using them as paths or derive a path-safe destination.

Useful? React with 👍 / 👎.

@keito4 keito4 self-assigned this Aug 4, 2026
@keito4
keito4 changed the base branch from fix/gh-guard-line-continuation to main August 4, 2026 12:53
@keito4 keito4 closed this Aug 4, 2026
@keito4 keito4 reopened this Aug 4, 2026
@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Claude finished @keito4's task in 3m 37s —— View job


タスク進捗

  • コンテキスト収集
  • 変更ファイルのレビュー(script/fleet-workflow-guards.sh, workflow yml, checks lib, tests)
  • Codex指摘(P1: artifact-retention guard, P2: repo名バリデーション)の検証
  • フィードバック投稿

概要

fleet-workflow-guards.sh の設計(1リポジトリ失敗で走査を打ち切らない、read-only 保証をテストで固定、CLAUDE_PAT の使用理由の明記)は PR 説明と一致しており、テストも discovery / 継続走査 / 空リポジトリ / markdown summary まで丁寧にカバーされています。ただし、Codex が指摘した P1 (script/fleet-workflow-guards.sh:79) はコードを追った結果 実際に成立する不具合 で、修正を推奨します。

Security / Correctness

  • [major, 要修正推奨] script/fleet-workflow-guards.sh:79check_artifact_retentionscript/lib/repo_maintenance_checks.sh:234-281、このPRでの変更なし)は違反メッセージを output::warning プレフィックス + \033[1;33m)ではなく、awk 内から素の printf "%s: ...\n" で直接出力しています。一方 scan_repo() の抽出は grep -E '^(⚠|.*\[1;33m)' に依存しており、check_claude_action_credentials / check_self_cancelling_workflows / check_gh_repo_context の3つ(すべて output::warning 経由)は拾えますが、check_artifact_retention の違反行は色もプレフィックスも無いため grep にマッチせず無条件に捨てられます。さらに | grep ... || true によって pipefail 経由のパイプライン失敗も握り潰されるため、retention-days 未設定/30超のワークフローがあっても該当リポジトリは ✅ clean として集計されます。

    • 実機検証: .context/ 配下に upload-artifact かつ retention-days 未指定のワークフローを置き、repo-maintenance.sh --check-artifact-retention の出力形式を確認 → 期待どおり /色無しの平文行のみが出力され、scan_repo のフィルタを素通りすることを確認しました。
    • test/fleet-workflow-guards.test.jsGH_CONTEXT_VIOLATIONcheck_gh_repo_context 由来)でしか違反検知をテストしておらず、この経路は未カバーです。PR 説明は「4検査を実行して結果を集計する」としているため、現状は仕様と実装が食い違っています。
    • 修正案: check_artifact_retention 側で他3ガードと同様 output::warning 経由の出力に揃えるか、scan_repo() 側で repo-maintenance.sh の終了コードそのものを violations 判定に使う(テキストのフォーマットに依存しない)方式に変更するのが安全です。
      Fix this →
  • [minor] script/fleet-workflow-guards.sh:106-107 — Codex の P2 指摘(../.. のような --repos 入力で rm -rf "$dest"WORK_DIR の外を指し得る)は妥当ですが、実害は低いと判断します。--repos を渡せるのは手動 CLI 実行者、または workflow_dispatch をトリガーできる書き込み権限者に限られ、discover_repos()gh repo list 由来)は信頼できる入力です。防御的に [[ "$repo" =~ ^[A-Za-z0-9._-]+$ ]] || { output::warning "..."; continue; } を入れておくと安全側に倒せます(前回のレビューコメントと同内容)。

Quality

  • script/fleet-workflow-guards.sh:122-126 — summary 行組み立てで $line| エスケープせずテーブルセルに埋め込んでいます。ガードの警告文に | が含まれるとテーブルが崩れます。現状の3ガードのメッセージには出てこないため実害は小さいですが、sed 's/|/\\|/g' を足すと将来的な破綻を防げます。
  • script/fleet-workflow-guards.sh:104-113 — 各リポジトリの一時クローンは同名リポジトリの次回実行時にのみ削除されます。discovery 対象から外れたリポジトリの旧クローンは $WORK_DIR(デフォルト .context/fleet)に残ります。CI(使い捨てランナー)では無害ですが、ローカルで繰り返し実行するとディスクが溜まるため、main() 冒頭で rm -rf "$WORK_DIR" してから mkdir -p すると解消します。

Test Coverage

  • test/fleet-workflow-guards.test.js は discovery・複数リポジトリでの継続走査・空リポジトリ・GITHUB_STEP_SUMMARY 出力・ワークフローの contract(CLAUDE_PAT 使用、read-only)まで広くカバーしていますが、上記の通り check_artifact_retention 由来の違反検知は未テストです。テストがあれば今回の不具合は Red で検出できたはずなので、追加を推奨します。
  • test/integration/workflows.bats のワークフロー数固定値更新(15→16)、test/repo-maintenance-gh-guards.test.js の行継続テスト追加は整合しています。

Performance / Accessibility

  • CI スクリプトでレンダリング対象は markdown サマリのみのため、該当なし。

AI Residuals

  • モック残骸・スキップされたテスト・ハードコードされた秘密情報は見つかりませんでした。テスト内の gh スタブは意図的なテスト用モックです。

その他確認事項

  • .github/workflows/fleet-workflow-guards.ymlpermissions: {} トップレベル + job 側 contents: read のみという最小権限構成、cron 0 22 * * 0(UTC)= JST 月曜 07:00 は妥当です。
  • AGENTS.md / script/README.md の差分(ワークフロー数 15→16、fleet-workflow-guards.yml の追記)は現状と一致しています。

まとめ: blocking級は P1(check_artifact_retention の違反が集計から漏れる)のみで、これは PR 自身が謳う「4検査を集計する」という仕様と食い違うため修正を推奨します。他は minor / recommendation です。

CI の shellcheck が SC2015 (A && B || C) を指摘した。単なる lint ではなく実害が
あり、cd の失敗を || true が握り潰して「違反なし」と報告していた。実際この構造で
テストが空振りしていた。

チェックアウトが無い場合は 2 を返して violation として集計し、サマリへ
"not scanned" と残す。テストのスタブも既知リポジトリのときだけ dest を作るよう
直し、「空リポジトリ」と「走査できなかった」を区別できるようにした。

あわせて && を使わない形へ書き換え、CI の古い shellcheck でも通るようにした。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (5)
script/README.md (1)

209-217: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the --owner option.

The script accepts --owner OWNER and the FLEET_OWNER environment variable. The usage block in the script lists --owner, but this section does not. Add it so the README matches the script surface.

📝 Proposed change
 ./script/fleet-workflow-guards.sh                       # 直近90日に push されたリポジトリ
 ./script/fleet-workflow-guards.sh --days 30
 ./script/fleet-workflow-guards.sh --repos "config ohana"
+./script/fleet-workflow-guards.sh --owner keito4
🤖 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 `@script/README.md` around lines 209 - 217, Update the “Usage” section in
script/README.md to document the supported --owner OWNER option and its
FLEET_OWNER environment-variable equivalent, alongside the existing
fleet-workflow-guards.sh examples, so the README matches the script’s interface.
script/fleet-workflow-guards.sh (1)

133-136: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

A finding that contains | breaks the markdown table row.

The sed filter strips ANSI codes and the leading , but it does not escape |. Guard messages include workflow content, so a pipe character is possible. The job summary row then renders with extra columns.

Escape | in the same sed expression.

♻️ Proposed change
-        summary+="| | $(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*m//g; s/^[[:space:]]*⚠[[:space:]]*//') |"$'\n'
+        summary+="| | $(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*m//g; s/^[[:space:]]*⚠[[:space:]]*//; s/|/\\\\|/g') |"$'\n'
🤖 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 `@script/fleet-workflow-guards.sh` around lines 133 - 136, Update the `sed`
filter in the `findings` loop to escape every `|` in each finding before
appending it to `summary`, while preserving the existing ANSI-code and
leading-warning-marker removal.
script/lib/repo_maintenance_checks.sh (1)

202-218: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Line-continuation state is not reset at a job boundary.

pending survives across jobs and files. The job-header rule at line 204 is guarded by pending == "", so a run step whose last line ends with \ swallows the next job header into pending. The following job is then evaluated as part of the previous job. This is unlikely in valid YAML, but it is cheap to make the parser strict.

Reset pending inside flush().

🤖 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 `@script/lib/repo_maintenance_checks.sh` around lines 202 - 218, Reset the
line-continuation variable pending inside the flush() function so no
continuation state carries into the next job or file. Keep the existing
job-header parsing and eval_line behavior unchanged.
.github/workflows/fleet-workflow-guards.yml (1)

31-37: 🧹 Nitpick | 🔵 Trivial

Consider the timeout and the owner default.

Two operational notes for the scheduled run:

  • timeout-minutes: 15 covers up to 200 shallow clones plus four guards per repository. Watch the first scheduled runs and raise the value if the job is cancelled.
  • The owner defaults to the literal keito4 inside the script. Passing FLEET_OWNER: ${{ github.repository_owner }} keeps the workflow correct in a fork or after a rename.
🤖 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 @.github/workflows/fleet-workflow-guards.yml around lines 31 - 37, Update the
scan job configuration to pass FLEET_OWNER using the GitHub repository owner
context, ensuring scheduled runs target the current owner in forks or renamed
repositories; retain the existing timeout unless initial scheduled runs show
that 15 minutes is insufficient.
test/fleet-workflow-guards.test.js (1)

23-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The gh stub returns discovered names for any repo list invocation.

The stub ignores --jq and --json, so it cannot detect a regression in the pushedAt cutoff filter or in the --days handling. The discovery test at line 141 therefore proves only that mapfile consumes stdout.

Consider echoing JSON and letting the real --jq path run, or add a stub assertion that --days reached gh repo list. This is optional for this PR.

🤖 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 `@test/fleet-workflow-guards.test.js` around lines 23 - 44, Strengthen
buildGhStub so repo list handling validates the requested --days cutoff and
supports the --json/--jq output path instead of always echoing discovered names.
Update the discovery test using buildGhStub to verify the cutoff argument
reaches gh repo list and that filtering is applied, while preserving existing
clone behavior.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@script/fleet-workflow-guards.sh`:
- Around line 112-116: Increment the violations counter in the clone-failure
branch before continuing, matching the existing missing-checkout handling so
failed scans produce a nonzero exit status and are not reported as clean. Add
test coverage for a gh repo clone invocation that exits nonzero, asserting the
failure is summarized as a violation and the script returns failure.
- Around line 107-110: Add path-safety validation for the repo variable in the
loop immediately after the existing non-empty check and before assigning dest.
Reject any repo value containing path traversal characters such as `/`, `..`,
embedded nulls, or other dangerous characters by adding a validation check that
continues to the next iteration if the name fails. This ensures only safe
repository names reach the dest assignment and the rm -rf command.
- Around line 81-85: Update the guard loop around repo-maintenance.sh so it
preserves the command’s exit status instead of unconditionally swallowing it
with || true. Continue processing all guards when output contains violation
findings, but treat a non-zero status with no matching warning lines as a scan
error and report the repository as not clean.
- Around line 62-71: The discover_repos function does not validate the DAYS
variable before passing it to the date commands. When DAYS is not a positive
integer, both date invocations fail, cutoff becomes an empty string, and the jq
select filter matches all repositories instead of enforcing the intended time
window. Add validation to ensure DAYS is a positive integer before using it in
the date command invocations, and remove or restructure the || fallback so that
an invalid DAYS or failed date processing causes the function to exit with an
error rather than allow an empty cutoff value to reach the jq filter.

In `@script/lib/repo_maintenance_checks.sh`:
- Around line 193-201: Update eval_line to detect GH_REPO declarations before
entering a job, store that workflow-level state, and seed each job’s has_gh_repo
value from it when the job begins. Preserve the existing per-job detection and
command validation behavior.

In `@test/fleet-workflow-guards.test.js`:
- Around line 188-193: In the test 'does not report clean when the checkout is
missing', add an assertion to verify that the result object from runFleet has a
non-zero exit status or exit code. This ensures the script exits with failure
when the repository checkout is missing, not just that the output contains the
expected text. Place this assertion alongside the existing output expectations.

---

Nitpick comments:
In @.github/workflows/fleet-workflow-guards.yml:
- Around line 31-37: Update the scan job configuration to pass FLEET_OWNER using
the GitHub repository owner context, ensuring scheduled runs target the current
owner in forks or renamed repositories; retain the existing timeout unless
initial scheduled runs show that 15 minutes is insufficient.

In `@script/fleet-workflow-guards.sh`:
- Around line 133-136: Update the `sed` filter in the `findings` loop to escape
every `|` in each finding before appending it to `summary`, while preserving the
existing ANSI-code and leading-warning-marker removal.

In `@script/lib/repo_maintenance_checks.sh`:
- Around line 202-218: Reset the line-continuation variable pending inside the
flush() function so no continuation state carries into the next job or file.
Keep the existing job-header parsing and eval_line behavior unchanged.

In `@script/README.md`:
- Around line 209-217: Update the “Usage” section in script/README.md to
document the supported --owner OWNER option and its FLEET_OWNER
environment-variable equivalent, alongside the existing fleet-workflow-guards.sh
examples, so the README matches the script’s interface.

In `@test/fleet-workflow-guards.test.js`:
- Around line 23-44: Strengthen buildGhStub so repo list handling validates the
requested --days cutoff and supports the --json/--jq output path instead of
always echoing discovered names. Update the discovery test using buildGhStub to
verify the cutoff argument reaches gh repo list and that filtering is applied,
while preserving existing clone behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 309a921d-42e9-46c9-8842-9d7034b9ed88

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4303c and 19acb5e.

📒 Files selected for processing (8)
  • .github/workflows/fleet-workflow-guards.yml
  • AGENTS.md
  • script/README.md
  • script/fleet-workflow-guards.sh
  • script/lib/repo_maintenance_checks.sh
  • test/fleet-workflow-guards.test.js
  • test/integration/workflows.bats
  • test/repo-maintenance-gh-guards.test.js

Comment thread script/fleet-workflow-guards.sh
Comment thread script/fleet-workflow-guards.sh
Comment on lines +107 to +110
for repo in "${repos[@]}"; do
[[ -n "$repo" ]] || continue
dest="$WORK_DIR/$repo"
rm -rf "$dest"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching fleet-workflow-guards.sh:\n'
fd -a 'fleet-workflow-guards\.sh$' . || true

file="$(fd 'fleet-workflow-guards\.sh$' . | head -n 1 || true)"
if [[ -n "$file" ]]; then
  printf '\nOutline:\n'
  ast-grep outline "$file" || true
  printf '\nRelevant lines:\n'
  sed -n '1,170p' "$file" | cat -n
fi

printf '\nSearch for script/fleet-workflow-guards.sh references and repos input/argument usage:\n'
rg -n --hidden --glob '!*.lock' --glob '!node_modules/**' 'fleet-workflow-guards\.sh|\\-\\-repos|repos=|workflow_dispatch|inputs\.repos|output::fatal|rm -rf' .

Repository: keito4/config

Length of output: 17642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re
for bad in ["foo/bar", "foo/..", "../etc", "/etc", "foo\\0bar"]:
    dest = "/tmp/work/" + bad
    print(f"{bad!r}: dest={dest!r}, starts_with={dest.startswith('/tmp/work/')}, contains_dotdot={bad.endswith('/..') or '/..' in bad}")
PY

Repository: keito4/config

Length of output: 529


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Workflow relevant lines:\n'
sed -n '1,80p' .github/workflows/fleet-workflow-guards.yml | cat -n

printf '\nTest cases touching fleet-workflow-guards validation/repo names:\n'
sed -n '1,260p' test/fleet-workflow-guards.test.js | cat -n

Repository: keito4/config

Length of output: 11410


Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Reject invalid repository names before constructing dest.

--repos comes from FLEET_REPOS, so values like ../evil, /etc/passwd, or embedded nulls are passed unchecked to rm -rf "$WORK_DIR/$repo" before the clone check. Gate repo before assigning dest, e.g. match an allowed repository-name pattern and reject names containing /, .., or other path-dangerous characters.

🤖 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 `@script/fleet-workflow-guards.sh` around lines 107 - 110, Add path-safety
validation for the repo variable in the loop immediately after the existing
non-empty check and before assigning dest. Reject any repo value containing path
traversal characters such as `/`, `..`, embedded nulls, or other dangerous
characters by adding a validation check that continues to the next iteration if
the name fails. This ensures only safe repository names reach the dest
assignment and the rm -rf command.

Comment on lines +112 to +116
if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

A clone failure is reported as no violations and the script exits 0.

The continue at line 115 skips the violations increment. The summary row shows ⚠️ clone failed, but violations stays 0. If every repository fails to clone, line 143 prints "No workflow guard violations across N repositories" and line 154 returns success.

This contradicts the stated behavior that a repository which cannot be scanned must be aggregated as a violation. The missing-checkout path at line 121 already increments violations; make the clone-failure path consistent.

The current test suite does not cover this path. does not report clean when the checkout is missing exercises the scan_status -eq 2 branch, because the gh stub exits 0 for unknown repositories. Add a test where gh repo clone exits non-zero.

🐛 Proposed fix
     if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
+      violations=$((violations + 1))
       output::warning "$repo: clone failed; skipped"
       summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
       continue
     fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi
if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
violations=$((violations + 1))
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi
🤖 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 `@script/fleet-workflow-guards.sh` around lines 112 - 116, Increment the
violations counter in the clone-failure branch before continuing, matching the
existing missing-checkout handling so failed scans produce a nonzero exit status
and are not reported as clean. Add test coverage for a gh repo clone invocation
that exits nonzero, asserting the failure is summarized as a violation and the
script returns failure.

Comment on lines +193 to +201
function eval_line(l) {
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

GH_REPO declared at workflow level is not detected.

eval_line returns early when in_job is 0. A workflow that declares GH_REPO in the top-level env: block (before jobs:) satisfies every job, but the guard never records it. Such a workflow is reported as a violation.

If you want the guard to accept that pattern, track a workflow-level flag before jobs: and seed each job with it.

♻️ Proposed change
       function flush() {
-        if (in_job && bad_cmd && !has_checkout && !has_gh_repo) bad = 1
+        if (in_job && bad_cmd && !has_checkout && !has_gh_repo && !global_gh_repo) bad = 1
         bad_cmd = 0; has_checkout = 0; has_gh_repo = 0
       }
       function eval_line(l) {
-        if (!in_job) return
+        if (!in_jobs && l ~ /^[[:space:]]*GH_REPO:/) { global_gh_repo = 1; return }
+        if (!in_job) return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function eval_line(l) {
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}
function flush() {
if (in_job && bad_cmd && !has_checkout && !has_gh_repo && !global_gh_repo) bad = 1
bad_cmd = 0; has_checkout = 0; has_gh_repo = 0
}
function eval_line(l) {
if (!in_jobs && l ~ /^[[:space:]]*GH_REPO:/) { global_gh_repo = 1; return }
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}
🤖 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 `@script/lib/repo_maintenance_checks.sh` around lines 193 - 201, Update
eval_line to detect GH_REPO declarations before entering a job, store that
workflow-level state, and seed each job’s has_gh_repo value from it when the job
begins. Preserve the existing per-job detection and command validation behavior.

Comment on lines +188 to +193
test('does not report clean when the checkout is missing', () => {
const result = runFleet(['--repos', 'ghost'], { workflows: {} });

expect(result.output).not.toContain('No workflow guard violations');
expect(result.output).toContain('ghost');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the exit status for the missing-checkout case.

The PR objective requires a non-zero exit when a repository cannot be scanned. This test only checks the output text. It passes even if the script exits 0.

Add the status assertion so the exit contract is fixed by the test.

💚 Proposed change
     const result = runFleet(['--repos', 'ghost'], { workflows: {} });
 
+    expect(result.status).not.toBe(0);
     expect(result.output).not.toContain('No workflow guard violations');
     expect(result.output).toContain('ghost');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('does not report clean when the checkout is missing', () => {
const result = runFleet(['--repos', 'ghost'], { workflows: {} });
expect(result.output).not.toContain('No workflow guard violations');
expect(result.output).toContain('ghost');
});
test('does not report clean when the checkout is missing', () => {
const result = runFleet(['--repos', 'ghost'], { workflows: {} });
expect(result.status).not.toBe(0);
expect(result.output).not.toContain('No workflow guard violations');
expect(result.output).toContain('ghost');
});
🤖 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 `@test/fleet-workflow-guards.test.js` around lines 188 - 193, In the test 'does
not report clean when the checkout is missing', add an assertion to verify that
the result object from runFleet has a non-zero exit status or exit code. This
ensures the script exits with failure when the repository checkout is missing,
not just that the output contains the expected text. Place this assertion
alongside the existing output expectations.

CodeRabbit と Codex の指摘4件を修正した。いずれも横断走査が黙って
「違反なし」を報告する、または想定外の範囲へ広がる不具合。

1. 違反の判定を ⚠ 行の grep で行っていた
   check_artifact_retention は output::warning ではなく素の "file: message"
   を出すため、非ゼロ終了しているのに違反が消えて clean と報告されていた。
   依存不足など違反以外の失敗も同様に握り潰していた。
   → 終了ステータスを正とし、出力が空でもその旨を残す。

2. --days を検証していなかった
   date が失敗すると cutoff が空になり、jq の比較が常に真になって全リポジトリ
   へ黙って広がる。→ 正の整数のみ受け付ける。

3. リポジトリ名を検証していなかった
   ../.. のような名前は dest が作業ディレクトリの外を指し、rm -rf がそこへ
   及ぶ。→ 英数字とドット・ハイフン・アンダースコアのみ受け付ける。

artifact-retention の消失は実際に再現して確認した。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@keito4
keito4 merged commit b590709 into main Aug 4, 2026
17 checks passed
@keito4
keito4 deleted the feat/fleet-workflow-guards branch August 4, 2026 14:39
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.134.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions github-actions Bot added the released リリース済み label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released リリース済み

Projects

None yet

Development

Successfully merging this pull request may close these issues.

repo-maintenance のワークフロー検査をリポジトリ横断で実行できるようにする

1 participant