Skip to content

refactor: generalize caller distribution (workflow-agnostic)#42

Merged
toshi0806 merged 3 commits into
mainfrom
generalize-distributor
Jun 8, 2026
Merged

refactor: generalize caller distribution (workflow-agnostic)#42
toshi0806 merged 3 commits into
mainfrom
generalize-distributor

Conversation

@toshi0806

Copy link
Copy Markdown
Member

概要

レビュー専用だった distribute-claude-review.sh を、特定ワークフロー・シークレットに依存しない汎用配布スクリプト distribute-workflow.sh に作り直す。今後の共有ワークフロー(レビューや ANTHROPIC_API_KEY と無関係なものも含む)を、テンプレートを1つ追加するだけで配布できるようにする。

変更内容

  • scripts/distribute-workflow.sh: caller 非依存。<caller> テンプレートを各リポジトリの .github/workflows/<caller>.yml として設置し、同名 reusable を @<ref> で呼ぶ
    • 置換は --ref__REF__)+ 汎用 --var KEY=VALUE__KEY__)。GitHub の ${{ }} 式は不変
    • --model / --language--var MODEL= / --var LANGUAGE= の別名(Claude caller 用の利便)
    • スクリプト本体に Claude / ANTHROPIC_API_KEY / model のハードコードなし。唯一の前提は gh の write 権限
  • scripts/callers/: caller テンプレート(claude-code-review.yml / claude-mention.yml)と、各 PR 注記 *.pr-note.md
    • ANTHROPIC_API_KEY 必須という固有事情は pr-note と README の caller 表に分離(スクリプトには書かない)
  • scripts/README.md: ワークフロー非依存に再構成。caller 別の前提を表で明示+「新しい共有ワークフローの追加手順」を記載

設計の要点

ワークフロー固有の事情(必要シークレット・可変値・PR 注記)は caller 側に置き、スクリプトは「テンプレートを置換して PR を作る」汎用機構に徹する。レビュー・mention 以外のワークフローもそのまま配布対象にできる。

検証

  • bash -n / shellcheck クリーン
  • --list-callers、dry-run、生成 caller の YAML 妥当性、PR 本文(汎用+pr-note)すべて確認

後続

本 PR マージ後、この汎用スクリプトで claude-mention を6テンプレートへ配布します(別途)。

Replace distribute-claude-review.sh with a generic distribute-workflow.sh
that knows nothing about Claude, reviews, secrets, or models. Distribute any
caller defined under scripts/callers/; adding a new shared workflow needs only
a template (and optional PR note), no script changes.

- distribute-workflow.sh: caller-agnostic; --ref + generic --var KEY=VALUE
  substitution (__REF__/__KEY__ tokens; GitHub ${{ }} left intact). --model
  and --language are thin aliases for --var. PR body is generic; per-caller
  details come from scripts/callers/<caller>.pr-note.md. Only prerequisite is
  gh write access — no secret assumptions.
- scripts/callers/: caller templates (claude-code-review, claude-mention) and
  their .pr-note.md (the ANTHROPIC_API_KEY requirement lives here, not in the
  script).
- scripts/README.md: rewritten workflow-agnostic; per-caller requirements in a
  table, with a guide for adding new shared workflows.

@github-actions github-actions 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.

汎用的な配布スクリプトへのリファクタリングは素晴らしい改善です。これにより、新しい共有ワークフローの追加が非常に容易になり、スクリプトの保守性も向上しました。scripts/callers/ ディレクトリにテンプレートとPR注記を分離した設計は、ワークフロー固有のロジックをスクリプト本体から切り離すという目標を達成しています。

Comment thread scripts/distribute-workflow.sh Outdated
# GitHub ${{ ... }} expressions contain no __X__ tokens and are left intact.
SED_ARGS=(-e "s|__REF__|${REF}|g")
for kv in "${VARS[@]}"; do
SED_ARGS+=(-e "s|__${kv%%=*}__|${kv#*=}|g")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ [MEDIUM] scripts/distribute-workflow.sh--var KEY=VALUE オプションで渡される値が sed コマンドで置換される際、VALUE|& といった sed の特殊文字が含まれていると、スクリプトがエラーになったり、意図しない置換が発生したりする可能性があります。汎用スクリプトとして、より堅牢な sed エスケープ処理を導入することを検討してください。

@@ -0,0 +1,3 @@
PR への自動レビュー(Claude)を有効化します。

