From 0f451dd33abaff671461c50c409be38039d9ede5 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Wed, 12 Aug 2026 14:58:47 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat(#34):=20git-flow=20Skill=20=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #34 のうち git-flow 部分に対応。featureブランチ作成〜developへの PR作成の手順を .claude/skills/git-flow/SKILL.md に定義し、誰が呼び出しても 同じブランチ命名・PRタイトル/本文になるようにする。 - .gitignore: !.claude/skills/ を追加し SKILL.md を追跡対象にする - .claude/skills/git-flow/SKILL.md: featureブランチ作成、developへのPR 作成の手順とテンプレートを定義(develop→main同期は対象外、別途手動) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 71 ++++++++++++++++++++++++++++++++ .gitignore | 1 + 2 files changed, 72 insertions(+) create mode 100644 .claude/skills/git-flow/SKILL.md diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md new file mode 100644 index 0000000..7945b25 --- /dev/null +++ b/.claude/skills/git-flow/SKILL.md @@ -0,0 +1,71 @@ +--- +name: git-flow +description: >- + 対応するGitHub Issueからfeatureブランチを作成し、developへのPRを作成する際に使う。 + 「Issueブランチを作って」「#Nのブランチを切って」「PRを出して」等の指示で使う。 + develop→mainの同期PRはこのSkillの対象外。 +allowed-tools: Bash(git:*), Bash(gh:*) +--- + +# git-flow + +このリポジトリ(bobtabo/authorization-showcase)のブランチ運用を統一する手順書。 + +## 前提 + +- 対応するGitHub Issue(番号 N)が既に存在すること。Issue番号を伴わないブランチ名 + (例: `feature/xxx-yyy`)は作らない。Issueがまだ無い場合は、既存Issueの書式 + (`[showcase] : <日本語要約>`、`## 背景` → `## タスク` の構成)に合わせて + 先にIssueを作成してから本Skillを使う。 +- 派生元ブランチは既定で `develop`。呼び出し時に別ブランチが明示された場合は + そちらを派生元として使う(例: hotfixを `main` から切る等、たまにある例外に対応)。 + +## 1. featureブランチ作成 + +```bash +BASE=develop # 明示指定があればそちらに置き換える +git checkout "$BASE" +git pull origin "$BASE" +gh issue view --repo bobtabo/authorization-showcase --json title -q .title +git checkout -b feature/issue- +``` + +## 2. developへのPR作成 + +- base: `develop` / head: `feature/issue-`(派生元を変えた場合もbaseは`develop`) +- タイトル: `(#): <日本語要約>` + (type: feat/fix/docs/chore/refactor 等、conventional commit風。Issueのtypeを踏襲する) +- 本文テンプレート: + + ```markdown + ## Summary + + Issue # 対応。<変更内容の要約> + + ## Changes + - <変更ファイル/内容> + + Closes # + + 🤖 Generated with [Claude Code](https://claude.com/claude-code) + ``` + + Issueの一部のみ対応する場合(Issueの残タスクが残る場合)は `Closes #` ではなく + `Refs #` とし、Issueを自動クローズさせない。 + +- コマンド: + + ```bash + git push -u origin feature/issue- + gh pr create --repo bobtabo/authorization-showcase \ + --base develop --head feature/issue- \ + --title "(#): <要約>" --body "$(cat <<'EOF' + ... + EOF + )" + ``` + +## 禁止事項 + +- `develop` / `main` への直接push(featureブランチ経由のPRを必ず通す) +- `develop` → `main` のマージはこのSkillでは行わない(「develop → main 同期」PRとして別途手動で作成する) diff --git a/.gitignore b/.gitignore index 82fae1d..e64debc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ docker/local/.env .claude/* !.claude/settings.json !.claude/hooks/ +!.claude/skills/ .claude/settings.local.json /backends/go-gin/tmp/ /backends/java-springboot/.gradle/ From 65d008a922eda6f83e48983ffa58508f950dd681 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Wed, 12 Aug 2026 17:34:12 +0900 Subject: [PATCH 02/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88issue=E6=A4=9C=E8=A8=BC=E3=81=AE?= =?UTF-8?q?=E5=A4=B1=E6=95=97=E5=81=9C=E6=AD=A2/--ff-only/PR=E6=9C=AC?= =?UTF-8?q?=E6=96=87=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC=E3=83=BC=E3=83=88?= =?UTF-8?q?=E5=B1=95=E9=96=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gh issue view 失敗時にブランチ作成へ進まないよう set -euo pipefail を追加し、 Issue番号を変数化してクォート - git pull を --ff-only にし、develop/BASEの分岐時に自動マージしないようにする - gh pr create の --body に省略記号(...)ではなくテンプレート本文をそのまま記載 - 保護ブランチへのpushを権限層で強制する仕組みが無い点を「既知の制約」として明記 (guard-bash.sh拡張やブランチ保護ルールの導入はIssue #34本文の記載通りスコープ外、 必要なら別Issue化) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 38 ++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index 7945b25..2462726 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -23,13 +23,20 @@ allowed-tools: Bash(git:*), Bash(gh:*) ## 1. featureブランチ作成 ```bash +set -euo pipefail BASE=develop # 明示指定があればそちらに置き換える +N= # 対応するIssue番号 + git checkout "$BASE" -git pull origin "$BASE" -gh issue view --repo bobtabo/authorization-showcase --json title -q .title -git checkout -b feature/issue- +git pull --ff-only origin "$BASE" +gh issue view "$N" --repo bobtabo/authorization-showcase --json title -q .title +git checkout -b "feature/issue-$N" ``` +`set -euo pipefail` により、Issueが存在せず `gh issue view` が失敗した場合は +`git checkout -b` を実行せずに停止する。`git pull` は `--ff-only` とし、 +ローカルとリモートが分岐している場合は自動マージせず停止する。 + ## 2. developへのPR作成 - base: `develop` / head: `feature/issue-`(派生元を変えた場合もbaseは`develop`) @@ -53,14 +60,23 @@ git checkout -b feature/issue- Issueの一部のみ対応する場合(Issueの残タスクが残る場合)は `Closes #` ではなく `Refs #` とし、Issueを自動クローズさせない。 -- コマンド: +- コマンド(本文には上記テンプレートの内容をそのまま埋め込む。省略記号で済ませない): ```bash - git push -u origin feature/issue- + git push -u origin "feature/issue-$N" gh pr create --repo bobtabo/authorization-showcase \ - --base develop --head feature/issue- \ + --base develop --head "feature/issue-$N" \ --title "(#): <要約>" --body "$(cat <<'EOF' - ... + ## Summary + + Issue # 対応。<変更内容の要約> + + ## Changes + - <変更ファイル/内容> + + Closes # + + 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )" ``` @@ -69,3 +85,11 @@ git checkout -b feature/issue- - `develop` / `main` への直接push(featureブランチ経由のPRを必ず通す) - `develop` → `main` のマージはこのSkillでは行わない(「develop → main 同期」PRとして別途手動で作成する) + +## 既知の制約 + +- 本SkillはSKILL.mdという「明示的に呼び出したときの手順書」であり、`allowed-tools` や + 現状の `.claude/hooks/guard-bash.sh` は `develop` / `main` への直接pushを機械的には + ブロックしない(`guard-bash.sh` が対象とするのは `push --force` / `reset --hard` 等の + 破壊的操作のみ)。保護ブランチへのpushを権限層で強制したい場合は、`guard-bash.sh` の + 拡張または GitHub 側のブランチ保護ルールの設定を別Issueとして検討する。 From 217a183e9a1570fe741bb7ba1df052880e0ce388 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Thu, 13 Aug 2026 05:32:52 +0900 Subject: [PATCH 03/18] =?UTF-8?q?fix:=20CodeRabbit=E5=86=8D=E6=8C=87?= =?UTF-8?q?=E6=91=98=E5=AF=BE=E5=BF=9C=EF=BC=88N=3D=E3=81=AEbash?= =?UTF-8?q?=E6=A7=8B=E6=96=87=E3=82=A8=E3=83=A9=E3=83=BC/PR=E4=BD=9C?= =?UTF-8?q?=E6=88=90=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=AEpipefail?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - N= はbash構文エラーになるため、N=34(実行前に置き換える旨を明記)に変更 - gh pr create のコマンドブロックにも set -euo pipefail を追加 - PR本文テンプレート内のClosesに、部分対応時はRefsへ置き換える旨のコメントを追記 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index 2462726..d99d80a 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -25,7 +25,8 @@ allowed-tools: Bash(git:*), Bash(gh:*) ```bash set -euo pipefail BASE=develop # 明示指定があればそちらに置き換える -N= # 対応するIssue番号 +N=34 # 対応するIssue番号に置き換える(実行前に書き換える。以下このSKILL.mdでは + # を「この値に置き換える」プレースホルダーとして表記する) git checkout "$BASE" git pull --ff-only origin "$BASE" @@ -63,6 +64,7 @@ git checkout -b "feature/issue-$N" - コマンド(本文には上記テンプレートの内容をそのまま埋め込む。省略記号で済ませない): ```bash + set -euo pipefail git push -u origin "feature/issue-$N" gh pr create --repo bobtabo/authorization-showcase \ --base develop --head "feature/issue-$N" \ @@ -75,6 +77,7 @@ git checkout -b "feature/issue-$N" - <変更ファイル/内容> Closes # + 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF From b63b63486ce76b89b76a996fe4c21a7bccf8c1d9 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Thu, 13 Aug 2026 18:22:51 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat(#34):=20=E6=AE=8B=E3=82=8A5=E3=81=A4?= =?UTF-8?q?=E3=81=AEClaude=20Code=20Skills=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #34の残タスクに対応。git-flowに続き、以下5つのSkillを追加し、 Issue #34で洗い出した候補を全て揃える。 - .claude/skills/backend-dispatch/SKILL.md: 言語名だけの曖昧な指示を backends/配下の正しいディレクトリとdocker/bin/docker-*.shに振り分ける - .claude/skills/new-backend-scaffold/SKILL.md: go-ginを雛形に、新規 バックエンド追加時の構成(README/.env.example/docker-compose.yml/ docker/bin/CI workflow/各READMEの更新箇所)を揃える - .claude/skills/docker-ops/SKILL.md: docker/bin/docker-*.shの起動順序・ 破壊的操作の注意・コンテナ未起動時のトラブルシュート - .claude/skills/demo-recording/SKILL.md: e2e/でのPlaywrightデモGIF 録画手順(npm run record → npm run to-gif) - .claude/skills/backend-ci-trigger/SKILL.md: 各バックエンドCIが paths限定+feature/issue-*除外のため自動実行されない点と、 workflow_dispatchでの手動発火・結果確認手順 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 70 ++++++++++++ .claude/skills/backend-dispatch/SKILL.md | 54 +++++++++ .claude/skills/demo-recording/SKILL.md | 42 +++++++ .claude/skills/docker-ops/SKILL.md | 82 ++++++++++++++ .claude/skills/new-backend-scaffold/SKILL.md | 113 +++++++++++++++++++ 5 files changed, 361 insertions(+) create mode 100644 .claude/skills/backend-ci-trigger/SKILL.md create mode 100644 .claude/skills/backend-dispatch/SKILL.md create mode 100644 .claude/skills/demo-recording/SKILL.md create mode 100644 .claude/skills/docker-ops/SKILL.md create mode 100644 .claude/skills/new-backend-scaffold/SKILL.md diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md new file mode 100644 index 0000000..9f834d8 --- /dev/null +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -0,0 +1,70 @@ +--- +name: backend-ci-trigger +description: >- + featureブランチでバックエンドの変更をpushしたあと、対応するCIが自動実行 + されないため手動でCIを発火・確認する際に使う。「CIを回して」「CIの結果を + 確認して」等の指示で使う。 +allowed-tools: Bash(gh:*) +--- + +# backend-ci-trigger + +各バックエンドCI(`.github/workflows/*-ci.yml`)は次の2点により、featureブランチへの +pushでは**自動実行されない**。 + +1. `paths` トリガーが対応するバックエンドディレクトリのみに限定されている +2. `branches-ignore: ['feature/issue-*']` により、`feature/issue-*` へのpush自体が + トリガー対象外 + +そのため `feature/issue-N` ブランチ上でCI結果を確認したい場合は、`workflow_dispatch` +で手動発火する。develop/main へのpush時は該当pathsに応じて自動実行される。 + +## ワークフロー対応表 + +| ワークフローファイル | 対象パス | +|---|---| +| `go-gin-ci.yml` | `backends/go-gin/**` | +| `java-springboot-ci.yml` | `backends/java-springboot/**` | +| `php-cakephp-ci.yml` | `backends/php-cakephp/**` | +| `php-codeigniter-ci.yml` | `backends/php-codeigniter/**` | +| `php-fuelphp-ci.yml` | `backends/php-fuelphp/**` | +| `python-django-ci.yml` | `backends/python-django/**` | +| `ruby-rails-ci.yml` | `backends/ruby-rails/**` | + +`gh workflow run` にはワークフロー表示名(日本語・括弧付き)ではなく、上記の +ファイル名を使う(曖昧さがなく確実)。 + +## 発火対象の判断 + +変更したファイルパスから、対応するバックエンドのワークフローを特定する。 +複数バックエンドにまたがる変更(例: 共通ドキュメントやCI設定自体の変更)は、 +影響するワークフローをそれぞれ個別に発火する(一括発火するコマンドは無い)。 + +```bash +git diff --name-only develop... | sed -n 's#^backends/\([^/]*\)/.*#\1#p' | sort -u +# 出力された各ディレクトリ名を上表と突き合わせて対象ワークフローを決定する +``` + +## 手動発火と結果確認 + +```bash +gh workflow run <ワークフローファイル> --repo bobtabo/authorization-showcase --ref feature/issue- + +# 実行中/直近のrunを確認 +gh run list --repo bobtabo/authorization-showcase \ + --workflow=<ワークフローファイル> --branch=feature/issue- --limit 1 + +# 完了まで待ってログを見る +gh run watch --repo bobtabo/authorization-showcase +gh run view --repo bobtabo/authorization-showcase --log +``` + +## 注意 + +- 全バックエンドCIは同一のngrokトンネル/LocalStackを共有しており、 + `concurrency: group: shared-authserver-tunnel, cancel-in-progress: false` + で直列実行される。複数ワークフローを続けて発火すると、後発のものは前段の + 完了待ちで少し時間がかかる。 +- develop/main へのマージ前にfeatureブランチ側でCIグリーンを確認したい場合は、 + このSkillで対象ワークフローを手動発火してから確認する(git-flow SkillでPRを + 作成しても、featureブランチ自体のpushではCIは動かない点に注意)。 diff --git a/.claude/skills/backend-dispatch/SKILL.md b/.claude/skills/backend-dispatch/SKILL.md new file mode 100644 index 0000000..81341cc --- /dev/null +++ b/.claude/skills/backend-dispatch/SKILL.md @@ -0,0 +1,54 @@ +--- +name: backend-dispatch +description: >- + 「Goのバックエンドで◯◯を直して」のように言語名だけで指示された際に、正しい + backends/ 配下のディレクトリと docker/bin/docker-*.sh のラッパースクリプトに + 振り分ける際に使う。曖昧なPHP指示(CakePHP/CodeIgniter/FuelPHPのどれか不明)の + ときにも使う。 +allowed-tools: Bash(docker:*), Bash(git:*) +--- + +# backend-dispatch + +このリポジトリは同一の認可サーバーAPI(JWT発行/検証)を7言語・FWで実装するショーケース。 +どのバックエンドを指しているかを機械的に確定させ、実行は必ずDockerラッパースクリプト +経由で行う。 + +## 対応表 + +| 呼ばれ方の例 | ソースディレクトリ | Docker操作 | execで入るシェル | +|---|---|---|---| +| Go, Gin | `backends/go-gin/` | `docker/bin/docker-go.sh` | `sh` | +| Java, Spring Boot | `backends/java-springboot/` | `docker/bin/docker-java.sh` | `bash` | +| CakePHP, Cake | `backends/php-cakephp/` | `docker/bin/docker-php-cake.sh` | `bash` | +| CodeIgniter, CI | `backends/php-codeigniter/` | `docker/bin/docker-php-codeigniter.sh` | `bash` | +| FuelPHP, Fuel | `backends/php-fuelphp/` | `docker/bin/docker-php-fuel.sh` | `bash` | +| Python, Django | `backends/python-django/` | `docker/bin/docker-python.sh` | `bash` | +| Ruby, Rails | `backends/ruby-rails/` | `docker/bin/docker-ruby.sh` | `bash` | + +- 「PHPのバックエンドで」のように3フレームワークのどれか特定できない指示を受けた場合は、 + 推測で決めずに CakePHP / CodeIgniter / FuelPHP のどれかをユーザーに確認する。 +- 「全バックエンドで」「7言語すべてで」という指示は、上記7ディレクトリ全てに同じ変更を + 展開する。一括Docker操作は `docker/bin/docker-backends.sh {up|down}` を使う + (docker-ops Skill参照)。 + +## 実行方針 + +- **ソース編集**: `backends//` はDockerコンテナへbind mountされているため、 + ホスト側で直接編集してよい(Edit/Writeツールで通常通り編集する)。 +- **テスト・ビルド・CLI実行**: 必ずコンテナ内で行う。ホストに言語ランタイムが + 入っていても、ホストで直接 `go test` 等を実行しない。 + ```bash + docker/bin/docker-.sh exec + # コンテナ内シェルに入った状態で、通常のテスト/ビルドコマンドを実行する + ``` + 非対話的に1コマンドだけ実行したい場合は、対応する `docker/local/app-/docker-compose.yml` + を直接指定する(`docker exec <コンテナ名>` のようにコンテナ名を直書きしない。 + プロジェクト名・サービス名がバックエンドごとに異なるため取り違えやすい): + ```bash + docker compose -p showcase- -f docker/local/app-/docker-compose.yml \ + exec --user 1000 <コマンド> + ``` + (`` は `go` / `java` / `php-cake` / `php-codeigniter` / `php-fuel` / `python` / `ruby`、 + `` は go=`go`, java=`java`, php系=`php`, python=`python`, ruby=`rb-rails`) +- コンテナが起動していない場合の対処は docker-ops Skillを参照。 diff --git a/.claude/skills/demo-recording/SKILL.md b/.claude/skills/demo-recording/SKILL.md new file mode 100644 index 0000000..7ae07e5 --- /dev/null +++ b/.claude/skills/demo-recording/SKILL.md @@ -0,0 +1,42 @@ +--- +name: demo-recording +description: >- + e2e/ ディレクトリで Playwright を使った動作デモGIFを録画する際に使う。 + 「デモGIFを録って」「動作を録画して」等の指示で使う。 +allowed-tools: Bash(npm:*), Bash(cd:*) +--- + +# demo-recording + +`e2e/` はE2Eテストではなく、動作デモGIF録画専用のディレクトリ(`frontend/` とは分離、 +CIには組み込まれない・手動実行のみ)。 + +## 前提 + +- `ffmpeg` がインストール済み(`brew install ffmpeg`) +- Playwright ブラウザをインストール済み(`npx playwright install chromium`、 + `e2e/` 配下で実行) +- `frontend/` の開発サーバーが `http://localhost:5173` で起動していること + (フロントの起動は別途 `npm run dev` を `frontend/` で実行しておく。このSkillの + 対象外) + +## 手順 + +```bash +cd e2e +npm install +npm run record # playwright test --project=chromium を実行。output/*.webm を生成 +npm run to-gif # scripts/to-gif.sh でwebm→GIF変換。docs/gif/*.gif を生成 +``` + +## 新規デモシナリオの追加 + +新しいシナリオを録画したい場合は `e2e/scenarios/` に `*.spec.ts` を追加する +(既存の `e2e/scenarios/jwt-flow.spec.ts` を参考にする)。シナリオ追加後に +上記の `npm run record` → `npm run to-gif` を実行する。 + +## 生成物の扱い + +- `e2e/output/`(録画中間ファイルの `.webm`)は `.gitignore` 対象。コミットしない。 +- `docs/gif/*.gif`(変換後のGIF)はREADME等に埋め込んで使うため、リポジトリに + 含める(コミット対象)。 diff --git a/.claude/skills/docker-ops/SKILL.md b/.claude/skills/docker-ops/SKILL.md new file mode 100644 index 0000000..2001bcc --- /dev/null +++ b/.claude/skills/docker-ops/SKILL.md @@ -0,0 +1,82 @@ +--- +name: docker-ops +description: >- + docker/bin/docker-*.sh の使い方(env/up/down/exec 等)でコンテナを起動・停止・ + 操作する際、またはコンテナが起動しない等のトラブル対応をする際に使う。 +allowed-tools: Bash(docker:*), Bash(cd:*), Bash(find:*) +--- + +# docker-ops + +`docker/bin/*.sh` は各バックエンド・共通インフラのDocker操作を統一するラッパー +スクリプト。`docker` / `docker compose` を直接叩かず、必ずこれらを経由する +(`guard-bash.sh` が `docker stop` / `docker compose down` 等の直接実行をブロックし、 +本Skillのラッパー使用へ誘導する設計と対応している)。 + +## 前提(初回のみ) + +```bash +cd docker +find ./bin -type f -exec chmod 755 {} + +bin/docker-common.sh env # 証明書・.envのセットアップ +``` + +## 起動順序 + +Docker ネットワーク `showcase` と nginx-proxy を作る `common` を先に起動し、 +その後で各バックエンドを起動する。順序を逆にすると起動に失敗する。 + +```bash +cd docker +bin/docker-common.sh up # 1. 共通コンテナ(nginx-proxy, showcaseネットワーク作成) +bin/docker-backends.sh up # 2. 全バックエンド一括起動 +# もしくは個別に: +bin/docker-go.sh up +bin/docker-java.sh up +bin/docker-php-cake.sh up +bin/docker-php-codeigniter.sh up +bin/docker-php-fuel.sh up +bin/docker-python.sh up +bin/docker-ruby.sh up +``` + +各 `docker-.sh up` は `.env` が無ければ `.env.example` から自動生成してから +`docker compose -p showcase- up -d --build --force-recreate` を実行する。 + +## コンテナに入る/破棄する + +```bash +docker/bin/docker-.sh exec # コンテナ内シェルに入る(Goはsh、他はbash) +docker/bin/docker-.sh down # そのバックエンドのコンテナ・イメージ・ボリュームを破棄 +docker/bin/docker-backends.sh down # 全バックエンド一括破棄 +``` + +`common` の操作は `up` / `down` に加えて `start` / `stop`(コンテナ破棄せず +起動・停止のみ)も使える: + +```bash +docker/bin/docker-common.sh start +docker/bin/docker-common.sh stop +``` + +## 破壊的操作への注意 + +- `down` は対象コンテナに加えて **イメージ・ボリュームも削除**する + (`--rmi all --volumes`)。 +- `docker-common.sh down` はさらに `docker/local/common/data/` と + `docker/local/common/logs/` ディレクトリも削除する。 +- 上記のためユーザーの明示的な指示なしに `down` を実行しない。コンテナの + 再起動だけで済む場合は `common` は `stop`→`start`、各バックエンドは + `up`(`--force-recreate` で再作成される)を使う。 + +## トラブルシュート + +- **バックエンドコンテナが起動しない**: `common` が起動しておらず `showcase` + ネットワークが無い可能性が高い。`docker/bin/docker-common.sh up` を先に実行する。 +- **状態確認**: `docker ps` でコンテナ名 `showcase-_` + (例: `showcase-go_go`, `showcase-go_nginx`)が上がっているか確認する。 +- **.env が古い/壊れている**: バックエンド側の `.env` は `docker/local/app-/.env` + にあり、削除して `docker/bin/docker-.sh up` を再実行すれば + `.env.example` から再生成される。 +- どのバックエンドがどのディレクトリ・スクリプトに対応するかは backend-dispatch + Skillを参照。 diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md new file mode 100644 index 0000000..7d35aed --- /dev/null +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -0,0 +1,113 @@ +--- +name: new-backend-scaffold +description: >- + 新しい言語/フレームワークのバックエンドをこのリポジトリに追加する際に使う。 + 既存7バックエンド(Go/Java/PHP×3/Python/Ruby)と同じ構成(README/.env.example/ + docker-compose.yml/CIワークフロー)で雛形を揃える。 +allowed-tools: Bash(git:*), Bash(cp:*), Bash(mkdir:*) +--- + +# new-backend-scaffold + +新規バックエンド ``(例: `node-express`)を、既存実装と同じ構成で追加する +手順。最小構成の `go-gin` を雛形として使う(アプリコンテナ1つ + nginxの2サービス +構成で、他バックエンドも基本的に同じ形)。 + +## 前提 + +- 事前に対応するGitHub Issueを作成し、git-flow Skillで `feature/issue-N` + ブランチを作成しておく。 + +## 1. バックエンドソース(`backends//`) + +```bash +mkdir -p backends/ +``` + +- アプリのソース一式を配置する。 +- `backends//README.md` を作成する(`backends/go-gin/README.md` の構成 + ―概要/起動方法/エンドポイント一覧―に合わせる)。 + +## 2. Docker実行環境(`docker/local/app-/`) + +`docker/local/app-go/` を丸ごとコピーして書き換える。 + +```bash +cp -r docker/local/app-go docker/local/app- +``` + +書き換える点: + +- `docker-compose.yml` + - `name: showcase-app-` + - サービス名・イメージのbuild文脈(`go/` ディレクトリ相当)を言語用に差し替え + - アプリ側の `volumes` を `./../../../backends/:/var/www/${APP_NAME}` に + - `networks.showcase.external: true` はそのまま維持(`common` が作るネットワーク + に参加する) + - `nginx` サービス定義(`VIRTUAL_HOST` / `VIRTUAL_PORT` 等の環境変数連携)は + ほぼそのまま流用可 +- `.env.example` + - `APP_NAME=app-` + - `SERVER_NAME=apis.showcase-.dev` + - `NGINX_CONTAINER=showcase-_nginx` + - 言語コンテナの `_CONTAINER=showcase-_` を追加 + - デバッグポート等、言語固有の変数を追加 +- `nginx/`(`Dockerfile` / `nginx.conf` / `templates/default.conf.template`)は + ほぼそのまま流用できる(`APP_PATH` 等の環境変数経由でルーティングするため) + +## 3. Dockerラッパースクリプト(`docker/bin/docker-.sh`) + +`docker/bin/docker-go.sh` をコピーして書き換える。 + +```bash +cp docker/bin/docker-go.sh docker/bin/docker-.sh +chmod 755 docker/bin/docker-.sh +``` + +書き換える点: `cd` 先を `app-`、`-p showcase-`、`exec` で入る +サービス名・シェル(bash推奨、必要ならsh)。 + +さらに `docker/bin/docker-backends.sh` の `run()` に1行追加する: + +```bash +bash "${SCRIPT_DIR}/docker-.sh" "${ARG}" +``` + +## 4. CIワークフロー(`.github/workflows/-ci.yml`) + +`.github/workflows/go-gin-ci.yml` をコピーして書き換える。 + +```bash +cp .github/workflows/go-gin-ci.yml .github/workflows/-ci.yml +``` + +書き換える点: +- `paths: - 'backends//**'` +- `branches-ignore: ['feature/issue-*']` と `concurrency.group: + shared-authserver-tunnel` は変更しない(全バックエンド共有のngrok/LocalStack + 輻輳防止のため) +- 言語ツールチェインのセットアップステップ(`actions/setup-go` 相当) +- `Resolve AUTH_SERVER_URL` ステップは他バックエンドと同一ロジックのため、 + そのまま流用する(LocalStackのAPI Gateway IDをngrok経由で動的取得する仕組み) +- テスト実行コマンド・`working-directory: backends/` のみ差し替え + +featureブランチ上でこのCIを確認する方法は backend-ci-trigger Skillを参照 +(`workflow_dispatch` での手動発火が必要)。 + +## 5. ドキュメント更新 + +- `backends/README.md`: 実装スタック一覧の表に1行追加(状態は `🚧 予定` または + `✅ 完了`) +- ルート `README.md`: + - 「システム構造」のツリー(`backends/` 配下の一覧)に追加 + - 「6. バックエンドの初期設定」に `6.x ` の節を追加 + +## 6. 動作確認 + +```bash +docker/bin/docker-.sh up +docker/bin/docker-.sh exec # コンテナに入り疎通確認 +``` + +CIの手動発火・確認は backend-ci-trigger Skillを参照。ここまで確認できたら +git-flow Skillでdevelopへのpullリクエストを作成する。 From 26ff432525dfe5d0d7f15483345268ad3c4746da Mon Sep 17 00:00:00 2001 From: bobtabo Date: Thu, 13 Aug 2026 22:25:03 +0900 Subject: [PATCH 05/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88Skill=E9=96=93=E3=81=AE=E7=9F=9B?= =?UTF-8?q?=E7=9B=BE/=E6=89=8B=E9=A0=86=E9=A0=86=E5=BA=8F/=E5=89=8D?= =?UTF-8?q?=E6=8F=90=E6=9D=A1=E4=BB=B6=E3=81=AE=E6=98=8E=E8=A8=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docker-ops: execは guard-bash.sh のブロック対象外である旨を明記し、 backend-dispatchが案内する直接 docker compose exec との矛盾を解消 - backend-ci-trigger: workflow_dispatchはdefaultブランチに存在する ワークフローのみ発火可能な制約を明記。git diffの比較元をBASE変数化 してgit-flowの派生元ブランチ変更に追従 - demo-recording / e2e/README.md: npm install の前に playwright install すると未固定バージョンを取得してしまう順序ミスを修正 (npm install → npm exec -- playwright install chromium の順に) - new-backend-scaffold: 新規バックエンド追加時に他3 Skill (backend-dispatch/backend-ci-trigger/docker-ops)への登録が必要な旨と 確認コマンドを追加。動作確認前にdocker-common.sh upが必要な旨を明記 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 13 ++++++++++-- .claude/skills/demo-recording/SKILL.md | 8 ++++++-- .claude/skills/docker-ops/SKILL.md | 11 +++++++--- .claude/skills/new-backend-scaffold/SKILL.md | 21 ++++++++++++++++++-- e2e/README.md | 6 +++++- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index 9f834d8..2b63db1 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -17,7 +17,15 @@ pushでは**自動実行されない**。 トリガー対象外 そのため `feature/issue-N` ブランチ上でCI結果を確認したい場合は、`workflow_dispatch` -で手動発火する。develop/main へのpush時は該当pathsに応じて自動実行される。 +で手動発火する。`feature/issue-*` **以外**のブランチ(develop/main に限らない)への +pushでは、該当pathsに応じて自動実行される。 + +`workflow_dispatch` は、対象のワークフローファイルがdefaultブランチ(`main`)に +既に存在している場合のみ使える。new-backend-scaffold Skillで新規追加したワークフロー +ファイルは、`main` へマージされるまで `gh workflow run` で手動発火できない +(GitHubがdispatch可否をdefaultブランチ上の定義で判定するため)。この場合は、 +develop→main同期後に確認するか、ワークフローファイルのみ先にdevelopおよびmainへ +反映しておく。 ## ワークフロー対応表 @@ -41,7 +49,8 @@ pushでは**自動実行されない**。 影響するワークフローをそれぞれ個別に発火する(一括発火するコマンドは無い)。 ```bash -git diff --name-only develop... | sed -n 's#^backends/\([^/]*\)/.*#\1#p' | sort -u +BASE=develop # git-flowでこのfeatureブランチの派生元とした基準ブランチに合わせる +git diff --name-only "$BASE"... | sed -n 's#^backends/\([^/]*\)/.*#\1#p' | sort -u # 出力された各ディレクトリ名を上表と突き合わせて対象ワークフローを決定する ``` diff --git a/.claude/skills/demo-recording/SKILL.md b/.claude/skills/demo-recording/SKILL.md index 7ae07e5..1fa7914 100644 --- a/.claude/skills/demo-recording/SKILL.md +++ b/.claude/skills/demo-recording/SKILL.md @@ -14,17 +14,21 @@ CIには組み込まれない・手動実行のみ)。 ## 前提 - `ffmpeg` がインストール済み(`brew install ffmpeg`) -- Playwright ブラウザをインストール済み(`npx playwright install chromium`、 - `e2e/` 配下で実行) - `frontend/` の開発サーバーが `http://localhost:5173` で起動していること (フロントの起動は別途 `npm run dev` を `frontend/` で実行しておく。このSkillの 対象外) ## 手順 +Playwrightブラウザのインストールは、`package.json` に固定された +`@playwright/test` のバージョンを使うため、必ず `npm install` の**後**に行う +(`npm install` 前に `npx playwright install` を実行すると、レジストリから +バージョン固定されていない `playwright` を取得してしまう)。 + ```bash cd e2e npm install +npm exec -- playwright install chromium # 初回のみ。ブラウザバイナリを取得 npm run record # playwright test --project=chromium を実行。output/*.webm を生成 npm run to-gif # scripts/to-gif.sh でwebm→GIF変換。docs/gif/*.gif を生成 ``` diff --git a/.claude/skills/docker-ops/SKILL.md b/.claude/skills/docker-ops/SKILL.md index 2001bcc..a03407a 100644 --- a/.claude/skills/docker-ops/SKILL.md +++ b/.claude/skills/docker-ops/SKILL.md @@ -9,9 +9,14 @@ allowed-tools: Bash(docker:*), Bash(cd:*), Bash(find:*) # docker-ops `docker/bin/*.sh` は各バックエンド・共通インフラのDocker操作を統一するラッパー -スクリプト。`docker` / `docker compose` を直接叩かず、必ずこれらを経由する -(`guard-bash.sh` が `docker stop` / `docker compose down` 等の直接実行をブロックし、 -本Skillのラッパー使用へ誘導する設計と対応している)。 +スクリプト。コンテナの起動・停止・破棄(`up`/`down`/`start`/`stop`)は必ずこれらを +経由する(`guard-bash.sh` が `docker stop` / `docker compose stop|kill|down` 等の +直接実行をブロックし、本Skillのラッパー使用へ誘導する)。 + +一方、起動済みコンテナに対する非対話の単発コマンド実行(`exec`)は +`guard-bash.sh` のブロック対象外であり、`docker compose -p showcase- -f +docker/local/app-/docker-compose.yml exec --user 1000 <コマンド>` +を直接使ってよい(backend-dispatch Skill参照)。 ## 前提(初回のみ) diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index 7d35aed..13226b9 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -91,8 +91,10 @@ cp .github/workflows/go-gin-ci.yml .github/workflows/-ci.yml そのまま流用する(LocalStackのAPI Gateway IDをngrok経由で動的取得する仕組み) - テスト実行コマンド・`working-directory: backends/` のみ差し替え -featureブランチ上でこのCIを確認する方法は backend-ci-trigger Skillを参照 -(`workflow_dispatch` での手動発火が必要)。 +新規追加したワークフローファイルは `main` にマージされるまで `workflow_dispatch` +での手動発火ができない(GitHubの仕様。詳細は backend-ci-trigger Skillの当該 +注記を参照)。featureブランチ段階でのCI確認は、develop→main同期後まで待つか、 +ワークフローファイルのみ先に反映する必要がある。 ## 5. ドキュメント更新 @@ -101,10 +103,25 @@ featureブランチ上でこのCIを確認する方法は backend-ci-trigger Ski - ルート `README.md`: - 「システム構造」のツリー(`backends/` 配下の一覧)に追加 - 「6. バックエンドの初期設定」に `6.x ` の節を追加 +- 他Skillの登録先(更新を忘れるとSkill経由で新バックエンドを選択・起動・CI発火 + できない): + - `.claude/skills/backend-dispatch/SKILL.md` の対応表に1行追加 + - `.claude/skills/backend-ci-trigger/SKILL.md` のワークフロー対応表に1行追加 + - `.claude/skills/docker-ops/SKILL.md` の「起動順序」の個別列挙に + `docker/bin/docker-.sh up` を追加 + +上記4ファイルすべてに `` が登場することを最終確認する +(`grep -rl "" .claude/skills/ backends/README.md README.md` +で漏れがないか機械的にチェックできる)。 ## 6. 動作確認 +`showcase` ネットワークが無い(= `docker/bin/docker-common.sh up` を一度も +実行していない)クリーンな環境では、先に共通コンテナを起動しておく必要がある +(docker-ops Skill参照)。 + ```bash +docker/bin/docker-common.sh up # showcaseネットワーク・nginx-proxyが無ければ起動 docker/bin/docker-.sh up docker/bin/docker-.sh exec # コンテナに入り疎通確認 ``` diff --git a/e2e/README.md b/e2e/README.md index baa8663..208762a 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -17,14 +17,18 @@ e2e/ ## :white_check_mark: 前提条件 - ffmpeg がインストール済みであること(`brew install ffmpeg`) -- Playwright ブラウザのインストール: `npx playwright install chromium` - `frontend/` の開発サーバーが `http://localhost:5173` で起動していること ## :rocket: 使い方 +Playwright ブラウザのインストールは `package.json` に固定されたバージョンを +使うため、`npm install` の**後**に行ってください(先に実行すると、バージョン +固定されていない `playwright` をレジストリから取得してしまいます)。 + ```bash cd e2e npm install +npm exec -- playwright install chromium # 初回のみ npm run record # 録画(output/*.webm が生成される) npm run to-gif # GIF 変換(docs/gif/*.gif が生成される) ``` From 7bc65426514ffc180fc7362fec91f9b8ecfdfa81 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 14 Aug 2026 03:36:17 +0900 Subject: [PATCH 06/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88=E7=99=BB=E9=8C=B2=E6=BC=8F=E3=82=8C?= =?UTF-8?q?=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E7=AF=84=E5=9B=B2/?= =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E8=AA=AC=E6=98=8E?= =?UTF-8?q?=E3=81=AE=E6=AD=A3=E7=A2=BA=E5=8C=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - new-backend-scaffold: grepでの最終確認範囲をdocker-backends.shと CIワークフローファイルまで拡大し、対象7ファイルを個別に列挙 - demo-recording / e2e/README.md: 「package.jsonに固定」ではなく 「package-lock.jsonで解決されたバージョン」という正確な説明に修正し、 厳密な再現性が必要な場合はnpm ciを使う旨を追記 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/demo-recording/SKILL.md | 9 +++++---- .claude/skills/new-backend-scaffold/SKILL.md | 18 +++++++++++++++--- e2e/README.md | 6 +++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.claude/skills/demo-recording/SKILL.md b/.claude/skills/demo-recording/SKILL.md index 1fa7914..aceda2a 100644 --- a/.claude/skills/demo-recording/SKILL.md +++ b/.claude/skills/demo-recording/SKILL.md @@ -20,10 +20,11 @@ CIには組み込まれない・手動実行のみ)。 ## 手順 -Playwrightブラウザのインストールは、`package.json` に固定された -`@playwright/test` のバージョンを使うため、必ず `npm install` の**後**に行う -(`npm install` 前に `npx playwright install` を実行すると、レジストリから -バージョン固定されていない `playwright` を取得してしまう)。 +Playwrightブラウザのインストールは、`package-lock.json` で解決されたバージョン +(本リポジトリでは `playwright@1.61.1`)を使うため、必ず `npm install` の**後**に +行う(`npm install` 前に `npx playwright install` を実行すると、ローカルに無い +`playwright` をレジストリから取得してしまい、バージョンがずれる)。再現性を +厳密にしたい場合は `npm install` の代わりに `npm ci` を使う。 ```bash cd e2e diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index 13226b9..cf03c1e 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -110,9 +110,21 @@ cp .github/workflows/go-gin-ci.yml .github/workflows/-ci.yml - `.claude/skills/docker-ops/SKILL.md` の「起動順序」の個別列挙に `docker/bin/docker-.sh up` を追加 -上記4ファイルすべてに `` が登場することを最終確認する -(`grep -rl "" .claude/skills/ backends/README.md README.md` -で漏れがないか機械的にチェックできる)。 +以下7ファイルすべてに `` が登場することを最終確認する: +`docker/bin/docker-backends.sh`(手順3)、`.github/workflows/-ci.yml` +(手順4)、`backends/README.md` / `README.md`(本節)、 +`.claude/skills/{backend-dispatch,backend-ci-trigger,docker-ops}/SKILL.md` +(本節)。 + +```bash +grep -rl "" \ + docker/bin/docker-backends.sh \ + .github/workflows/-ci.yml \ + backends/README.md README.md \ + .claude/skills/backend-dispatch/SKILL.md \ + .claude/skills/backend-ci-trigger/SKILL.md \ + .claude/skills/docker-ops/SKILL.md +``` ## 6. 動作確認 diff --git a/e2e/README.md b/e2e/README.md index 208762a..7475c8d 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -21,9 +21,9 @@ e2e/ ## :rocket: 使い方 -Playwright ブラウザのインストールは `package.json` に固定されたバージョンを -使うため、`npm install` の**後**に行ってください(先に実行すると、バージョン -固定されていない `playwright` をレジストリから取得してしまいます)。 +Playwright ブラウザのインストールは `package-lock.json` で解決されたバージョンを +使うため、`npm install` の**後**に行ってください(先に実行すると、ローカルに +無い `playwright` をレジストリから取得してしまいバージョンがずれます)。 ```bash cd e2e From c818ca7987227211a21f19aa005ee92a435d4281 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 14 Aug 2026 16:38:13 +0900 Subject: [PATCH 07/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88=E3=83=97=E3=83=AC=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=83=9B=E3=83=AB=E3=83=80=E3=83=BC=E3=81=AEbash=E6=A7=8B?= =?UTF-8?q?=E6=96=87=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E5=85=A8=E9=9D=A2?= =?UTF-8?q?=E8=A7=A3=E6=B6=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbitの4件の指摘に対応するとともに、同種の未検出バグを自主的に監査して まとめて修正した。 - backend-ci-trigger: 変更パス抽出が backends// 以外の共有変更 (.github/workflows/, docker/ 等)を検出できない問題に対し、該当時は 7ワークフロー全発火のフォールバックを追加。手動発火コマンドの <ワークフローファイル>// をWORKFLOW/N/RUN_ID変数に置き換え (bash構文エラーになる箇所だった。RUN_IDはgh run listの出力から取得する形に) - backend-dispatch / docker-ops: docker compose exec に -T を追加(TTY未割当の 非対話環境で失敗するため)。//<コマンド> をX/SERVICE/CMD変数に 置き換え(同じくbash構文エラー箇所だった) - git-flow: PR作成コマンドで //<要約> 等のプレースホルダーが実値展開 されずそのまま送信される問題を修正。TYPE/SUMMARY/CHANGES/LINK変数を導入し、 heredocを非クォート化して変数展開させるようにした - new-backend-scaffold: 全編でNAME変数を導入し、mkdir/cp/grep等の例に残っていた 直書き(bash構文エラー)を解消。最終登録チェックはgrep -rlではなく ファイルごとに個別検査し、未登録時はexit 1で停止するように変更 全SKILL.mdの```bashブロック(20件)を `bash -n` で構文チェック済み。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 28 +++++-- .claude/skills/backend-dispatch/SKILL.md | 14 ++-- .claude/skills/docker-ops/SKILL.md | 10 ++- .claude/skills/git-flow/SKILL.md | 44 ++++------ .claude/skills/new-backend-scaffold/SKILL.md | 88 ++++++++++++-------- 5 files changed, 107 insertions(+), 77 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index 2b63db1..280dfa1 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -50,22 +50,34 @@ develop→main同期後に確認するか、ワークフローファイルのみ ```bash BASE=develop # git-flowでこのfeatureブランチの派生元とした基準ブランチに合わせる -git diff --name-only "$BASE"... | sed -n 's#^backends/\([^/]*\)/.*#\1#p' | sort -u -# 出力された各ディレクトリ名を上表と突き合わせて対象ワークフローを決定する +git diff --name-only "$BASE"... ``` +- `backends//**` に一致する変更 → 出力パスの `` 部分を上表と + 突き合わせ、対応するワークフローのみ発火する。 +- `.github/workflows/**`、`docker/**`、リポジトリ直下の設定ファイル等、 + 特定のバックエンドに閉じない変更(=上記以外の変更)が1件でも含まれる場合は、 + 影響範囲を個別に切り分けず、上表の7ワークフロー全てを発火するフォールバックを + 取る。 + ## 手動発火と結果確認 ```bash -gh workflow run <ワークフローファイル> --repo bobtabo/authorization-showcase --ref feature/issue- +WORKFLOW=go-gin-ci.yml # 対象ワークフローファイル名(上表を参照して置き換える) +N=34 # このfeatureブランチに対応するIssue番号に置き換える + +gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase --ref "feature/issue-$N" # 実行中/直近のrunを確認 gh run list --repo bobtabo/authorization-showcase \ - --workflow=<ワークフローファイル> --branch=feature/issue- --limit 1 - -# 完了まで待ってログを見る -gh run watch --repo bobtabo/authorization-showcase -gh run view --repo bobtabo/authorization-showcase --log + --workflow="$WORKFLOW" --branch="feature/issue-$N" --limit 1 + +# 完了まで待ってログを見る(run idを直近実行結果から取得する) +RUN_ID=$(gh run list --repo bobtabo/authorization-showcase \ + --workflow="$WORKFLOW" --branch="feature/issue-$N" --limit 1 \ + --json databaseId -q '.[0].databaseId') +gh run watch --repo bobtabo/authorization-showcase "$RUN_ID" +gh run view --repo bobtabo/authorization-showcase "$RUN_ID" --log ``` ## 注意 diff --git a/.claude/skills/backend-dispatch/SKILL.md b/.claude/skills/backend-dispatch/SKILL.md index 81341cc..c110c49 100644 --- a/.claude/skills/backend-dispatch/SKILL.md +++ b/.claude/skills/backend-dispatch/SKILL.md @@ -39,16 +39,20 @@ allowed-tools: Bash(docker:*), Bash(git:*) - **テスト・ビルド・CLI実行**: 必ずコンテナ内で行う。ホストに言語ランタイムが 入っていても、ホストで直接 `go test` 等を実行しない。 ```bash - docker/bin/docker-.sh exec + X=go # 対応表の値に置き換える: go/java/php-cake/php-codeigniter/php-fuel/python/ruby + "docker/bin/docker-$X.sh" exec # コンテナ内シェルに入った状態で、通常のテスト/ビルドコマンドを実行する ``` 非対話的に1コマンドだけ実行したい場合は、対応する `docker/local/app-/docker-compose.yml` を直接指定する(`docker exec <コンテナ名>` のようにコンテナ名を直書きしない。 プロジェクト名・サービス名がバックエンドごとに異なるため取り違えやすい): ```bash - docker compose -p showcase- -f docker/local/app-/docker-compose.yml \ - exec --user 1000 <コマンド> + X=go # 対応表の値に置き換える + SERVICE=go # execで指定するサービス名: go=go, java=java, php系=php, python=python, ruby=rb-rails + CMD="go test ./..." # 実行したいコマンド + + docker compose -p "showcase-$X" -f "docker/local/app-$X/docker-compose.yml" \ + exec -T --user 1000 "$SERVICE" $CMD ``` - (`` は `go` / `java` / `php-cake` / `php-codeigniter` / `php-fuel` / `python` / `ruby`、 - `` は go=`go`, java=`java`, php系=`php`, python=`python`, ruby=`rb-rails`) + `-T` は必須(付けないとTTY割り当てを試みて、非対話環境では失敗しうる)。 - コンテナが起動していない場合の対処は docker-ops Skillを参照。 diff --git a/.claude/skills/docker-ops/SKILL.md b/.claude/skills/docker-ops/SKILL.md index a03407a..d7ee1e2 100644 --- a/.claude/skills/docker-ops/SKILL.md +++ b/.claude/skills/docker-ops/SKILL.md @@ -15,8 +15,9 @@ allowed-tools: Bash(docker:*), Bash(cd:*), Bash(find:*) 一方、起動済みコンテナに対する非対話の単発コマンド実行(`exec`)は `guard-bash.sh` のブロック対象外であり、`docker compose -p showcase- -f -docker/local/app-/docker-compose.yml exec --user 1000 <コマンド>` -を直接使ってよい(backend-dispatch Skill参照)。 +docker/local/app-/docker-compose.yml exec -T --user 1000 <コマンド>` +を直接使ってよい(`-T`必須。無いとTTY割り当てを試みて非対話環境で失敗しうる。 +backend-dispatch Skill参照)。 ## 前提(初回のみ) @@ -51,8 +52,9 @@ bin/docker-ruby.sh up ## コンテナに入る/破棄する ```bash -docker/bin/docker-.sh exec # コンテナ内シェルに入る(Goはsh、他はbash) -docker/bin/docker-.sh down # そのバックエンドのコンテナ・イメージ・ボリュームを破棄 +X=go # 対応表(backend-dispatch Skill参照)の値に置き換える +"docker/bin/docker-$X.sh" exec # コンテナ内シェルに入る(Goはsh、他はbash) +"docker/bin/docker-$X.sh" down # そのバックエンドのコンテナ・イメージ・ボリュームを破棄 docker/bin/docker-backends.sh down # 全バックエンド一括破棄 ``` diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index d99d80a..b4336e6 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -40,44 +40,36 @@ git checkout -b "feature/issue-$N" ## 2. developへのPR作成 -- base: `develop` / head: `feature/issue-`(派生元を変えた場合もbaseは`develop`) -- タイトル: `(#): <日本語要約>` - (type: feat/fix/docs/chore/refactor 等、conventional commit風。Issueのtypeを踏襲する) -- 本文テンプレート: +- base: `develop` / head: `feature/issue-$N`(派生元を変えた場合もbaseは`develop`) +- タイトル: `$TYPE(#$N): $SUMMARY` + (`TYPE`: feat/fix/docs/chore/refactor 等、conventional commit風。Issueのtypeを踏襲する) +- 本文: Summary / Changes / Closes(またはRefs) / 署名の4点を含める。Issueの一部のみ + 対応する場合(Issueの残タスクが残る場合)は `Closes #$N` ではなく `Refs #$N` とし、 + Issueを自動クローズさせない。 - ```markdown - ## Summary - - Issue # 対応。<変更内容の要約> - - ## Changes - - <変更ファイル/内容> - - Closes # - - 🤖 Generated with [Claude Code](https://claude.com/claude-code) - ``` - - Issueの一部のみ対応する場合(Issueの残タスクが残る場合)は `Closes #` ではなく - `Refs #` とし、Issueを自動クローズさせない。 - -- コマンド(本文には上記テンプレートの内容をそのまま埋め込む。省略記号で済ませない): +- コマンド(`TYPE`/`SUMMARY`/`CHANGES`/`LINK` に実際の値を設定してから実行する。 + heredocはクォートしない `<(#): <要約>" --body "$(cat <<'EOF' + --title "${TYPE}(#${N}): ${SUMMARY}" --body "$(cat < 対応。<変更内容の要約> + Issue #$N 対応。$SUMMARY ## Changes - - <変更ファイル/内容> + $CHANGES - Closes # - + $LINK 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index cf03c1e..125b2f1 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -17,79 +17,86 @@ allowed-tools: Bash(git:*), Bash(cp:*), Bash(mkdir:*) - 事前に対応するGitHub Issueを作成し、git-flow Skillで `feature/issue-N` ブランチを作成しておく。 +- 以降のコマンド例は `NAME` 変数に実際のバックエンド名を入れて使う + (`` をそのままシェルに渡すと `<`/`>` がリダイレクトと解釈され + 構文エラーになるため、必ず変数化する): -## 1. バックエンドソース(`backends//`) + ```bash + NAME=node-express # 実際に追加するバックエンド名に置き換える + ``` + +## 1. バックエンドソース(`backends/$NAME/`) ```bash -mkdir -p backends/ +mkdir -p "backends/$NAME" ``` - アプリのソース一式を配置する。 -- `backends//README.md` を作成する(`backends/go-gin/README.md` の構成 +- `backends/$NAME/README.md` を作成する(`backends/go-gin/README.md` の構成 ―概要/起動方法/エンドポイント一覧―に合わせる)。 -## 2. Docker実行環境(`docker/local/app-/`) +## 2. Docker実行環境(`docker/local/app-$NAME/`) `docker/local/app-go/` を丸ごとコピーして書き換える。 ```bash -cp -r docker/local/app-go docker/local/app- +cp -r docker/local/app-go "docker/local/app-$NAME" ``` 書き換える点: - `docker-compose.yml` - - `name: showcase-app-` + - `name: showcase-app-$NAME` - サービス名・イメージのbuild文脈(`go/` ディレクトリ相当)を言語用に差し替え - - アプリ側の `volumes` を `./../../../backends/:/var/www/${APP_NAME}` に + - アプリ側の `volumes` を `./../../../backends/$NAME:/var/www/${APP_NAME}` に - `networks.showcase.external: true` はそのまま維持(`common` が作るネットワーク に参加する) - `nginx` サービス定義(`VIRTUAL_HOST` / `VIRTUAL_PORT` 等の環境変数連携)は ほぼそのまま流用可 - `.env.example` - - `APP_NAME=app-` - - `SERVER_NAME=apis.showcase-.dev` - - `NGINX_CONTAINER=showcase-_nginx` - - 言語コンテナの `_CONTAINER=showcase-_` を追加 + - `APP_NAME=app-$NAME` + - `SERVER_NAME=apis.showcase-$NAME.dev` + - `NGINX_CONTAINER=showcase-${NAME}_nginx` + - 言語コンテナの `_CONTAINER=showcase-${NAME}_` を追加 - デバッグポート等、言語固有の変数を追加 - `nginx/`(`Dockerfile` / `nginx.conf` / `templates/default.conf.template`)は ほぼそのまま流用できる(`APP_PATH` 等の環境変数経由でルーティングするため) -## 3. Dockerラッパースクリプト(`docker/bin/docker-.sh`) +## 3. Dockerラッパースクリプト(`docker/bin/docker-$NAME.sh`) `docker/bin/docker-go.sh` をコピーして書き換える。 ```bash -cp docker/bin/docker-go.sh docker/bin/docker-.sh -chmod 755 docker/bin/docker-.sh +cp docker/bin/docker-go.sh "docker/bin/docker-$NAME.sh" +chmod 755 "docker/bin/docker-$NAME.sh" ``` -書き換える点: `cd` 先を `app-`、`-p showcase-`、`exec` で入る +書き換える点: `cd` 先を `app-$NAME`、`-p showcase-$NAME`、`exec` で入る サービス名・シェル(bash推奨、必要ならsh)。 さらに `docker/bin/docker-backends.sh` の `run()` に1行追加する: ```bash -bash "${SCRIPT_DIR}/docker-.sh" "${ARG}" +bash "${SCRIPT_DIR}/docker-$NAME.sh" "${ARG}" ``` -## 4. CIワークフロー(`.github/workflows/-ci.yml`) +## 4. CIワークフロー(`.github/workflows/$NAME-ci.yml`) `.github/workflows/go-gin-ci.yml` をコピーして書き換える。 ```bash -cp .github/workflows/go-gin-ci.yml .github/workflows/-ci.yml +cp .github/workflows/go-gin-ci.yml ".github/workflows/$NAME-ci.yml" ``` 書き換える点: -- `paths: - 'backends//**'` +- `paths: - 'backends/$NAME/**'` - `branches-ignore: ['feature/issue-*']` と `concurrency.group: shared-authserver-tunnel` は変更しない(全バックエンド共有のngrok/LocalStack 輻輳防止のため) - 言語ツールチェインのセットアップステップ(`actions/setup-go` 相当) - `Resolve AUTH_SERVER_URL` ステップは他バックエンドと同一ロジックのため、 そのまま流用する(LocalStackのAPI Gateway IDをngrok経由で動的取得する仕組み) -- テスト実行コマンド・`working-directory: backends/` のみ差し替え +- テスト実行コマンド・`working-directory: backends/$NAME` のみ差し替え 新規追加したワークフローファイルは `main` にマージされるまで `workflow_dispatch` での手動発火ができない(GitHubの仕様。詳細は backend-ci-trigger Skillの当該 @@ -102,28 +109,41 @@ cp .github/workflows/go-gin-ci.yml .github/workflows/-ci.yml `✅ 完了`) - ルート `README.md`: - 「システム構造」のツリー(`backends/` 配下の一覧)に追加 - - 「6. バックエンドの初期設定」に `6.x ` の節を追加 + - 「6. バックエンドの初期設定」に `6.x $NAME` の節を追加 - 他Skillの登録先(更新を忘れるとSkill経由で新バックエンドを選択・起動・CI発火 できない): - `.claude/skills/backend-dispatch/SKILL.md` の対応表に1行追加 - `.claude/skills/backend-ci-trigger/SKILL.md` のワークフロー対応表に1行追加 - `.claude/skills/docker-ops/SKILL.md` の「起動順序」の個別列挙に - `docker/bin/docker-.sh up` を追加 + `docker/bin/docker-$NAME.sh up` を追加 -以下7ファイルすべてに `` が登場することを最終確認する: -`docker/bin/docker-backends.sh`(手順3)、`.github/workflows/-ci.yml` +以下7ファイルすべてに `$NAME` が登場することを最終確認する: +`docker/bin/docker-backends.sh`(手順3)、`.github/workflows/$NAME-ci.yml` (手順4)、`backends/README.md` / `README.md`(本節)、 `.claude/skills/{backend-dispatch,backend-ci-trigger,docker-ops}/SKILL.md` -(本節)。 +(本節)。1ファイルでも欠けていたら止まるように、ファイルごとに個別検査する +(`grep -rl` は一致したファイルのみ表示するため、欠落の見逃しに気づけない)。 ```bash -grep -rl "" \ - docker/bin/docker-backends.sh \ - .github/workflows/-ci.yml \ - backends/README.md README.md \ - .claude/skills/backend-dispatch/SKILL.md \ - .claude/skills/backend-ci-trigger/SKILL.md \ - .claude/skills/docker-ops/SKILL.md +set -euo pipefail + +files=( + "docker/bin/docker-backends.sh" + ".github/workflows/$NAME-ci.yml" + "backends/README.md" + "README.md" + ".claude/skills/backend-dispatch/SKILL.md" + ".claude/skills/backend-ci-trigger/SKILL.md" + ".claude/skills/docker-ops/SKILL.md" +) + +for f in "${files[@]}"; do + if ! grep -q -- "$NAME" "$f"; then + echo "登録漏れ: $f に '$NAME' が見つかりません" >&2 + exit 1 + fi +done +echo "7ファイルすべてに登録を確認しました" ``` ## 6. 動作確認 @@ -134,8 +154,8 @@ grep -rl "" \ ```bash docker/bin/docker-common.sh up # showcaseネットワーク・nginx-proxyが無ければ起動 -docker/bin/docker-.sh up -docker/bin/docker-.sh exec # コンテナに入り疎通確認 +"docker/bin/docker-$NAME.sh" up +"docker/bin/docker-$NAME.sh" exec # コンテナに入り疎通確認 ``` CIの手動発火・確認は backend-ci-trigger Skillを参照。ここまで確認できたら From 27fdb90fecbcf44f868bb5ec9ff381de961ae513 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Sat, 15 Aug 2026 12:12:36 +0900 Subject: [PATCH 08/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88bash=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E9=96=93=E3=81=AE=E5=A4=89=E6=95=B0=E9=9D=9E=E5=85=B1?= =?UTF-8?q?=E6=9C=89=E3=82=92=E5=89=8D=E6=8F=90=E3=81=AB=E8=87=AA=E5=B7=B1?= =?UTF-8?q?=E5=AE=8C=E7=B5=90=E5=8C=96=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Codeのbashツールは呼び出しごとに新しいシェルプロセスになり、変数は ブロック間で共有されない。この前提のもと、各SKILL.mdの```bashブロックが 他ブロックの変数に依存しないよう修正した。 - git-flow: PR作成ブロックでNが未定義のまま参照され、set -uで停止する 問題を修正。ブロック先頭でNを再設定するようにした - new-backend-scaffold: 手順1〜6の全ブロックがNAMEを共有する前提だった ものを、各ブロック先頭でNAMEを再設定する自己完結形に修正。あわせて 以下も対応: - cp -r docker/local/app-go "docker/local/app-$NAME" は展開先が既存の 場合にネストする問題があったため、mkdir -p後に中身をコピーする形に変更 - docker/bin/docker-backends.shへの追記例で$NAMEをそのまま書くと その場では未定義変数になる問題を修正し、実際の値を直接書く指示に変更 - CIワークフローのコピー後にname:フィールドとGo向けのヘッダーコメントも 書き換える必要がある旨をチェックリストに追加 全SKILL.mdについて、bashブロックで参照される変数が同一ブロック内で 定義されていることをスクリプトで検証済み。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 8 +++-- .claude/skills/new-backend-scaffold/SKILL.md | 33 ++++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index b4336e6..ddaa3a8 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -47,12 +47,14 @@ git checkout -b "feature/issue-$N" 対応する場合(Issueの残タスクが残る場合)は `Closes #$N` ではなく `Refs #$N` とし、 Issueを自動クローズさせない。 -- コマンド(`TYPE`/`SUMMARY`/`CHANGES`/`LINK` に実際の値を設定してから実行する。 - heredocはクォートしない `<` をそのままシェルに渡すと `<`/`>` がリダイレクトと解釈され - 構文エラーになるため、必ず変数化する): - - ```bash - NAME=node-express # 実際に追加するバックエンド名に置き換える - ``` + 構文エラーになるため、必ず変数化する)。 +- **各手順のbashブロックは別々のシェルプロセスとして実行される想定**で、 + `NAME` はブロック間で共有されない。そのため各ブロックの先頭で毎回 + `NAME=<実際の値>` を(同じ値で)設定し直す。同一シェルセッション内で + 複数手順を続けて実行する場合は、2回目以降の再設定は害にならない。 ## 1. バックエンドソース(`backends/$NAME/`) ```bash +NAME=node-express # 実際に追加するバックエンド名に置き換える mkdir -p "backends/$NAME" ``` @@ -37,10 +38,14 @@ mkdir -p "backends/$NAME" ## 2. Docker実行環境(`docker/local/app-$NAME/`) -`docker/local/app-go/` を丸ごとコピーして書き換える。 +`docker/local/app-go/` の中身を丸ごとコピーして書き換える。ディレクトリ自体を +`cp -r src dst` すると、`dst` が既に存在する場合は `dst/app-go/` のようにネストして +しまうため、コピー先を先に作ってから**中身**をコピーする: ```bash -cp -r docker/local/app-go "docker/local/app-$NAME" +NAME=node-express # 手順1と同じ値 +mkdir -p "docker/local/app-$NAME" +cp -r docker/local/app-go/. "docker/local/app-$NAME/" ``` 書き換える点: @@ -67,6 +72,7 @@ cp -r docker/local/app-go "docker/local/app-$NAME" `docker/bin/docker-go.sh` をコピーして書き換える。 ```bash +NAME=node-express # 手順1と同じ値 cp docker/bin/docker-go.sh "docker/bin/docker-$NAME.sh" chmod 755 "docker/bin/docker-$NAME.sh" ``` @@ -74,10 +80,13 @@ chmod 755 "docker/bin/docker-$NAME.sh" 書き換える点: `cd` 先を `app-$NAME`、`-p showcase-$NAME`、`exec` で入る サービス名・シェル(bash推奨、必要ならsh)。 -さらに `docker/bin/docker-backends.sh` の `run()` に1行追加する: +さらに `docker/bin/docker-backends.sh` の `run()` に1行追加する。 +この行は `docker-backends.sh` 自身のスクリプト内に書く固定のコードであり、 +その場では `NAME` 変数は存在しないため、`$NAME` ではなく**実際のバックエンド名を +直接書く**(例: `node-express`): ```bash -bash "${SCRIPT_DIR}/docker-$NAME.sh" "${ARG}" + bash "${SCRIPT_DIR}/docker-node-express.sh" "${ARG}" ``` ## 4. CIワークフロー(`.github/workflows/$NAME-ci.yml`) @@ -85,10 +94,14 @@ bash "${SCRIPT_DIR}/docker-$NAME.sh" "${ARG}" `.github/workflows/go-gin-ci.yml` をコピーして書き換える。 ```bash +NAME=node-express # 手順1と同じ値 cp .github/workflows/go-gin-ci.yml ".github/workflows/$NAME-ci.yml" ``` 書き換える点: +- ファイル先頭のコメント(`# Go(Gin)CI ワークフロー` / `# developブランチの + backends/go-gin/** 変更時に CI を実行`)と `name: Go(Gin)CI` を、追加する + 言語・バックエンド名に合わせて書き換える(コピー直後はGo向けの文言が残っている) - `paths: - 'backends/$NAME/**'` - `branches-ignore: ['feature/issue-*']` と `concurrency.group: shared-authserver-tunnel` は変更しない(全バックエンド共有のngrok/LocalStack @@ -126,6 +139,7 @@ cp .github/workflows/go-gin-ci.yml ".github/workflows/$NAME-ci.yml" ```bash set -euo pipefail +NAME=node-express # 手順1と同じ値 files=( "docker/bin/docker-backends.sh" @@ -153,6 +167,7 @@ echo "7ファイルすべてに登録を確認しました" (docker-ops Skill参照)。 ```bash +NAME=node-express # 手順1と同じ値 docker/bin/docker-common.sh up # showcaseネットワーク・nginx-proxyが無ければ起動 "docker/bin/docker-$NAME.sh" up "docker/bin/docker-$NAME.sh" exec # コンテナに入り疎通確認 From 283a4c89d2f97ecdece5b7742ba39bb6e361bc78 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 02:17:57 +0900 Subject: [PATCH 09/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88NAME=E3=81=AEkebab-case=E6=A4=9C?= =?UTF-8?q?=E8=A8=BC/=E3=82=B3=E3=83=94=E3=83=BC=E5=85=88=E3=81=AE?= =?UTF-8?q?=E6=97=A2=E5=AD=98=E6=A4=9C=E5=87=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 各bashブロックでNAME設定直後にkebab-caseパターン (^[a-z0-9]+(-[a-z0-9]+)*$) を検証し、不一致ならexit 1で停止 - docker/local/app-$NAME、docker/bin/docker-$NAME.sh、 .github/workflows/$NAME-ci.yml のコピー前に既存チェックを追加し、 再実行時に既存の変更をcpで上書きしないようにした 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/new-backend-scaffold/SKILL.md | 31 +++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index b694300..9929652 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -19,16 +19,24 @@ allowed-tools: Bash(git:*), Bash(cp:*), Bash(mkdir:*) ブランチを作成しておく。 - 以降のコマンド例は `NAME` 変数に実際のバックエンド名を入れて使う (`` をそのままシェルに渡すと `<`/`>` がリダイレクトと解釈され - 構文エラーになるため、必ず変数化する)。 + 構文エラーになるため、必ず変数化する)。`NAME` はディレクトリ名・Docker + Composeのプロジェクト名・CIワークフローファイル名にそのまま使われるため、 + 既存7バックエンド同様のkebab-case(`^[a-z0-9]+(-[a-z0-9]+)*$`)にする。 + 各ブロックの冒頭で検証している。 - **各手順のbashブロックは別々のシェルプロセスとして実行される想定**で、 `NAME` はブロック間で共有されない。そのため各ブロックの先頭で毎回 `NAME=<実際の値>` を(同じ値で)設定し直す。同一シェルセッション内で 複数手順を続けて実行する場合は、2回目以降の再設定は害にならない。 +- 手順2〜4のコピー操作は、コピー先が既に存在する場合は実行前に停止する + (再実行で既存の変更を`cp`が上書きするのを防ぐ)。既存の生成物を作り直したい + 場合は、手動で削除するか意図的に上書きすることを確認した上で行う。 ## 1. バックエンドソース(`backends/$NAME/`) ```bash NAME=node-express # 実際に追加するバックエンド名に置き換える +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } + mkdir -p "backends/$NAME" ``` @@ -44,6 +52,12 @@ mkdir -p "backends/$NAME" ```bash NAME=node-express # 手順1と同じ値 +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } + +if [ -e "docker/local/app-$NAME" ]; then + echo "docker/local/app-$NAME は既に存在します。上書きを避けるため中断します" >&2 + exit 1 +fi mkdir -p "docker/local/app-$NAME" cp -r docker/local/app-go/. "docker/local/app-$NAME/" ``` @@ -73,6 +87,12 @@ cp -r docker/local/app-go/. "docker/local/app-$NAME/" ```bash NAME=node-express # 手順1と同じ値 +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } + +if [ -e "docker/bin/docker-$NAME.sh" ]; then + echo "docker/bin/docker-$NAME.sh は既に存在します。上書きを避けるため中断します" >&2 + exit 1 +fi cp docker/bin/docker-go.sh "docker/bin/docker-$NAME.sh" chmod 755 "docker/bin/docker-$NAME.sh" ``` @@ -95,6 +115,12 @@ chmod 755 "docker/bin/docker-$NAME.sh" ```bash NAME=node-express # 手順1と同じ値 +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } + +if [ -e ".github/workflows/$NAME-ci.yml" ]; then + echo ".github/workflows/$NAME-ci.yml は既に存在します。上書きを避けるため中断します" >&2 + exit 1 +fi cp .github/workflows/go-gin-ci.yml ".github/workflows/$NAME-ci.yml" ``` @@ -140,6 +166,7 @@ cp .github/workflows/go-gin-ci.yml ".github/workflows/$NAME-ci.yml" ```bash set -euo pipefail NAME=node-express # 手順1と同じ値 +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } files=( "docker/bin/docker-backends.sh" @@ -168,6 +195,8 @@ echo "7ファイルすべてに登録を確認しました" ```bash NAME=node-express # 手順1と同じ値 +[[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } + docker/bin/docker-common.sh up # showcaseネットワーク・nginx-proxyが無ければ起動 "docker/bin/docker-$NAME.sh" up "docker/bin/docker-$NAME.sh" exec # コンテナに入り疎通確認 From ad574ce7db6cbf45dd737783fc9e0b968d273d1b Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 14:27:06 +0900 Subject: [PATCH 10/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88=E6=89=8B=E9=A0=861=E3=81=AEbackends?= =?UTF-8?q?/$NAME=E3=81=AB=E3=82=82=E6=97=A2=E5=AD=98=E3=83=81=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 手順2〜4と同様、backends/$NAME が既に存在する場合はmkdir -p実行前に 中断するようにした(再実行時の意図しない混在を防ぐ)。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/new-backend-scaffold/SKILL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index 9929652..6bf6edb 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -37,6 +37,10 @@ allowed-tools: Bash(git:*), Bash(cp:*), Bash(mkdir:*) NAME=node-express # 実際に追加するバックエンド名に置き換える [[ "$NAME" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || { echo "invalid backend name: $NAME" >&2; exit 1; } +if [ -e "backends/$NAME" ]; then + echo "backends/$NAME は既に存在します。上書きを避けるため中断します" >&2 + exit 1 +fi mkdir -p "backends/$NAME" ``` From 7284cf16624c716d5ce7c2c5d115ac3cf78b166f Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 17:18:59 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat(#34):=20git-flow=E3=81=ABCI/CodeRabb?= =?UTF-8?q?it=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E7=9B=A3=E8=A6=96?= =?UTF-8?q?=E3=83=BB=E5=AF=BE=E5=BF=9C=E6=89=8B=E9=A0=86=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit これまでgit-flow Skillはブランチ作成とPR作成までで、PR作成後のCI待ち・ CodeRabbit指摘への対応・reviews paused時の再トリガーは手順化されておらず、 本PR #35で実際に7ラウンド繰り返した対応が再現できない状態だった。 「## 3. レビュー監視・対応(CI/CodeRabbit)」として以下を追加: - 3.1 固定sleepでブロックせずrun_in_background前提でCI完了をポーリングする手順 - 3.2 未返信のtop-levelコメントを列挙し、検証→修正→コメントIDへの返信までの手順 - 3.3 reviews paused時に @coderabbitai review で再トリガーする手順と、 「新規actionable commentが0件」かつ「mergeable/mergeStateStatusがCLEAN」を 収束条件とする判定基準。無限ループを避けるため5ラウンド目安で打ち切りユーザーに 判断を仰ぐ旨も明記。マージ自体はこのSkillでは行わない。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index ddaa3a8..514881f 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -78,6 +78,77 @@ git checkout -b "feature/issue-$N" )" ``` +## 3. レビュー監視・対応(CI/CodeRabbit) + +PRを作成したら、CIとCodeRabbitレビューが収束するまで監視する。人間レビュアーの +承認を待つ/催促するのはこのSkillの対象外(そこはユーザーに判断を委ねる)。 + +### 3.1 CI待ち + +固定の `sleep` ループでフォアグラウンドをブロックしない。Bashツールの +`run_in_background: true` でポーリングし、完了通知を待つ: + +```bash +PR=35 # gh pr create の出力から取得したPR番号 + +for i in $(seq 1 40); do + STATE=$(gh pr view "$PR" --repo bobtabo/authorization-showcase \ + --json statusCheckRollup -q '.statusCheckRollup[0].state' 2>/dev/null) + echo "[$i] state=$STATE" + if [ "$STATE" = "SUCCESS" ] || [ "$STATE" = "FAILURE" ]; then + break + fi + sleep 15 +done +``` + +### 3.2 CodeRabbitの指摘への対応 + +```bash +PR=35 # 対象PR番号 + +gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments" --paginate \ + --jq '.[] | select(.in_reply_to_id == null) | {id, path, line}' +``` + +未返信(`in_reply_to_id == null` で自分がまだ返信していない)の指摘ごとに: + +1. 指摘内容を現在のコードと照らして検証する。既に対応済み/的外れ/このリポジトリの + 既知のスコープ外事項(Issue本文に明記済み等)なら、修正はせず理由を添えて返信する。 +2. 妥当な指摘は修正してコミット・プッシュする。 +3. 各コメントIDに返信する: + +```bash +PR=35 +COMMENT_ID=1234567 # 対応表または上記jqの出力から取得 + +gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments/$COMMENT_ID/replies" \ + -f body="対応しました()。<何をどう直したか>" +``` + +### 3.3 再レビューのトリガーと収束判定 + +CodeRabbitは短時間に複数コミットが積まれると "reviews paused"(自動一時停止)になり、 +push しただけでは新しいレビューが走らないことがある。その場合は明示的に再トリガーする: + +```bash +PR=35 +gh pr comment "$PR" --repo bobtabo/authorization-showcase --body "@coderabbitai review" +``` + +再トリガー後、3.1と同様にポーリングし、top-levelコメント数 +(`in_reply_to_id == null` の件数)が増えていないか確認する。増えていれば3.2に戻る。 + +収束(完了)の判定基準は次の**両方**を満たすこと: +- 新しいラウンドで actionable comment が0件(top-levelコメント数が増えない) +- `gh pr view "$PR" --json mergeable,mergeStateStatus` が `MERGEABLE` / `CLEAN` + +目安として5ラウンド前後で収束しない場合は、無限にループさせず一旦打ち切り、 +未解決点を添えてユーザーに報告し、続行するかどうかの判断を仰ぐ。 + +収束したら最終状態(CI結果・対応した指摘の要約・PRリンク)をユーザーに報告する。 +**developへのマージはこのSkillでは行わない**(ユーザーの明示的な指示を待つ)。 + ## 禁止事項 - `develop` / `main` への直接push(featureブランチ経由のPRを必ず通す) From 09e7c0168e82580ed06a0749ffd84eb6ed9a32a9 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 18:39:04 +0900 Subject: [PATCH 12/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88section3=E3=81=AEPR=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E5=80=A4/=E5=8D=98=E4=B8=80=E3=83=81=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=82=AF=E5=88=A4=E5=AE=9A/=E6=9C=AA=E8=BF=94?= =?UTF-8?q?=E4=BF=A1=E5=88=A4=E5=AE=9A=E3=82=92=E4=BF=AE=E6=AD=A3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 追加したばかりの「## 3. レビュー監視・対応」自体に5件の設計ミスがあった。 - PR=35のハードコードをやめ、feature/issue-$Nからgh pr viewで解決するように変更 (他のPRを誤って操作する事故を防ぐ) - CI待ちがstatusCheckRollup[0]のみを見ていた問題を修正し、全チェックを集約。 API取得失敗・タイムアウトも明示的に失敗として扱うようにした - backends/**の変更はfeature/issue-*へのpushでは自動実行されない (branches-ignoreで除外)ため、backend-ci-trigger Skillでの手動発火が 必要な旨を明記 - 「未返信」の判定をtop-levelコメント数ではなく、coderabbitai[bot]の top-levelコメントのうちcoderabbitai[bot]以外からの返信が無いものとして jq+commで算出するように修正(ダミーデータで動作確認済み) - 返信コマンドのbodyに残っていたプレースホルダー(""等)を、 実値を入れた変数(REPLY_BODY)経由で渡す形に修正 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 81 ++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index 514881f..86099f5 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -82,48 +82,89 @@ git checkout -b "feature/issue-$N" PRを作成したら、CIとCodeRabbitレビューが収束するまで監視する。人間レビュアーの 承認を待つ/催促するのはこのSkillの対象外(そこはユーザーに判断を委ねる)。 +以下の各bashブロックは自己完結させてあるが、`PR`はハードコードせず毎回 +`feature/issue-$N` から解決する(PR番号を決め打ちすると、別のPRを誤って操作する +事故につながる)。 ### 3.1 CI待ち +`backends/**` を変更している場合、`feature/issue-*` へのpushではバックエンドCIが +自動実行されない(`branches-ignore`で除外されるため)。該当ワークフローは +backend-ci-trigger Skillで`workflow_dispatch`により手動発火してから待つ +(バックエンド変更が無ければ不要。CodeRabbitのチェックはpush毎に自動で走る)。 + 固定の `sleep` ループでフォアグラウンドをブロックしない。Bashツールの -`run_in_background: true` でポーリングし、完了通知を待つ: +`run_in_background: true` でポーリングし、完了通知を待つ。全チェックの状態を +集約し、1件でも `FAILURE` なら停止、API取得失敗・タイムアウトも明示的に失敗として +扱う(先頭のチェックだけを見ない): ```bash -PR=35 # gh pr create の出力から取得したPR番号 +set -euo pipefail +N=34 # このfeatureブランチのIssue番号 +PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) for i in $(seq 1 40); do - STATE=$(gh pr view "$PR" --repo bobtabo/authorization-showcase \ - --json statusCheckRollup -q '.statusCheckRollup[0].state' 2>/dev/null) - echo "[$i] state=$STATE" - if [ "$STATE" = "SUCCESS" ] || [ "$STATE" = "FAILURE" ]; then + JSON=$(gh pr view "$PR" --repo bobtabo/authorization-showcase --json statusCheckRollup) || { + echo "gh pr view の取得に失敗しました" >&2 + exit 1 + } + STATES=$(echo "$JSON" | jq -r '.statusCheckRollup[].state') + if echo "$STATES" | grep -q '^FAILURE$'; then + echo "CIが失敗しました" >&2 + exit 1 + fi + if [ -n "$STATES" ] && ! echo "$STATES" | grep -qv '^SUCCESS$'; then + echo "全チェックSUCCESS" break fi + echo "[$i] pending: $STATES" + if [ "$i" -eq 40 ]; then + echo "タイムアウト: CIが完了しませんでした" >&2 + exit 1 + fi sleep 15 done ``` ### 3.2 CodeRabbitの指摘への対応 +「未返信」とは、CodeRabbit(`coderabbitai[bot]`)のtop-levelコメントのうち、 +`coderabbitai[bot]` 以外からの返信が付いていないものを指す(top-levelコメント数を +そのまま見ると、既に返信済みのものも数えてしまい判定を誤る): + ```bash -PR=35 # 対象PR番号 +set -euo pipefail +N=34 # このfeatureブランチのIssue番号 +PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) + +ALL=$(gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments" --paginate) +TOP_IDS=$(echo "$ALL" | jq -r '.[] | select(.user.login=="coderabbitai[bot]" and .in_reply_to_id==null) | .id') +REPLIED_IDS=$(echo "$ALL" | jq -r '.[] | select(.in_reply_to_id != null and .user.login!="coderabbitai[bot]") | .in_reply_to_id') -gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments" --paginate \ - --jq '.[] | select(.in_reply_to_id == null) | {id, path, line}' +comm -23 <(echo "$TOP_IDS" | sort) <(echo "$REPLIED_IDS" | sort -u) +# 出力された各IDについて本文を確認する: +# gh api repos/bobtabo/authorization-showcase/pulls/comments/ --jq '.path, .line, .body' ``` -未返信(`in_reply_to_id == null` で自分がまだ返信していない)の指摘ごとに: +未返信の指摘ごとに: 1. 指摘内容を現在のコードと照らして検証する。既に対応済み/的外れ/このリポジトリの 既知のスコープ外事項(Issue本文に明記済み等)なら、修正はせず理由を添えて返信する。 2. 妥当な指摘は修正してコミット・プッシュする。 -3. 各コメントIDに返信する: +3. 各コメントIDに返信する(本文はプレースホルダーのまま送信せず、実際のコミットSHAと + 修正内容を変数に入れてから渡す): ```bash -PR=35 -COMMENT_ID=1234567 # 対応表または上記jqの出力から取得 +set -euo pipefail +N=34 # このfeatureブランチのIssue番号 +PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) +COMMENT_ID=1234567 # 3.2で洗い出した未返信コメントのID +COMMIT_SHA=$(git rev-parse --short HEAD) +FIX_SUMMARY="何をどう直したかを記述する" +REPLY_BODY="対応しました(${COMMIT_SHA})。${FIX_SUMMARY}" gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments/$COMMENT_ID/replies" \ - -f body="対応しました()。<何をどう直したか>" + -f body="$REPLY_BODY" ``` ### 3.3 再レビューのトリガーと収束判定 @@ -132,16 +173,18 @@ CodeRabbitは短時間に複数コミットが積まれると "reviews paused" push しただけでは新しいレビューが走らないことがある。その場合は明示的に再トリガーする: ```bash -PR=35 +set -euo pipefail +N=34 # このfeatureブランチのIssue番号 +PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) gh pr comment "$PR" --repo bobtabo/authorization-showcase --body "@coderabbitai review" ``` -再トリガー後、3.1と同様にポーリングし、top-levelコメント数 -(`in_reply_to_id == null` の件数)が増えていないか確認する。増えていれば3.2に戻る。 +再トリガー後、3.1のCI待ちと3.2の未返信コメント抽出を繰り返す。 収束(完了)の判定基準は次の**両方**を満たすこと: -- 新しいラウンドで actionable comment が0件(top-levelコメント数が増えない) -- `gh pr view "$PR" --json mergeable,mergeStateStatus` が `MERGEABLE` / `CLEAN` +- 3.2の未返信コメント抽出結果が0件 +- `gh pr view "$PR" --repo bobtabo/authorization-showcase --json mergeable,mergeStateStatus` + が `mergeable: MERGEABLE` / `mergeStateStatus: CLEAN` 目安として5ラウンド前後で収束しない場合は、無限にループさせず一旦打ち切り、 未解決点を添えてユーザーに報告し、続行するかどうかの判断を仰ぐ。 From e29c571857a196e7161899aff55b33153bea3908 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 18:57:22 +0900 Subject: [PATCH 13/18] =?UTF-8?q?fix:=20=E6=9C=AA=E8=BF=94=E4=BF=A1?= =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E6=8A=BD=E5=87=BA=E3=81=A7?= =?UTF-8?q?echo|jq=E3=81=8C=E3=83=91=E3=83=BC=E3=82=B9=E3=82=A8=E3=83=A9?= =?UTF-8?q?=E3=83=BC=E3=81=AB=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh api の応答をALL変数に保持してからecho "$ALL" | jq に通す方式だと、 本文中のエスケープシーケンスがechoの実装によって壊れ、jqがcontrol character関連のパースエラーで失敗することを実PR #35で確認した (このとき失敗はTOP_IDS/REPLIED_IDSが空文字になるだけで、 set -euo pipefailでも検出できず「未返信0件」と誤判定しうる)。 gh api の --jq オプションで応答を直接フィルタする方式(API呼び出しは 2回になる)に変更し、echo経由の変数受け渡しを避けた。実PR #35で 新方式が正しく動作すること(未返信0件を検出)を確認済み。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index 86099f5..3dc5a2c 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -137,9 +137,14 @@ set -euo pipefail N=34 # このfeatureブランチのIssue番号 PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) -ALL=$(gh api "repos/bobtabo/authorization-showcase/pulls/$PR/comments" --paginate) -TOP_IDS=$(echo "$ALL" | jq -r '.[] | select(.user.login=="coderabbitai[bot]" and .in_reply_to_id==null) | .id') -REPLIED_IDS=$(echo "$ALL" | jq -r '.[] | select(.in_reply_to_id != null and .user.login!="coderabbitai[bot]") | .in_reply_to_id') +COMMENTS_URL="repos/bobtabo/authorization-showcase/pulls/$PR/comments" +TOP_IDS=$(gh api "$COMMENTS_URL" --paginate \ + --jq '.[] | select(.user.login=="coderabbitai[bot]" and .in_reply_to_id==null) | .id') +REPLIED_IDS=$(gh api "$COMMENTS_URL" --paginate \ + --jq '.[] | select(.in_reply_to_id != null and .user.login!="coderabbitai[bot]") | .in_reply_to_id') +# gh api の --jq でAPI応答を直接フィルタする(応答をシェル変数に保持してから +# echo | jq に通すと、本文中のエスケープシーケンスがechoで壊れてjqがパース +# エラーになることがあるため避ける) comm -23 <(echo "$TOP_IDS" | sort) <(echo "$REPLIED_IDS" | sort -u) # 出力された各IDについて本文を確認する: From 18026ec4ffe767d4c4eda885aac57af94f9f3520 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Mon, 17 Aug 2026 23:38:24 +0900 Subject: [PATCH 14/18] =?UTF-8?q?fix:=20CI=E5=BE=85=E3=81=A1=E3=81=AEERROR?= =?UTF-8?q?=E7=8A=B6=E6=85=8B=E6=9C=AA=E6=A4=9C=E7=9F=A5=E3=81=A8=E6=96=B0?= =?UTF-8?q?=E8=A6=8Fworkflow=E6=9C=AA=E3=83=9E=E3=83=BC=E3=82=B8=E6=99=82?= =?UTF-8?q?=E3=81=AE=E5=88=86=E5=B2=90=E6=BC=8F=E3=82=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - statusCheckRollupのstateにFAILUREだけでなくERROR(GitHubのStatusState 仕様上の終端失敗状態)も即失敗として検知するようにgrepパターンを拡張 - new-backend-scaffoldで新規追加したworkflowはmainにマージされるまで workflow_dispatchできないため、backend-ci-triggerでの手動発火を無条件に 案内せず、その場合は「CI未実行(develop→main同期後に確認)」と明示して 報告する分岐を追記 - CI待ちブロックのJSON=$(...) | echo | jq もecho経由のパースエラーの 可能性があったため、gh pr view --jq で直接フィルタする形に統一 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/git-flow/SKILL.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index 3dc5a2c..b3c0731 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -89,14 +89,20 @@ PRを作成したら、CIとCodeRabbitレビューが収束するまで監視す ### 3.1 CI待ち `backends/**` を変更している場合、`feature/issue-*` へのpushではバックエンドCIが -自動実行されない(`branches-ignore`で除外されるため)。該当ワークフローは -backend-ci-trigger Skillで`workflow_dispatch`により手動発火してから待つ -(バックエンド変更が無ければ不要。CodeRabbitのチェックはpush毎に自動で走る)。 +自動実行されない(`branches-ignore`で除外されるため)。対応ワークフローが既に +defaultブランチ(`main`)に存在するなら、backend-ci-trigger Skillで +`workflow_dispatch`により手動発火してから待つ。new-backend-scaffold Skillで +**新規追加した**ワークフロー(まだ`main`に無い)は`workflow_dispatch`で発火できない +ため、この場合は無理に発火しようとせず「CI未実行(develop→main同期後に確認)」と +明示してユーザーに報告する(バックエンド変更が無ければこの節は不要。CodeRabbitの +チェックはpush毎に自動で走る)。 固定の `sleep` ループでフォアグラウンドをブロックしない。Bashツールの `run_in_background: true` でポーリングし、完了通知を待つ。全チェックの状態を -集約し、1件でも `FAILURE` なら停止、API取得失敗・タイムアウトも明示的に失敗として -扱う(先頭のチェックだけを見ない): +集約し、`FAILURE`/`ERROR`(GitHubのStatusState仕様上の終端失敗状態)が1件でもあれば +即停止、API取得失敗・タイムアウトも明示的に失敗として扱う(先頭のチェックだけを +見ない。応答をシェル変数に保持してからechoでjqへ渡すと本文中のエスケープシーケンスで +壊れることがあるため、`gh`の`--jq`で直接フィルタする): ```bash set -euo pipefail @@ -104,12 +110,12 @@ N=34 # このfeatureブランチのIssue番号 PR=$(gh pr view "feature/issue-$N" --repo bobtabo/authorization-showcase --json number -q .number) for i in $(seq 1 40); do - JSON=$(gh pr view "$PR" --repo bobtabo/authorization-showcase --json statusCheckRollup) || { + STATES=$(gh pr view "$PR" --repo bobtabo/authorization-showcase \ + --json statusCheckRollup --jq '.statusCheckRollup[].state') || { echo "gh pr view の取得に失敗しました" >&2 exit 1 } - STATES=$(echo "$JSON" | jq -r '.statusCheckRollup[].state') - if echo "$STATES" | grep -q '^FAILURE$'; then + if echo "$STATES" | grep -qE '^(FAILURE|ERROR)$'; then echo "CIが失敗しました" >&2 exit 1 fi From c11eccaf60ee78bc6313946557c872019a889ab7 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 21 Aug 2026 01:25:00 +0900 Subject: [PATCH 15/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88=E8=AA=A4=E3=81=A3=E3=81=9Frun?= =?UTF-8?q?=E7=9B=A3=E8=A6=96/=E5=BC=95=E6=95=B0=E5=88=86=E5=89=B2/?= =?UTF-8?q?=E8=A1=A8=E7=8F=BE=E7=9F=9B=E7=9B=BE/.env=E6=B7=B7=E5=85=A5?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend-ci-trigger: gh run list --limit 1 が既存の別runを拾ってしまう 問題を修正。発火前時刻を記録し、それより後に作られたworkflow_dispatch イベントのrunだけをポーリングで検出。gh run watchに--exit-statusを追加し CI失敗をスクリプト自体の終了ステータスに反映 - backend-dispatch: CMDを文字列から配列に変更し"${CMD[@]}"で展開。文字列の ままだと引数中のクォートや*がホスト側シェルで単語分割・パス展開される問題 を修正 - git-flow: 「CodeRabbitのチェックはpush毎に自動で走る」という、reviews paused時の実態と矛盾する表現を、3.2/3.3の未返信確認・再トリガー手順に 従う旨に修正 - new-backend-scaffold: docker/local/app-go/を丸ごとコピーする際、実際に ホストへ存在する.env(ローカル起動で生成された実際の認証情報等を含む)と logs/(実行時ログ)が新バックエンドへ混入することを実ファイルで確認し、 rsync --exclude で除外するよう修正。.envはdocker-ops Skillの記載通り docker-.sh up実行時に.env.exampleから自動生成される仕様のため、 ここでは生成しない 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 33 ++++++++++++++------ .claude/skills/backend-dispatch/SKILL.md | 9 ++++-- .claude/skills/git-flow/SKILL.md | 4 +-- .claude/skills/new-backend-scaffold/SKILL.md | 10 ++++-- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index 280dfa1..bb194d2 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -62,21 +62,36 @@ git diff --name-only "$BASE"... ## 手動発火と結果確認 +`gh run list --limit 1` は既存の(今回発火した run とは別の)直近 run を拾って +しまう可能性があるため、発火前の時刻を記録しておき、それより後に作られた +`workflow_dispatch` イベントの run だけを新しい run として待つ: + ```bash +set -euo pipefail WORKFLOW=go-gin-ci.yml # 対象ワークフローファイル名(上表を参照して置き換える) N=34 # このfeatureブランチに対応するIssue番号に置き換える +DISPATCHED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ) gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase --ref "feature/issue-$N" -# 実行中/直近のrunを確認 -gh run list --repo bobtabo/authorization-showcase \ - --workflow="$WORKFLOW" --branch="feature/issue-$N" --limit 1 - -# 完了まで待ってログを見る(run idを直近実行結果から取得する) -RUN_ID=$(gh run list --repo bobtabo/authorization-showcase \ - --workflow="$WORKFLOW" --branch="feature/issue-$N" --limit 1 \ - --json databaseId -q '.[0].databaseId') -gh run watch --repo bobtabo/authorization-showcase "$RUN_ID" +# 新しいrunが現れるまでポーリングする(発火直後はまだ一覧に出ないことがある) +RUN_ID="" +for i in $(seq 1 20); do + RUN_ID=$(gh run list --repo bobtabo/authorization-showcase \ + --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ + --json databaseId,createdAt \ + --jq "[.[] | select(.createdAt > \"$DISPATCHED_AT\")] | sort_by(.createdAt) | .[0].databaseId // empty") + [ -n "$RUN_ID" ] && break + sleep 3 +done +if [ -z "$RUN_ID" ]; then + echo "新しいrunを検出できませんでした" >&2 + exit 1 +fi + +# 完了まで待ってログを見る。--exit-status でCI失敗をこのコマンド自体の +# 終了ステータスにも反映する +gh run watch --repo bobtabo/authorization-showcase "$RUN_ID" --exit-status gh run view --repo bobtabo/authorization-showcase "$RUN_ID" --log ``` diff --git a/.claude/skills/backend-dispatch/SKILL.md b/.claude/skills/backend-dispatch/SKILL.md index c110c49..c695634 100644 --- a/.claude/skills/backend-dispatch/SKILL.md +++ b/.claude/skills/backend-dispatch/SKILL.md @@ -45,14 +45,17 @@ allowed-tools: Bash(docker:*), Bash(git:*) ``` 非対話的に1コマンドだけ実行したい場合は、対応する `docker/local/app-/docker-compose.yml` を直接指定する(`docker exec <コンテナ名>` のようにコンテナ名を直書きしない。 - プロジェクト名・サービス名がバックエンドごとに異なるため取り違えやすい): + プロジェクト名・サービス名がバックエンドごとに異なるため取り違えやすい)。 + `CMD` は文字列ではなく配列にし、`"${CMD[@]}"` で展開する(文字列のまま + `$CMD` と書くと、引数中のクォートや `*` がホスト側のシェルで単語分割・ + パス展開されてしまい、意図しない引数がコンテナに渡る): ```bash X=go # 対応表の値に置き換える SERVICE=go # execで指定するサービス名: go=go, java=java, php系=php, python=python, ruby=rb-rails - CMD="go test ./..." # 実行したいコマンド + CMD=(go test ./...) # 実行したいコマンド(配列で書く) docker compose -p "showcase-$X" -f "docker/local/app-$X/docker-compose.yml" \ - exec -T --user 1000 "$SERVICE" $CMD + exec -T --user 1000 "$SERVICE" "${CMD[@]}" ``` `-T` は必須(付けないとTTY割り当てを試みて、非対話環境では失敗しうる)。 - コンテナが起動していない場合の対処は docker-ops Skillを参照。 diff --git a/.claude/skills/git-flow/SKILL.md b/.claude/skills/git-flow/SKILL.md index b3c0731..2b36775 100644 --- a/.claude/skills/git-flow/SKILL.md +++ b/.claude/skills/git-flow/SKILL.md @@ -94,8 +94,8 @@ defaultブランチ(`main`)に存在するなら、backend-ci-trigger Skill `workflow_dispatch`により手動発火してから待つ。new-backend-scaffold Skillで **新規追加した**ワークフロー(まだ`main`に無い)は`workflow_dispatch`で発火できない ため、この場合は無理に発火しようとせず「CI未実行(develop→main同期後に確認)」と -明示してユーザーに報告する(バックエンド変更が無ければこの節は不要。CodeRabbitの -チェックはpush毎に自動で走る)。 +明示してユーザーに報告する(バックエンド変更が無ければCI待ちは不要。CodeRabbit +レビューは自動起動を前提とせず、3.2/3.3の未返信コメント確認と再トリガー手順に従う)。 固定の `sleep` ループでフォアグラウンドをブロックしない。Bashツールの `run_in_background: true` でポーリングし、完了通知を待つ。全チェックの状態を diff --git a/.claude/skills/new-backend-scaffold/SKILL.md b/.claude/skills/new-backend-scaffold/SKILL.md index 6bf6edb..a52dcd3 100644 --- a/.claude/skills/new-backend-scaffold/SKILL.md +++ b/.claude/skills/new-backend-scaffold/SKILL.md @@ -52,7 +52,12 @@ mkdir -p "backends/$NAME" `docker/local/app-go/` の中身を丸ごとコピーして書き換える。ディレクトリ自体を `cp -r src dst` すると、`dst` が既に存在する場合は `dst/app-go/` のようにネストして -しまうため、コピー先を先に作ってから**中身**をコピーする: +しまうため、コピー先を先に作ってから**中身**をコピーする。ただし `docker/local/app-go/.env` +(`.gitignore`対象。ホストでのローカル起動により生成された実際の認証情報・ +コンテナ名・ポート等を含みうる)と `docker/local/app-go/logs/`(実行時ログ)は +新バックエンド用の雛形に含めるべきではないため除外する(`.env`は docker-ops Skillの +通り、後で `docker/bin/docker-$NAME.sh up` を実行した際に `.env.example` から +自動生成されるため、ここで手動生成する必要はない): ```bash NAME=node-express # 手順1と同じ値 @@ -63,7 +68,8 @@ if [ -e "docker/local/app-$NAME" ]; then exit 1 fi mkdir -p "docker/local/app-$NAME" -cp -r docker/local/app-go/. "docker/local/app-$NAME/" +rsync -a --exclude='.env' --exclude='logs/' docker/local/app-go/ "docker/local/app-$NAME/" +mkdir -p "docker/local/app-$NAME/logs" ``` 書き換える点: From 62caedff973b6409bb11e4d70db16647d60fb007 Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 21 Aug 2026 16:43:39 +0900 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88run=E7=89=B9=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E4=B8=80=E6=84=8F=E6=80=A7/exit-status=E5=A4=B1=E6=95=97?= =?UTF-8?q?=E6=99=82=E3=81=AE=E3=83=AD=E3=82=B0=E6=AC=A0=E8=90=BD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gh workflow run が返すrun URLからrun IDを取得することを優先し、URLが 返らない場合のみgh run listへフォールバック。フォールバック時も 「発火前時刻より後」の候補が複数/0件なら一意に特定できないため中断する ようにし、同時刻の並行dispatchとの取り違えを防いだ - gh run watch --exit-status がCI失敗で非0終了すると、set -e下では直後の gh run view --logが実行されず失敗時にログを確認できない問題を修正。 set +e/set -eでステータスを一旦保存し、ログ取得後に元のステータスで exitするようにした - URL抽出・候補数判定・ステータス保存の各ロジックをローカルで動作確認済み 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 53 +++++++++++++++------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index bb194d2..04968a1 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -62,9 +62,12 @@ git diff --name-only "$BASE"... ## 手動発火と結果確認 -`gh run list --limit 1` は既存の(今回発火した run とは別の)直近 run を拾って -しまう可能性があるため、発火前の時刻を記録しておき、それより後に作られた -`workflow_dispatch` イベントの run だけを新しい run として待つ: +`gh workflow run` は対象runのURLを返せる場合は返す(`gh`のバージョンやイベント +種別により返らないこともある)。まずそのURLからrun IDを取得することを優先し、 +取れなかった場合のみ `gh run list` へフォールバックする。フォールバック時は +「発火前の時刻より後に作られた `workflow_dispatch` runが複数ある」と一意に +特定できないため、候補が**ちょうど1件**のときだけ採用し、0件または複数件なら +中断する(同時刻の並行dispatchと取り違えないため): ```bash set -euo pipefail @@ -72,27 +75,43 @@ WORKFLOW=go-gin-ci.yml # 対象ワークフローファイル名(上表を N=34 # このfeatureブランチに対応するIssue番号に置き換える DISPATCHED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ) -gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase --ref "feature/issue-$N" - -# 新しいrunが現れるまでポーリングする(発火直後はまだ一覧に出ないことがある) -RUN_ID="" -for i in $(seq 1 20); do - RUN_ID=$(gh run list --repo bobtabo/authorization-showcase \ - --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ - --json databaseId,createdAt \ - --jq "[.[] | select(.createdAt > \"$DISPATCHED_AT\")] | sort_by(.createdAt) | .[0].databaseId // empty") - [ -n "$RUN_ID" ] && break - sleep 3 -done +DISPATCH_OUTPUT=$(gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase \ + --ref "feature/issue-$N" 2>&1) +echo "$DISPATCH_OUTPUT" + +RUN_ID=$(echo "$DISPATCH_OUTPUT" | grep -oE '/actions/runs/[0-9]+' | grep -oE '[0-9]+' | head -1 || true) + +if [ -z "$RUN_ID" ]; then + # URLが返らない場合のフォールバック。候補が複数/0件の場合は一意に特定できないため中断する + for i in $(seq 1 20); do + CANDIDATES=$(gh run list --repo bobtabo/authorization-showcase \ + --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ + --json databaseId,createdAt \ + --jq "[.[] | select(.createdAt > \"$DISPATCHED_AT\")]") + COUNT=$(echo "$CANDIDATES" | jq 'length') + if [ "$COUNT" -eq 1 ]; then + RUN_ID=$(echo "$CANDIDATES" | jq -r '.[0].databaseId') + break + elif [ "$COUNT" -gt 1 ]; then + echo "runの候補が複数あり一意に特定できません。手動でrun IDを確認してください" >&2 + exit 1 + fi + sleep 3 + done +fi if [ -z "$RUN_ID" ]; then echo "新しいrunを検出できませんでした" >&2 exit 1 fi -# 完了まで待ってログを見る。--exit-status でCI失敗をこのコマンド自体の -# 終了ステータスにも反映する +# 完了まで待ってログを見る。--exit-status はCI失敗時に非0で終了するため、 +# set -e 下でもgh run view --logが実行されるようステータスを一旦保存する +set +e gh run watch --repo bobtabo/authorization-showcase "$RUN_ID" --exit-status +WATCH_STATUS=$? +set -e gh run view --repo bobtabo/authorization-showcase "$RUN_ID" --log +exit "$WATCH_STATUS" ``` ## 注意 From f6c42cc1ccfc6b52708b5191d6913f41b2c1aaae Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 21 Aug 2026 19:30:40 +0900 Subject: [PATCH 17/18] =?UTF-8?q?fix:=20CodeRabbit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=EF=BC=88=E6=99=82=E5=88=BB=E6=AF=94=E8=BC=83?= =?UTF-8?q?=E3=81=AE=E7=A7=92=E7=B2=BE=E5=BA=A6=E5=95=8F=E9=A1=8C/?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E5=8F=96=E5=BE=97=E5=A4=B1=E6=95=97=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=82=B9=E3=83=86=E3=83=BC=E3=82=BF=E3=82=B9=E6=AC=A0?= =?UTF-8?q?=E8=90=BD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DISPATCHED_AT(秒精度のタイムスタンプ)による createdAt > DISPATCHED_AT 比較では、発火と同じ秒に既存runが作られていた場合に取り違える可能性が あった。時刻ではなく「発火前に存在したrunのdatabaseId集合」を記録し、 そこに含まれない新しいIDだけを候補とする方式に変更(comm -13で差分抽出) - gh run view --log 自体がgh CLIの既知の不具合等で失敗すると、 set -e下ではexit "$WATCH_STATUS"に到達せずrunの成否が失われていた。 ログ取得の失敗は握りつぶして警告のみ出し、必ず保存済みのWATCH_STATUSで 終了するように修正 - 差分抽出・複数候補時の中断・ログ取得失敗時のステータス保持の各ロジックを ローカルで動作確認済み 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 38 +++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index 04968a1..575963a 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -63,18 +63,21 @@ git diff --name-only "$BASE"... ## 手動発火と結果確認 `gh workflow run` は対象runのURLを返せる場合は返す(`gh`のバージョンやイベント -種別により返らないこともある)。まずそのURLからrun IDを取得することを優先し、 -取れなかった場合のみ `gh run list` へフォールバックする。フォールバック時は -「発火前の時刻より後に作られた `workflow_dispatch` runが複数ある」と一意に -特定できないため、候補が**ちょうど1件**のときだけ採用し、0件または複数件なら -中断する(同時刻の並行dispatchと取り違えないため): +種別により返らないこともある)。まずそのURLからrun IDを取得することを優先する。 +取れなかった場合は `gh run list` へフォールバックするが、`createdAt` は秒精度 +しかなく発火と同じ秒に既存runが作られていると誤って一致しうるため、時刻ではなく +**発火前に存在したrunのdatabaseId集合を記録し、そこに含まれない新しいID**を +候補とする。候補が複数見つかった場合は一意に特定できないため中断する: ```bash set -euo pipefail WORKFLOW=go-gin-ci.yml # 対象ワークフローファイル名(上表を参照して置き換える) N=34 # このfeatureブランチに対応するIssue番号に置き換える -DISPATCHED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ) +EXISTING_IDS=$(gh run list --repo bobtabo/authorization-showcase \ + --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ + --json databaseId --jq '.[].databaseId' | sort -u) + DISPATCH_OUTPUT=$(gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase \ --ref "feature/issue-$N" 2>&1) echo "$DISPATCH_OUTPUT" @@ -82,15 +85,15 @@ echo "$DISPATCH_OUTPUT" RUN_ID=$(echo "$DISPATCH_OUTPUT" | grep -oE '/actions/runs/[0-9]+' | grep -oE '[0-9]+' | head -1 || true) if [ -z "$RUN_ID" ]; then - # URLが返らない場合のフォールバック。候補が複数/0件の場合は一意に特定できないため中断する + # URLが返らない場合のフォールバック。発火前に存在しなかったIDだけを候補とする for i in $(seq 1 20); do - CANDIDATES=$(gh run list --repo bobtabo/authorization-showcase \ + CURRENT_IDS=$(gh run list --repo bobtabo/authorization-showcase \ --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ - --json databaseId,createdAt \ - --jq "[.[] | select(.createdAt > \"$DISPATCHED_AT\")]") - COUNT=$(echo "$CANDIDATES" | jq 'length') + --json databaseId --jq '.[].databaseId' | sort -u) + NEW_IDS=$(comm -13 <(echo "$EXISTING_IDS") <(echo "$CURRENT_IDS")) + COUNT=$(echo "$NEW_IDS" | grep -c . || true) if [ "$COUNT" -eq 1 ]; then - RUN_ID=$(echo "$CANDIDATES" | jq -r '.[0].databaseId') + RUN_ID="$NEW_IDS" break elif [ "$COUNT" -gt 1 ]; then echo "runの候補が複数あり一意に特定できません。手動でrun IDを確認してください" >&2 @@ -104,13 +107,18 @@ if [ -z "$RUN_ID" ]; then exit 1 fi -# 完了まで待ってログを見る。--exit-status はCI失敗時に非0で終了するため、 -# set -e 下でもgh run view --logが実行されるようステータスを一旦保存する +# 完了まで待つ。--exit-status はCI失敗時に非0で終了するため、set -e 下でも +# 後続のログ取得が実行されるようステータスを一旦保存する set +e gh run watch --repo bobtabo/authorization-showcase "$RUN_ID" --exit-status WATCH_STATUS=$? set -e -gh run view --repo bobtabo/authorization-showcase "$RUN_ID" --log + +# ログ取得自体の失敗(gh CLIの既知の不具合等)でこのステータスが失われないよう、 +# 失敗しても警告のみ出して握りつぶす +if ! gh run view --repo bobtabo/authorization-showcase "$RUN_ID" --log; then + echo "警告: ログの取得に失敗しました(run自体の結果には影響しません)" >&2 +fi exit "$WATCH_STATUS" ``` From f97ccfc7b964a2cd8e53f5ab727f3415b1c3810a Mon Sep 17 00:00:00 2001 From: bobtabo Date: Fri, 21 Aug 2026 20:24:43 +0900 Subject: [PATCH 18/18] =?UTF-8?q?fix:=20gh=20run=20list=E3=81=AE=E6=97=A2?= =?UTF-8?q?=E5=AE=9A20=E4=BB=B6=E5=88=B6=E9=99=90=E3=81=A7=E5=AF=BE?= =?UTF-8?q?=E8=B1=A1run=E3=82=92=E8=A6=8B=E9=80=83=E3=81=99=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=80=A7=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gh run listは既定で20件しか返さないため、対象workflow+branchで workflow_dispatchのrunが20件を超えて既に存在すると、EXISTING_IDS/ CURRENT_IDSの差分から対象runが漏れうる問題を修正。両方の呼び出しに --limit 100を指定した。 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .claude/skills/backend-ci-trigger/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude/skills/backend-ci-trigger/SKILL.md b/.claude/skills/backend-ci-trigger/SKILL.md index 575963a..2afd24e 100644 --- a/.claude/skills/backend-ci-trigger/SKILL.md +++ b/.claude/skills/backend-ci-trigger/SKILL.md @@ -76,7 +76,7 @@ N=34 # このfeatureブランチに対応するIssue番号 EXISTING_IDS=$(gh run list --repo bobtabo/authorization-showcase \ --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ - --json databaseId --jq '.[].databaseId' | sort -u) + --limit 100 --json databaseId --jq '.[].databaseId' | sort -u) DISPATCH_OUTPUT=$(gh workflow run "$WORKFLOW" --repo bobtabo/authorization-showcase \ --ref "feature/issue-$N" 2>&1) @@ -89,7 +89,7 @@ if [ -z "$RUN_ID" ]; then for i in $(seq 1 20); do CURRENT_IDS=$(gh run list --repo bobtabo/authorization-showcase \ --workflow="$WORKFLOW" --branch="feature/issue-$N" --event=workflow_dispatch \ - --json databaseId --jq '.[].databaseId' | sort -u) + --limit 100 --json databaseId --jq '.[].databaseId' | sort -u) NEW_IDS=$(comm -13 <(echo "$EXISTING_IDS") <(echo "$CURRENT_IDS")) COUNT=$(echo "$NEW_IDS" | grep -c . || true) if [ "$COUNT" -eq 1 ]; then