fix(PLAN61): bin/devbase の位置引数の解決を名前の形・衝突・ヘルプ・グループで正す (#146, #142, #196, #200) - #207
Conversation
- utils/names.py を新設し、SINGLE_SEGMENT_NAME_PATTERN と is_single_segment_name に 名前の形の規則を 1 か所で置く - _resolve_project_name は形に合わない名前で chdir せず、env も候補も読まずに False - _named_lifecycle_project は形に合わない名前で実在と store_for を見ずに None - _build_single_image の _IMAGE_NAME_RE を同じ関数へ寄せる - 実装計画を issues/PLAN61_name-resolution.md の末尾に足す Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- is_single_segment_name と _SINGLE_SEGMENT_NAME_RE を足し、maybe_cd_project の入口で 形に合わない値 (フラグ・空・`..` や `/` を含む値) を名前として扱わない - tests/cli/conftest.py に exec_wrapper を新設。bin/devbase を tmp へ複製して起動し、 uv だけを PATH で差し替える (maybe_cd_project と cmd_build は本物のまま動く) - wrapper の正規表現と Python の定義の同期テスト、LC_ALL=C の確認 (café) を足す Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- build_usage を足し、build の引数のどこかに -h / --help があれば name 解決より前に 使い方を出して終了コード 0 で終わる (build carmo --help でも cd と env の読み込みを起こさない) - --context --help は使い方、--context=--help は下流へ渡す (決定 8) - exec_wrapper で cmd_build を本物のまま動かし、=== Building devbase images === が 出ないことを確かめる Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…イメージをビルドする (#142) - name 解決の case に build の分岐を足す。<x> が名前の形に合い containers/<x> が実在すれば name 解決を通さず Python の単体ビルドへ渡し、projects/<x> もあれば stderr に 1 行知らせる - projects/ にだけある名前は今と同じくプロジェクトへ cd して cmd_build へ進む - build 分岐の PLAN49 の注記を新しい振り分けに合わせる Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> EOF )
- name 解決の case を project だけにし、container <sub> <name> / ct <sub> <name> は 実在する名前でも cd せずそのまま Python へ渡す (parser が [name] を持たないため usage エラー) - name 解決の説明コメント (衝突注意の footgun) を変更後の規則に書き替え、cli.py の 同期注意を project だけに直す - test_wrapper_ct_up_name_cds_and_strips を受け入れ条件 12 のテストへ置き換える Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…と CHANGELOG に書く 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 R1-001, R1-002, and R1-004 without changing production code. Item-Id: R1-001 Round: 1 Impl-Runtime: codex Impl-Model: default
改修計画 — devbasex/devbase #207
ラウンド 1(実装 codex / レビュー agy / kiro)R1-001 —
|
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| branch | unit | — | kiro | 採用 | 1 |
なぜ: _dispatch は project サブコマンドを list / migrate-config / lifecycle に振り分ける。project list・top-level list・project up の 3 分岐は test_project_list / test_project_dispatch で固定されているが、subcommand=='migrate-config' を _require_devbase_root + cmd_project_migrate_config へ振り分ける分岐は固定されていない (ハンドラ単体は別テストにあるが dispatch 経路の配線は未固定)。
手順: 1. DEVBASE_ROOT を tmp_path に設定する
2. project.cmd_project_migrate_config を差し替えて (root, args) を記録する
3. args を subcommand='migrate-config' で作り _dispatch('project', args) を呼ぶ
4. 差し替えたハンドラが tmp_path を root として 1 回呼ばれ、戻り値が伝わることを assert する
R1-002 — lib/devbase/cli.py#_expand_argv
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| branch | unit | — | kiro | 採用 | 1 |
なぜ: _expand_argv には 4 つの解決分岐がある (トップレベル / SUBCMD_MAP / plugin repo sub-subcommand)。トップレベルと SUBCMD_MAP の分岐は test_prefix_resolution / test_project_dispatch で固定されているが、sys.argv[1] in ('plugin','pl') and sys.argv[2]=='repo' の 4 番目の引数を repo_subcmds (add/remove/list/refresh) で解決する分岐だけが固定されていない。
手順: 1. sys.argv を ['devbase','plugin','repo','r'] にして _expand_argv を呼び、実行結果を確認する
2. sys.argv[3] が 'refresh' に解決されることを assert する
3. 一意でない prefix (例 'r' が複数候補) の現状も実行して確認し、返る値をそのまま期待値にする
4. plugin repo でない argv (例 env) では 4 番目が書き換わらないことを固定する
R1-003 — lib/devbase/cli.py#_require_devbase_root
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| error | unit | — | kiro | 検証中 | 1 |
なぜ: _require_devbase_root は DEVBASE_ROOT 未設定時に sys.exit(1) する共有ヘルパ (list / migrate-config / _ROOT_COMMANDS 経路が通る) だが、未設定→終了コード 1 のエラー分岐が固定されていない。設定済みで Path を返す正常系も含めて未固定。
手順: 1. DEVBASE_ROOT を delenv して _require_devbase_root() を呼び、SystemExit が送出され code が 1 であることを assert する
2. DEVBASE_ROOT を tmp_path に設定した場合は Path(tmp_path) を返すことを assert する (表示文字列ではなく戻り値と終了コードだけを見る)
R1-004 — lib/devbase/cli.py#main
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| error | unit | — | codex | 採用 | 1 |
なぜ: 既存テストは注入対象の選択・スキップと main からの名前伝達を固定しているが、CLI の任意注入が DevbaseError または一般例外で失敗しても処理を継続する経路は固定されていない。container の required=False のテストは別の例外処理であり、cli.main の保証にはならない。
手順: 1. sys.argv を devbase project ps、DEVBASE_ROOT をテスト用ディレクトリに設定し、公開 main を呼ぶ。_load_secret_env と _dispatch は本物を通し、外部の機密注入境界 runtime.inject とコマンドハンドラ cmd_project をスタブにする。
2. runtime.inject が DevbaseError と RuntimeError をそれぞれ投げ、cmd_project が 7 を返す独立ケースを実行する。実測済みの main の戻り値 7 を固定し、注入失敗がコマンド結果を置き換えないことを示す。
3. 別ケースとして注入を成功させ、cmd_project が DevbaseError を投げると、実測済みの戻り値 1 になることを固定する。
4. 各ケースを独立したテストにし、環境と argv を復元する。ログ全文や内部関数の呼び出し回数は検証しない。
R1-005 — lib/devbase/cli.py#main
| 兆候・経路 | 手法・階層 | 重要度 | 提案元 | 状態 | コミット |
|---|---|---|---|---|---|
| normal | unit | — | agy | 検証中 | 1 |
なぜ: トップレベルショートカット(例: devbase up )経由で実行された場合に、解析されたプロジェクト名が _load_secret_env へ正しく渡る主要経路が main エントリで固定されていない
手順: 1. sys.argv を ['devbase', 'up', 'carmo'] に設定し、_load_secret_env と _dispatch をモックする
2. cli.main() を実行する
3. _load_secret_env に cmd='up', name='carmo' が渡されることを検証する
見送った項目
| ラウンド | 対象 | 兆候・経路 | 理由 |
|---|---|---|---|
| 1 | lib/devbase/commands/container.py#cmd_build |
branch | 1 ラウンドの採用上限 5 件を超えた |
| 1 | lib/devbase/commands/container.py#cmd_logs |
branch | 1 ラウンドの採用上限 5 件を超えた |
| 1 | lib/devbase/commands/container.py#cmd_project |
error | 1 ラウンドの採用上限 5 件を超えた |
| 1 | lib/devbase/utils/names.py#is_single_segment_name |
boundary | 1 ラウンドの採用上限 5 件を超えた |
現状固定テストを 2 件追加する(対象コードは変更しない)。 - test_project_dispatch.py: _require_devbase_root の DEVBASE_ROOT 未設定→ SystemExit(1) のエラー分岐と、設定済み→Path 返却の正常系を固定する。 - test_secret_injection.py: トップレベルショートカット (devbase up <name>) 経由でも解析された名前が cmd='up' とともに _load_secret_env へ渡る主要 経路を main エントリで固定する。 Item-Id: R1-003 Round: 1 Impl-Runtime: kiro Impl-Model: default
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | kiro | APPROVE
位置引数の名前解決を「名前の形・衝突・ヘルプ・グループ」で正す 4 件を、共有ヘルパ utils/names.is_single_segment_name に集約し、shell (bin/devbase) と Python 3 入口 (_resolve_project_name / _named_lifecycle_project / _build_single_image) の規則を一致させている。.. / の連結前弾き、build --help の name 解決前判定、build <x> の containers 優先、container/ct の name 解決除外がいずれも受け入れ条件(16 件)に検証テストで対応し、影響ファイルのテスト 294 件をローカルで再実行して全通過を確認した(uv run --locked pytest)。設計・正確性・セキュリティ(パストラバーサル対策)とも修正提案なし。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | codex | APPROVE
新規の修正指摘はありません。CLI・名前検証テスト 856 件成功、bash 構文検査成功。
検証結果(quality-gates / standard)head
構造改善: cross-refactoring(提案 9 件 → 採用 5 件 / 見送り 4 件、適用 2 ラウンド、最終ゲートの全体テストを通過) 受け入れ条件(issues/PLAN61_name-resolution.md): 16/16 満たす。条件ごとの検証手段は同ファイルのチェックリストに記載(テスト名つき)。 未検証の項目: 実機での |
Summary
bin/devbaseの name 解決とbuild)の分岐にかかる 4 件を 1 本で直す。[A-Za-z0-9][A-Za-z0-9._-]*に限り、..や/を含む値を名前として扱わない(cd もprojects/の外のenvの読み込みもしない)。Python 側の 3 入口(_resolve_project_name/_named_lifecycle_project/_build_single_image)は新設のutils/names.pyの同じ規則を使うbuild <x>でcontainers/<x>とprojects/<x>が両方あれば、イメージとしてビルドし、プロジェクトのビルド方法を stderr で知らせるbuild --help/-hがビルドを始めず、bin/devbaseが使い方を出して終了コード 0container/ctグループを name 解決から外す(container up carmoは argparse の usage エラー、終了コード 2)tests/cli/conftest.pyのexec_wrapper(bin/devbaseを tmp へ複製しuvだけを差し替えて実プロセスで起動)で name 解決の経路を固定要求と受け入れ条件:
issues/PLAN61_name-resolution.md(16 件、検証手段つき)/ 設計:issues/PLAN61_name-resolution-design.md(#204 でマージ済み)Closes #146
Closes #142
Closes #196
Closes #200
Test plan
uv run --locked pytest -q tests/→2813 passed、exit=0PATH="/bin:$PATH" uv run --locked pytest -q tests/cli(bash 3.2.57)→824 passed、exit=0uvx --from shellcheck-py shellcheck --severity=error bin/devbase→ exit=0uvx ruff check --select=E9,F63,F7,F82 lib→ exit=0devbase build --help・devbase build ../etc・devbase container up carmoを打ち、出力と終了コードを見る🤖 Generated with Claude Code