- 動作には org シークレット `ANTHROPIC_API_KEY` がこのリポジトリで利用可能である必要があります(未配布なら安全にスキップ)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ [LOW] 以前の distribute-claude-review.sh のPR本文にあった「draft はスキップ(ready_for_review で起動)」「fork PR では secret が渡らないため安全にスキップ」といった運用上の重要な注意点を追記すると、配布するユーザーにとってより親切になるでしょう。これらの情報は、ワークフローの動作を理解する上で役立ちます。

Comment thread scripts/distribute-workflow.sh Outdated
# GitHub ${{ ... }} expressions contain no __X__ tokens and are left intact.
SED_ARGS=(-e "s|__REF__|${REF}|g")
for kv in "${VARS[@]}"; do
SED_ARGS+=(-e "s|__${kv%%=*}__|${kv#*=}|g")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[warning] --var の VALUE に sed のデリミタ | や特殊文字(&\)が含まれると、sed 式が壊れるか意図しない置換が起きます。

具体的な挙動:

  • --var MODEL=foo|bars|__MODEL__|foo|bar|g は sed 構文エラー(デリミタが4つになる)
  • --var MODEL=claude-&-model& が sed の「マッチ文字列」(__MODEL__)に展開され claude-__MODEL__-model になる
  • --var MODEL=foo\nbar → GNU sed では \n が改文字に化けて YAML が壊れる

壊れた YAML がそのまま各リポジトリへ配布される点が問題です。

修正案: printf '%s\n' + |/ への変換か、awk -v による置換に切り替える。最小コスト案は sed -e "s|...|...|g" を同等な Python ワンライナーや envsubst に置き換えることです。少なくとも | が VALUE に含まれていないかチェックするバリデーションを追加してください。

Comment on lines +26 to +27
model: __MODEL__
review_language: __LANGUAGE__

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[warning] --model / --language を省略してスクリプトを実行すると、これらのトークンが未置換のまま(リテラルの __MODEL____LANGUAGE__)各リポジトリへ配布されます。

旧スクリプトはデフォルトを sonnet / 日本語 にしていましたが、新スクリプトではデフォルト値もバリデーションも除去されています。未置換の値が reusable ワークフローへ渡ると、不明モデル名として API コールが失敗します。

同じ問題は scripts/callers/claude-mention.yml の 32〜33 行目にも存在します。

対処案(いずれか):

  1. スクリプト側でデプロイ時に未置換トークン(__[A-Z_]+__)が残っていないか検証してエラーにする
  2. テンプレート自体に YAML デフォルト値を持たせ、トークン方式をやめる(例: reusable 側の with.model input にデフォルト値がある場合は with: ブロック自体を省略する)
  3. 旧スクリプト同様にスクリプト側でデフォルトを復活させる

Comment thread scripts/distribute-workflow.sh Outdated
Comment on lines +174 to +175
gh api -X POST "repos/${repo}/git/refs" \
-f ref="refs/heads/${PR_BRANCH}" -f sha="${head_sha}" >/dev/null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[warning] set -euo pipefail 下でこの gh api -X POST(ブランチ作成)が失敗した場合、スクリプト全体が即座に終了します。他のリポジトリの処理が中断されるため、複数リポジトリに配布する際にリスクがあります。

失敗しうるケース:ネットワーク瞬断・GitHub API レート制限・並行実行による競合(TOCTOU: 存在チェックと作成の間に他のプロセスが同名ブランチを作成)。

旧スクリプトからの引き継ぎですが、失敗時は continue で次のリポジトリへ進む方が設計意図(per-repo エラーカウント)に合っています。

Suggested change
gh api -X POST "repos/${repo}/git/refs" \
-f ref="refs/heads/${PR_BRANCH}" -f sha="${head_sha}" >/dev/null
if ! gh api -X POST "repos/${repo}/git/refs" \
-f ref="refs/heads/${PR_BRANCH}" -f sha="${head_sha}" >/dev/null 2>&1; then
echo " ✗ failed to create branch ${PR_BRANCH}"; failed=$((failed+1)); continue
fi

Address review findings on the generalized distributor:
- Escape sed replacement so --var values with | & \ substitute literally
  (a generic --var may carry anything); validate token keys
- Refuse to distribute a template with unsubstituted __TOKEN__ left (e.g. a
  forgotten --model) — fail once, before touching any repo
- Restore per-caller defaults via scripts/callers/<caller>.defaults (CLI
  --var/--model/--language still override); claude callers default to
  sonnet / 日本語
- Don't let one repo's gh API failure abort the batch: guard the
  head-sha read and branch creation and continue to the next repo
- pr-note: document draft/fork graceful-skip for the review caller
@toshi0806

Copy link
Copy Markdown
Member Author

