Skip to content

Commit 8eeb2d7

Browse files
takemi-ohamaclaude
andcommitted
fix(wrapper): ct alias を name 解決経路に統合 + 衝突注意を強化
PR #33 cross-review round2 codex 指摘対応。 - ct alias 修正 (minor, bin/devbase:319): name 解決 case の `project|container)` を `project|container|ct)` に拡張。 `ct up <name>` が container と同じ strip/chdir 経路を通るようにし、 従来 `unrecognized arguments: <name>` になっていた回帰を解消。 alias `ct` 自体は cli.py 側で container へ解決されるため Python へは `ct` のまま渡す。回帰テスト test_wrapper_ct_up_name_cds_and_strips を追加。 - 衝突注意の強化 (major=却下に伴うドキュメント補強): login/build/scale の存在性ベース name 解決は意図的設計のため挙動は維持。 positional 引数 (index/image/service) が実在プロジェクト名と衝突した際に project 解決が優先され引数解釈が変わる footgun を bin/devbase コメントと docs/user/cli-reference.md に明記。回避策 (対象プロジェクト内で実行 / 明示 切り替え) も併記。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 739bd09 commit 8eeb2d7

3 files changed

Lines changed: 46 additions & 1 deletion

File tree

bin/devbase

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,10 +313,31 @@ _DEVBASE_ARGS=("${@:2}")
313313
# 集合 + shell 実装の build を含む。
314314
# cli.py 側でサブコマンドを追加/削除した際は両リストの更新漏れに注意すること
315315
# (cli.py の _add_project_parser / SHORTCUTS にも対の注記あり)。
316+
#
317+
# ⚠ 衝突注意 (footgun): トップレベルシノニムの name 解決は「存在性ベース」で
318+
# 行うため、本来 positional 引数として渡したい値が実在プロジェクト名
319+
# ($DEVBASE_ROOT/projects/<name>) と一致した場合、その引数が name と解釈され
320+
# project 解決 (cd) が優先されて引数の意味が変わる。具体的には:
321+
# - `devbase login <index>` の index が実在プロジェクト名と一致
322+
# (例: projects/2 が存在する状態で `devbase login 2`) → index=2 ではなく
323+
# project `2` への cd になり、login の対象が変わる。
324+
# - `devbase build <image>` の image が実在プロジェクト名と一致
325+
# (例: projects/web が存在する状態で `devbase build web`) → image=web では
326+
# なく project `web` への cd になり、ビルド対象が変わる。
327+
# - `devbase scale <service>` の service 引数も同様に化けうる。
328+
# これはトップレベル build/login/scale を「そのプロジェクトを操作」と解釈する
329+
# 意図的設計 (存在性ベース判定) のトレードオフであり、挙動としては仕様である。
330+
# 回避策: 衝突時は対象プロジェクトのディレクトリ内で実行するか、明示的に
331+
# そのプロジェクトへ切り替えてから (cd 済みの状態で) コマンドを実行すること。
332+
# こうすれば name 解決トークンを与える必要がなくなり、index/image/service を
333+
# 意図どおり渡せる。
316334
_PROJECT_NAME_SUBCOMMANDS=" up down ps logs scale "
317335
_NAME_RESOLVABLE_SHORTCUTS=" up down ps scale login build "
318336
case "$_resolved_cmd" in
319-
project|container)
337+
project|container|ct)
338+
# `ct` は container の alias (cli.py: add_parser('container', aliases=['ct']))。
339+
# name 解決経路でも container と同じ strip/chdir を通すため分岐に含める。
340+
# _resolved_cmd は `ct` のまま python に渡してよい (cli.py 側で alias 解決済み)。
320341
if [[ "$_PROJECT_NAME_SUBCOMMANDS" == *" ${2:-} "* ]] \
321342
&& maybe_cd_project "${3:-}"; then
322343
_DEVBASE_ARGS=("${2:-}" "${@:4}")

docs/user/cli-reference.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,15 @@ cd $DEVBASE_ROOT/projects/adminer && devbase project up
160160
> ラッパー (`bin/devbase`) の存在性判定(`$DEVBASE_ROOT/projects/<name>` が実在すれば cd)で
161161
> 名前解決されます(実在しない場合は従来どおり `index` / `image` として下流へ渡されます)。
162162
163+
> **⚠ 衝突注意(footgun):** トップレベルシノニムの名前解決は「存在性ベース」のため、本来
164+
> positional 引数として渡したい値が実在プロジェクト名と一致すると、その引数が名前解決の対象と
165+
> なり project への `cd` が優先されて引数の意味が変わります。例えば `projects/2` が存在する状態の
166+
> `devbase login 2` は index=2 ではなく project `2` への操作に、`projects/web` が存在する状態の
167+
> `devbase build web` は image=web ではなく project `web` のビルドに化けます(`scale` の service 引数
168+
> も同様)。これは「`build carmo` / `login carmo` でそのプロジェクトを操作する」意図的設計の
169+
> トレードオフです。**回避策:** 衝突する場合は対象プロジェクトのディレクトリ内で実行するか、
170+
> 明示的にそのプロジェクトへ切り替えてから(`cd` 済みの状態で)コマンドを実行してください。
171+
163172
### `devbase project up`
164173

165174
コンテナを起動します。

tests/cli/test_project_name_resolution.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,21 @@ def test_wrapper_project_build_keeps_image_positional(wrapper_root):
420420
assert _python_args(r) == "project build carmo", r.stdout
421421

422422

423+
def test_wrapper_ct_up_name_cds_and_strips(wrapper_root):
424+
"""`ct up carmo` は container alias として name 解決される (codex 指摘 #319)。
425+
426+
`ct` は cli.py で container の alias (add_parser('container', aliases=['ct']))
427+
のため、wrapper の name 解決 case でも `container` と同じ strip/chdir 経路を
428+
通す。`ct` 自体は strip せず Python へ渡し、name のみ strip する。
429+
"""
430+
r = _run_wrapper(["ct", "up", "carmo"], wrapper_root)
431+
assert "unknown command" not in r.stderr.lower(), r.stderr
432+
assert "unrecognized arguments" not in r.stderr.lower(), r.stderr
433+
assert _pwd(r).endswith("/projects/carmo"), r.stdout
434+
# name は strip されるが alias `ct` は保持して Python へ渡す
435+
assert _python_args(r) == "ct up", r.stdout
436+
437+
423438
def test_wrapper_project_login_keeps_index_positional(wrapper_root):
424439
"""`project login carmo` の carmo は index positional として素通しする。
425440

0 commit comments

Comments
 (0)