feat(PLAN58): Compose の profiles で付随サービス群を dev に触れずに後から起動・停止する (#189) - #191
Conversation
- 子プロセスの COMPOSE_PROFILES へ打ち消し用のプロファイル名を入れる (決定 7) - devbase up の起動は既定のサービスを明示し、停止は --profile '*' で全体を対象にする - プロファイル名とサービスの対応を docker compose config で解決する (決定 1) - 実装計画 issues/PLAN58_compose-profiles-impl.md を追加 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add characterization tests for malformed ps JSON, ps execution errors, and comma-separated active profiles. Production code is unchanged. Item-Id: R1-001 Round: 1 Impl-Runtime: codex Impl-Model: default
改修計画 — devbasex/devbase #191
ラウンド 1(実装 codex / レビュー agy / kiro)R1-001 —
|
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| error | unit | — | codex / kiro | 採用 | 1 |
なぜ: cmd_profile_list の RUNNING 列は _running_services の解析結果で決まる。既存テストは returncode 0 + 正常 JSON (running/partial/stopped) と returncode 非0 (不明) と JSON 配列形式を固定しているが、returncode 0 で stdout が JSON として壊れている (json.loads が ValueError) 経路は固定されていない。この経路も None を返して RUNNING を不明にするが、解析部を組み替えると例外の握り (ValueError) が抜けて落ちる方向へ退行しうる。
手順: 1. 既存project fixtureとFakeComposeで生成物およびtestプロファイルのappサービスを用意する。
2. 外部subprocess.runのps応答だけを、終了コード0で壊れたJSONを返す場合とOSErrorを送出する場合にパラメータ化する。config応答は正常に保つ。
3. 公開入口cmd_profile_list()を実行し、現状の終了コード0を固定する。
4. 標準出力からtestの行を取り出し、appと稼働状況の不明が残ることを確認する。表全体の文字列・空白幅やprivate関数は固定しない。
R1-002 — lib/devbase/commands/container.py#cmd_profile_up
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| error | unit | — | codex / agy | 検証中 | 1 |
なぜ: 既存テストはCompose起動失敗とdeploy実行失敗を固定しているが、deployが存在し、起動前のcurrent_project_configがDevbaseErrorになる経路は固定していない。簡易実行では終了コード1となり、Compose起動に進まなかった。
手順: 1. 既存project fixtureで生成済みComposeと、実行すると印を残すdeployを用意する。
2. プロファイル解決は成功させ、公開依存current_project_configをDevbaseErrorを返すスタブにする。外部プロセスは既存FakeComposeで記録する。
3. 公開入口cmd_profile_up("test")を実行し、現状の終了コード1を固定する。
4. 外部Composeへの起動要求が出ず、deployの印も作られないことを確認する。内部ヘルパーの呼び出し順・回数やログ全文は比較しない。
R1-003 — lib/devbase/project/runtime.py#hook_env
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| boundary | unit | — | agy | 採用 | 1 |
なぜ: hook_env は DEVBASE_ACTIVE_PROFILES をカンマ区切りで生成する仕様を持つが、既存テストは 0 件と 1 件のみで複数プロファイル指定時の結合の境界値が未固定である
手順: 1. テスト用の ProjectConfig を準備する
2. active_profiles に複数のプロファイル名を渡して hook_env を呼び出す
3. 返却された環境変数の DEVBASE_ACTIVE_PROFILES がカンマ区切りで正しく結合されていることを検証する
見送った項目
| ラウンド | 対象 | 兆候・経路 | 理由 |
|---|---|---|---|
| 1 | lib/devbase/utils/docker.py#compose_env |
branch | 1 ラウンドの採用上限 3 件を超えた |
| 1 | lib/devbase/utils/docker.py#docker_compose_down |
branch | 1 ラウンドの採用上限 3 件を超えた |
cmd_profile_up で deploy 存在時に current_project_config が DevbaseError を送出した際の終了コードと未実行の振る舞いを固定する。 Item-Id: R1-002 Round: 1 Impl-Runtime: agy Impl-Model: default
構造改善(cross-refactoring)の結果
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | kiro | REQUEST_CHANGES
プロファイルの有効/無効を COMPOSE_PROFILES=__devbase_none__ で打ち消し、必要な経路だけ --profile で有効化する設計は一貫している。ただし --profile と env の優先関係に依存する箇所が 2 つあり、稼働状況の表示と down の網羅性に影響しうる(インライン参照)。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | agy | APPROVE
PLAN58 の仕様と設計(プロファイル解決、--no-deps 起動、--profile '*' 停止、環境分離、TUI・CLI連携、フック通知)が整合しており、修正を要する問題は認められません。
非アクティブなプロファイルのサービスを ps に出さない版でも RUNNING が stopped に張り付かないよう、_running_services の ps に --profile '*' を付ける。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
/ndf:fix サマリ(cross-review round 1)対応件数: critical=0 / major=1 / minor=0 (合計 1 件) 詳細
検証
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | kiro | REQUEST_CHANGES
_run_deploy_pipeline の起動サービス解決が down の後に来ており、構成解決を down 前に済ませるという設計不変条件を破っている。詳細はインライン参照。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | codex | REQUEST_CHANGES
TUI のプロファイル取得にも、CLI と同じ対象プロジェクトの環境準備を適用してください(指摘1件)。
- _run_deploy_pipeline: default_services を docker_compose_down より前に求める。 config --services が失敗しても稼働中の環境を落とさず旧構成を書き戻す - TUI の _profile_names: container.project_profile_names を _preserve_cwd_env の 中で呼び、対象プロジェクトの env と機密を載せてから Compose に解決させる。 切替手順は _enter_project として _dispatch_lifecycle と共有する Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🔧 /ndf:fix サマリ (round 2)対応件数: critical=0 / major=2 / minor=0 (合計 2 件) 詳細
検証
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | codex | REQUEST_CHANGES
修正が必要な指摘は1件です。関連テスト94件は通過しました。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | agy | APPROVE
要求仕様 (PLAN58) の受け入れ条件を満たしており、不整合や退行は見出せませんでした。
_preserve_cwd_env は os.environ の値だけを戻し、runtime の注入履歴を戻して いなかった。ハンドラの中で別プロジェクトへ切り替えると、値は切替元へ戻るのに 履歴は切替先のものになり、次の clear_injected が切替元固有の機密を落とせず 次に操作するプロジェクトの Compose 子プロセスへ渡っていた。メニュー表示時の プロファイル照会と、既存の dispatch_lifecycle の経路の両方が該当する。 runtime に snapshot_injected / restore_injected を足し、_preserve_cwd_env が 入口で履歴を控え、finally で os.environ と同時に書き戻す。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🔧 /ndf:fix サマリ (round 3)対応件数: critical=0 / major=1 / minor=0 (合計 1 件) 詳細
検証
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | kiro | COMMENT
設計と正確性は妥当で、プロファイル解決・--no-deps・COMPOSE_PROFILES 打ち消し・フック委譲の各経路はテストで固定されている。ブロッカーは無い。以下は minor の修正提案のみ。
lib/devbase/utils/docker.py:225—docker_compose_downが常に--profile '*'を付けるが、--profileへのワイルドカード*は比較的新しい Compose(概ね v2.24 以降)でしか「全プロファイル有効化」として解釈されない。README の前提は「Compose v2.x 以上」なので、それより古い v2.x では*がリテラルのプロファイル名として扱われ、profile upで起動したサービスがdevbase downで残る(downはエラーを握り潰すため気づきにくい)。前提の最小 Compose バージョンを引き上げて明記するか、この劣化を docs/troubleshooting に注記するのが安全。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | agy | APPROVE
全変更点およびラウンド 1〜3 の修正(TUI 復元境界での機密履歴の復元、稼働状況 ps のプロファイル指定、事前解決順序等)を精査し、追加の修正アクションが必要な問題がないことを確認しました。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | kiro | APPROVE
PLAN58 の Compose profiles 実装をレビューした。設計(決定 1〜8 との対応)、正確性、責務分割、後方互換いずれも修正提案なし。compose_env() による COMPOSE_PROFILES 打ち消しは docker_compose / _compose_run / config 読み取り 2 か所 / editor の ps まで一貫して適用され、profile up の --no-deps + 明示サービス指定で dev 非再作成、docker_compose_down の --profile '*' で残留防止という不変条件が保たれている。_preserve_cwd_env の注入履歴 snapshot/restore(round 3 指摘)も値復元と整合。テストは境界・失敗系・版差(ps の JSON 形式)まで押さえており、追加の指摘は無い。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🔧 /ndf:fix サマリ(最終スイープ)対応件数: critical=0 / major=0 / minor=1 (合計 1 件) 詳細
検証
未解決スレッド数(Resolve 後に GraphQL で数え直し): 0 |
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
構造改善で範囲外と判断した |
リリース後テスト対象の版: v3.5.0(2026-09-17 09:28 公開、タグ
合否: 合格(13 件中 10 件を実施し全件合格、3 件は保留) |
Summary
issues/PLAN58_compose-profiles.mdissues/PLAN58_compose-profiles-design.md/ 決定:issues/PLAN58_compose-profiles-decisions.mdissues/PLAN58_compose-profiles-impl.md(この PR で追加。実装中に範囲へ入れたものを含む)devbase project profile {up,down,list} [name] <profile>を追加(container/ctも同じ。[name]なし)up: そのプロファイルのサービスを全件明示して--no-depsで起動し、./deployをDEVBASE_ACTIVE_PROFILES=<profile>で生成物の全インスタンスへ呼び直すdown:stop→rm -f(down <サービス>は依存元の dev まで消すため使わない)。ボリュームは残すlist:PROFILE / SERVICES / RUNNING(ps は--profile '*'で問い合わせる)。デーモンへ接続できなければ不明で exit 0devbase upの起動は既定のサービスを明示(解決は既存コンテナの停止より前)し、devbase downとup冒頭の停止は--profile '*'で全体を対象にするdocker_compose/ps/logs/config2 か所 / エディタのps)へCOMPOSE_PROFILES=__devbase_none__を渡すdevbase listの起動中の行の操作メニューに「テスト用サーバ起動 / 停止」を追加(プロファイルを持つプロジェクトだけ。解決は対象プロジェクトの env と機密を載せて行い、TUI セッションへ残さない)_preserve_cwd_env()が機密の注入履歴を戻さず、別プロジェクトを続けて操作すると最初のプロジェクト固有の機密が次の Compose へ渡る欠陥(PR 前からの経路にもあった)docs/plugin-dev/compose-profiles.md)・CHANGELOG を更新レビュー
cmd_scaleの抽出)はこの PR で触れていない既存コードのため適用を止めた(別途起票予定)Test plan
head
d08af76(コード変更はbf2cf52まで。以降は CHANGELOG のみ)uv run pytest tests/ -q -p no:cacheprovider(全体)→ 2600 passed, exit=0(2026-09-17 07:31)uvx ruff check --select=E9,F63,F7,F82 lib(CI と同じ選択)→ exit=0python -m compileall -q lib(3.10 / 3.11 / 3.12)→ exit=0bash -n etc/devbase-completion.bash→ exit=0alpine:3、bf2cf52で再実行): devbase の関数(default_services→docker_compose_up/cmd_profile_up/cmd_profile_list/cmd_profile_down/docker_compose_down)を実コンテナで通し 21/21 OKupは dev-1/dev-2 だけ。profile up testで app/db が起動し dev の Container ID・StartedAt不変。./deployはproject.ymlを scale 1 に書き換えても生成物の 2 台へtestで走る。profile downで app/db のコンテナだけ消え、名前付きボリュームは残る。profile up後のdownで全コンテナと network が消えるdepends_on: [dev-1, dev-2](required なし)でも dev 不変profile upでも dev は再作成されない(参考:--no-depsなしの dry-run では dev-1/dev-2 が Recreate になる)depends_on required: falseを持つ構成でprofile up→profile downしても dev 不変COMPOSE_PROFILES=testを環境変数に置いても、.envに書いて dev→db の依存を持たせても、upは dev だけ。その後のprofile up/downは効くprofilesを持たない構成のup/downは従来どおり(全起動 / 何も残らない)DOCKER_HOSTを存在しないソケットにするとlistは不明で exit 0、up/downは exit 1devbase up全経路(イメージ準備・機密注入を含む)、devbase listの TUI の目視、Compose 2.20.0 以上 5.x 未満での--profile '*'🤖 Generated with Claude Code