レビュー対応(commit 943aab4

Claude・Gemini 両レビューの指摘に対応しました。いずれも汎用化に伴う妥当な指摘でした。

[warning] sed 特殊文字で置換が壊れる(Claude・Gemini)

--var の VALUE を sed 置換用にエスケープ(\ & |)し、リテラル置換されるよう修正。トークンキーも [A-Za-z_][A-Za-z0-9_]* で検証。

  • 検証: --var MODEL='a|b&c'model: "a|b&c"(YAML 妥当・リテラル一致)✅

[warning] --model 省略で __MODEL__ 未置換のまま配布(Claude)

2段で対処:

  1. caller 別 scripts/callers/<caller>.defaults を追加(claude 系は MODEL=sonnet / LANGUAGE=日本語)。CLI --var/--model/--language が優先。旧スクリプトのデフォルト挙動を汎用な形で復活
  2. 未置換トークン検出: 描画後に __TOKEN__ が残れば配布前に die(どのリポジトリにも触れない)
  • 検証: --model 省略でも sonnet で動作 / __BOGUS__ 残存テンプレは error: ... unsubstituted tokens: __BOGUS__ で停止 ✅

[warning] set -e 下で1リポジトリの API 失敗が全体を中断(Claude)

head_sha 取得とブランチ作成(gh api -X POST)をガードし、失敗時は failed に計上して次のリポジトリへ continueするよう修正。バッチが途中で全停止しなくなりました。

[LOW] pr-note の運用注記(Gemini)

claude-code-review.pr-note.md に「draft は ready_for_review まで・fork は secret 不在でスキップ」を追記。

検証

bash -n / shellcheck クリーン。defaults・CLI override・エスケープ・未置換検出・dry-run すべて確認済み。

@github-actions github-actions 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.

このPRは、ワークフロー配布スクリプトを汎用化するという素晴らしいリファクタリングです。特定のワークフローやシークレットに依存しない設計は、将来的な拡張性とメンテナンス性を大幅に向上させます。テンプレートベースのアプローチ、堅牢なトークン置換ロジック、そして詳細なドキュメントは、非常に高品質な変更だと評価できます。

Comment thread scripts/README.md
Claude Code Review の caller ワークフローを、指定したリポジトリ群へ配布します。
caller は共有 reusable
`smkwlab/.github/.github/workflows/claude-code-review.yml@<ref>` を呼び出します。
共有 caller ワークフローを、指定したリポジトリ群へ配布する**汎用**スクリプト。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] ワークフロー配布スクリプトの汎用化に伴い、READMEが非常に分かりやすく更新されています。特に、callerテンプレートの表と新しいワークフローを追加する手順は、今後の運用と拡張性を大きく助けるでしょう。


jobs:
review:
uses: smkwlab/.github/.github/workflows/claude-code-review.yml@__REF__

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] Claude Code Reviewワークフローのcallerテンプレートが適切に定義されています。__REF____MODEL____LANGUAGE__といったプレースホルダーの使用は、スクリプトの汎用性を保ちつつ、caller固有の設定を可能にする良い設計です。

claude:
# Run only when @claude is mentioned AND the author has repository access
# (OWNER/MEMBER/COLLABORATOR), so external users cannot trigger a write run.
if: >

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] Claude Mentionワークフローのcallerテンプレートも適切に定義されています。特に、if条件で@claudeメンションとauthor_associationをチェックしている点は、セキュリティ面で非常に良い実装です。

Comment thread scripts/distribute-workflow.sh Outdated

# Refuse to distribute a template with any unsubstituted __TOKEN__ (e.g. a
# forgotten --model). Caught once here, before touching any repository.
leftover=$(render "$TEMPLATE" | grep -oE '__[A-Za-z0-9_]+__' | sort -u | tr '\n' ' ' || true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] このスクリプトは、以前の特定ワークフロー向けスクリプトから完全に汎用化されており、設計思想が明確に反映されています。特に、--varオプションによる柔軟な置換、.defaultsファイルからのデフォルト値読み込み、そして未置換トークンのチェック(144行目)は、スクリプトの堅牢性と安全性を大きく高めています。

@@ -0,0 +1,4 @@
PR への自動レビュー(Claude)を有効化します。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] 各callerの.pr-note.mdファイルは、生成されるPRの本文に重要な情報(必要なシークレットなど)を自動的に含めることができ、非常に有用です。これにより、配布されたワークフローの前提条件が明確になり、利用者の混乱を防ぎます。

@toshi0806

Copy link
Copy Markdown
Member Author

@claude 最新コミット(943aab4)で distribute-workflow.sh をレビュー指摘に沿って堅牢化しました(sed エスケープ・未置換トークン検出・caller 別 defaults・エラー時の継続化)。最新の変更を改めてレビューして、残課題や見落としがあれば指摘してください。

@claude

claude Bot commented Jun 8, 2026

Copy link
Copy Markdown

Claude finished @toshi0806's task in 2m 44s —— View job


コードレビュー(943aab4)

  • コンテキスト確認
  • distribute-workflow.sh 読み込み・分析
  • scripts/callers/ テンプレート・defaults 読み込み・分析
  • 残課題・見落とし確認

全体評価: 前回レビューで指摘した4点はいずれも的確に対処されており、スクリプトの堅牢性が大きく向上しています。このままマージして問題ないと判断します。以下に各修正の評価と残る細かい指摘を記します。


前回指摘への対処評価

✅ sed エスケープ(L134–139)

esc=$(printf '%s' "$val" | sed -e 's/[\\&|]/\\&/g')
SED_ARGS+=(-e "s|__${key}__|${esc}|g")

\&| の3種すべてをエスケープしており正しい実装です。
置換後の動作を確認しました:

  • |\| → sed の置換部で | がリテラルになる ✓
  • &\& → sed の置換部でバックリファレンス展開されなくなる ✓
  • \\\ → リテラルのバックスラッシュになる ✓

✅ 未置換トークン検出(L144–145)

leftover=$(render "$TEMPLATE" | grep -oE '__[A-Za-z0-9_]+__' | sort -u | tr '\n' ' ' || true)
[[ -z "${leftover// /}" ]] || die "..."

テンプレートファイルを配布前に一度だけ検証し、どのリポジトリにも触れる前に fail する設計は正確です。|| truegrep 非マッチ時の終了コードを吸収している点も適切。

.defaults 優先順位ロジック(L119–128)

ORDERED=()
((${#VARS[@]}))     && ORDERED+=("${VARS[@]}")
((${#DEFAULTS[@]})) && ORDERED+=("${DEFAULTS[@]}")
ORDERED+=("REF=${REF}")

CLI --varDEFAULTS より先に ORDERED に積まれ、sed は最初の -e マッチが勝つため、CLI → defaults → REF の優先順位が正確に実装されています。旧スクリプトのデフォルト値(MODEL=sonnet / LANGUAGE=日本語)を汎用な形で復活させた設計は筋が良いです。

✅ エラー時の継続化(L196–205)

if ! head_sha=$(gh api ... 2>/dev/null); then
  echo "  ✗ ..."; failed=$((failed+1)); continue
fi
if ! gh api -X POST "repos/${repo}/git/refs" ... >/dev/null 2>&1; then
  echo "  ✗ ..."; failed=$((failed+1)); continue
fi

set -euo pipefail 下でも if ! でガードしているため、1リポジトリの失敗が全体を中断しません。前回指摘の TOCTOU(ブランチ存在チェックと作成の間)については、POST 失敗が if ! で捕捉され次のリポジトリへ継続するため実質的に対処済みです。


残る細かい指摘

[LOW] pr-note.md の未置換トークンは検査されない

L144 の検証は $TEMPLATE のみが対象です。$NOTE_FILE(pr-note)を render するのは pr_body() 内(L150)であり、note 内の __TOKEN__ は配布前に検出されません。現在の pr-note には動的トークンがないため実害はありませんが、将来のカラーが note 内にトークンを使う場合に見落としが生じます。

# 将来の拡張で問題になりうる箇所
if [[ -f "$NOTE_FILE" ]]; then
  leftover_note=$(render "$NOTE_FILE" | grep -oE '__[A-Za-z0-9_]+__' | sort -u | tr '\n' ' ' || true)
  [[ -z "${leftover_note// /}" ]] || die "pr-note '${CALLER}' has unsubstituted tokens: ${leftover_note}..."
fi

現時点でのブロッカーではなく、将来カラーを追加する際の注意点として。Fix this →

[INFO] ((expr)) && cmd パターンの可読性

((${#VARS[@]}))     && ORDERED+=("${VARS[@]}")
((${#DEFAULTS[@]})) && ORDERED+=("${DEFAULTS[@]}")

set -euo pipefail 下でも && の左辺は終了コード 1 でスクリプトを落とさないため機能的には問題ありません。ただし一般読者には [[ ${#VARS[@]} -gt 0 ]] の方が意図が明確です。shellcheck 通過済みとのことなので現状のままでも可。

[INFO] --var REF=x--ref y を上書きする

CLI --varORDERED の先頭に積まれるため、--var REF=foo --ref bar を渡すと foo が勝ちます。一貫したCLI優先ルールではありますが、--ref が明示的なref設定インターフェースである以上、ドキュメントに一言あると親切です(現時点でバグではない)。


まとめ

前回レビューの主要指摘(sed 特殊文字・未置換検出・defaults・エラー継続)はすべて適切に対処されています。残る指摘は LOW/INFO レベルで、マージブロッカーはありません。設計の明確さと安全性が増しており、このままマージ可と評価します。

Follow-up to the @claude re-review:
- [LOW] Extend the unsubstituted-__TOKEN__ check to the caller's pr-note,
  not just the template, so a future caller with tokens in its note can't
  ship them unrendered.
- [INFO] README: note that --var overrides --ref/--model/--language
  (e.g. --var REF=x beats --ref).
@toshi0806

Copy link
Copy Markdown
Member Author

再レビュー指摘への対応(commit 20c8a79

@claude 再レビューの LOW/INFO 指摘に対応しました。

[LOW] pr-note の未置換トークンが未検査

配布前の __TOKEN__ 検証を $TEMPLATE だけでなく $NOTE_FILE(pr-note)も対象に拡張。将来 caller が note 内にトークンを使っても、未置換のまま配布されることを防ぎます。

for f in "$TEMPLATE" "$NOTE_FILE"; do
  [[ -f "$f" ]] || continue
  leftover=$(render "$f" | grep -oE '__[A-Za-z0-9_]+__' | sort -u | tr '\n' ' ' || true)
  [[ -z "${leftover// /}" ]] || die "$(basename "$f") has unsubstituted tokens: ..."
done
  • 検証: pr-note に __BOGUS__ を注入 → error: claude-mention.pr-note.md has unsubstituted tokens: __BOGUS__(配布前 die)✅

[INFO] --var の優先順位

README の --var 行に「--ref/--model/--language より優先(--var REF=x--ref を上書き)」と明記。

[INFO] ((expr)) && cmd の可読性

shellcheck クリーン・機能問題なしのため現状維持(指摘のとおり可)。

bash -n / shellcheck クリーン。これで再レビューの全指摘(LOW/INFO 含む)に対応済みです。

@github-actions github-actions 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.

このPRは、ワークフロー配布スクリプトを汎用化する素晴らしいリファクタリングです。特定のワークフロー(Claudeレビューなど)に依存しない設計にすることで、将来的に新しい共有ワークフローを追加する際の柔軟性と保守性が大幅に向上します。caller固有のロジック(テンプレート、デフォルト値、PR注記)がscripts/callers/ディレクトリに集約され、スクリプト本体から分離されたことで、コードの関心事が明確に分かれ、非常に読みやすくなりました。

for f in "$TEMPLATE" "$NOTE_FILE"; do
[[ -f "$f" ]] || continue
leftover=$(render "$f" | grep -oE '__[A-Za-z0-9_]+__' | sort -u | tr '\n' ' ' || true)
[[ -z "${leftover// /}" ]] || die "$(basename "$f") has unsubstituted tokens: ${leftover}(provide them via --var / --model / --language or a ${CALLER}.defaults file)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] 配布前にテンプレートやPR注記に未置換の__TOKEN__が残っていないかチェックする機能は、非常に堅牢な安全対策です。これにより、不完全なワークフローが誤って配布されることを防ぎ、汎用スクリプトとしての信頼性を高めています。

for kv in "${ORDERED[@]}"; do
key="${kv%%=*}"; val="${kv#*=}"
[[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || die "invalid token key: '$key'"
esc=$(printf '%s' "$val" | sed -e 's/[\\&|]/\\&/g')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] --varオプションで指定される値に含まれる特殊文字(\&|)がsedで適切にエスケープされるように処理されている点は、非常に堅牢な設計です。これにより、任意の値を安全に置換できるため、スクリプトの汎用性が高まります。

Comment thread scripts/README.md
| `--list-candidates` | — | 非アーカイブの smkwlab リポジトリを表示して終了 |
| `-h`, `--help` | — | ヘルプ |

### 新しい共有ワークフローを配布対象に追加するには

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✨ [POSITIVE] scripts/README.mdに「新しい共有ワークフローを配布対象に追加するには」というセクションが追加されたことで、将来的に新しいワークフローを追加する際のプロセスが明確になり、非常に分かりやすくなりました。これは、この汎用化されたスクリプトの利用を促進する上で重要な改善です。

@toshi0806 toshi0806 merged commit f953626 into main Jun 8, 2026
1 check passed
@toshi0806 toshi0806 deleted the generalize-distributor branch June 8, 2026 03:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant