Skip to content

Commit 37b6e3f

Browse files
takemi-ohamaclaude
andcommitted
feat(up): devbase up 時の .vscode/tasks.json 自動配置を廃止
統合ターミナル自動表示用の folderOpen tasks.json をユーザのワークスペースへ 自動配置する挙動は余計だったため削除する。VS Code 自動オープン (PR #69/#70) はそのまま維持する。 - opener: build_folder_open_tasks_json() / is_open_terminal_enabled() を削除 - container: _maybe_place_terminal_task() と cmd_up/dispatch の open_terminal 引数を削除 - cli: --open-terminal / --no-open-terminal フラグを削除 - keys: DEVBASE_OPEN_TERMINAL 定数を削除 - docs: DEVBASE_OPEN_TERMINAL の記述を削除 - tests: 関連テストを削除 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d6dbcfd commit 37b6e3f

7 files changed

Lines changed: 13 additions & 253 deletions

File tree

docs/user/environment-variables.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,8 @@ devbase はホストマシンの認証情報を自動収集し、コンテナ内
151151
| `DEVBASE_OPEN_INDEX` | scale 時に開く dev インスタンス番号(既定: `1`|
152152
| `DEVBASE_EDITOR_SSH_HOST` | Remote-SSH 跨ホスト構成での ssh-remote ホスト名(例 `mac2`)。**通常は `~/.vscode-server` から自動検出**され不要。検出が外れる場合のみ明示。下記「跨ホスト」参照 |
153153
| `DEVBASE_EDITOR_DOCKER_CONTEXT` | 跨ホスト時に ssh 先で使う docker context(既定: ホストの `docker context show`|
154-
| `DEVBASE_OPEN_TERMINAL` | 真で `up` 後に folderOpen ターミナル用 `.vscode/tasks.json` を配置(**既定: ON**|
155154

156-
都度の上書きは CLI フラグで行います: `devbase up --open` / `--no-open` / `--open-index N` / `--open-terminal` / `--no-open-terminal`(env より優先)。
157-
158-
### 起動時に統合ターミナルを自動表示(`DEVBASE_OPEN_TERMINAL`
159-
160-
`up` 時に、開く dev コンテナのワークスペース(`/work/$GIT_REPO`)へ folderOpen タスク(`.vscode/tasks.json`)を `docker exec` で配置します(既存があれば変更しません)。VS Code はフォルダを開いた時にこのタスクで統合ターミナルを前面表示します。
161-
162-
VS Code 公式には「起動時にターミナルを開く」専用設定が無く、folderOpen タスクが唯一の方法です。なお自動実行には次の 2 つの **VS Code クライアント側ユーザー設定**が関わり、devbase からは制御できません(いずれも application/user スコープ専用):
163-
164-
- **Workspace Trust**: 信頼していないフォルダではタスクは自動実行されません(初回は「フォルダを信頼」が必要)。
165-
- **`task.allowAutomaticTasks`**: 既定 `off` ではフォルダごとに 1 回「自動タスクを許可」を尋ねます。`on` にするとプロンプト無しで実行されます。
166-
167-
→ 実際は「初回のみ信頼(+許可)クリック、以降は自動でターミナルが開く」挙動になります。無効化は `DEVBASE_OPEN_TERMINAL=0` または `devbase up --no-open-terminal`
155+
都度の上書きは CLI フラグで行います: `devbase up --open` / `--no-open` / `--open-index N`(env より優先)。
168156

169157
### 実行コンテキスト別の挙動
170158

lib/devbase/cli.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,6 @@ def _add_open_args(parser):
112112
parser.add_argument('--open-index', dest='open_index', type=int, default=None,
113113
metavar='N',
114114
help='Container index to open (default: 1)')
115-
parser.add_argument('--open-terminal', dest='open_terminal', action='store_true',
116-
default=None,
117-
help='Place .vscode/tasks.json so the integrated terminal '
118-
'auto-opens on folder open (overrides DEVBASE_OPEN_TERMINAL)')
119-
parser.add_argument('--no-open-terminal', dest='open_terminal', action='store_false',
120-
help='Do not place the folderOpen terminal tasks.json '
121-
'(overrides DEVBASE_OPEN_TERMINAL)')
122115
return parser
123116

124117

lib/devbase/commands/container.py

Lines changed: 6 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,7 @@ def _dispatch_lifecycle(args) -> int:
327327
'up': lambda: cmd_up(project_name=project_name,
328328
scale=getattr(args, 'scale', None),
329329
open_editor=getattr(args, 'open_editor', None),
330-
open_index=getattr(args, 'open_index', None),
331-
open_terminal=getattr(args, 'open_terminal', None)),
330+
open_index=getattr(args, 'open_index', None)),
332331
'down': lambda: cmd_down(),
333332
'login': lambda: cmd_login(index=getattr(args, 'index', '1')),
334333
'ps': lambda: cmd_ps(all_containers=getattr(args, 'all', False)),
@@ -397,8 +396,8 @@ def _resolve_open_index(open_index: Optional[int], scale: int) -> int:
397396
"""開く dev インスタンス番号を解決する (CLI 引数 → env ``DEVBASE_OPEN_INDEX`` → 既定 1)。
398397
399398
``1..scale`` の範囲外 (0・負数・``scale`` 超過) は存在しないコンテナを指し原因不明な
400-
起動失敗を招くため、警告を出して 1 へフォールバックする。:func:`_maybe_place_terminal_task`
401-
と :func:`_maybe_open_editor` で共有し env フォールバック・範囲チェックの重複を避ける
399+
起動失敗を招くため、警告を出して 1 へフォールバックする。:func:`_maybe_open_editor`
400+
env フォールバック・範囲チェックを共有する
402401
"""
403402
if open_index is None:
404403
raw = os.environ.get('DEVBASE_OPEN_INDEX')
@@ -432,8 +431,7 @@ def _maybe_open_editor(project_name: str, open_flag: Optional[bool],
432431
未指定なら ``.docker-compose.scale.yml`` が存在すればそれ、無ければ None。
433432
434433
``container_name`` が渡されれば :func:`opener.open_editor` 内の
435-
``resolve_container_name`` (= ``docker compose ps``) をスキップして再利用する
436-
(``_maybe_place_terminal_task`` が既に解決済みの名前を使い回し二重実行を避ける)。
434+
``resolve_container_name`` (= ``docker compose ps``) をスキップして再利用する。
437435
"""
438436
from devbase.editor import opener
439437

@@ -464,69 +462,9 @@ def _maybe_open_editor(project_name: str, open_flag: Optional[bool],
464462
logger.warning("エディタの自動オープンに失敗しましたがデプロイは成功しています: %s", e)
465463

466464

467-
def _maybe_place_terminal_task(project_name: str, open_flag: Optional[bool],
468-
open_index: Optional[int], scale: int,
469-
compose_file=None) -> Optional[str]:
470-
"""`up` 後、開く dev コンテナの作業ディレクトリへ folderOpen ターミナル tasks.json を配置。
471-
472-
フォルダを開いた時に統合ターミナルを自動表示するための ``.vscode/tasks.json`` を、
473-
対象 dev インスタンスのワークスペース (``/work/$GIT_REPO``) に置く。作業ディレクトリは
474-
コンテナ内 (named volume) のためホストから直接書けず、起動済みコンテナへ ``docker exec``
475-
で書き込む。**既存の ``.vscode/tasks.json`` があれば一切触らない**。
476-
477-
有効判定は ``open_flag`` (CLI ``--open-terminal``/``--no-open-terminal``) が優先、None なら
478-
env ``DEVBASE_OPEN_TERMINAL`` (既定 ON)。配置失敗は warning に握り潰し ``up`` を倒さない。
479-
``open_index`` は開くインスタンスに合わせる (範囲外は 1 へフォールバック)。
480-
481-
解決した実コンテナ名を返す (無効時は None)。直後の :func:`_maybe_open_editor` へ渡して
482-
``resolve_container_name`` (= ``docker compose ps``) の二重実行を避けるため。
483-
"""
484-
from devbase.editor import opener
485-
486-
enabled = open_flag if open_flag is not None else opener.is_open_terminal_enabled()
487-
if not enabled:
488-
return None
489-
490-
open_index = _resolve_open_index(open_index, scale)
491-
492-
if compose_file is None and _SCALE_COMPOSE_FILE.exists():
493-
compose_file = _SCALE_COMPOSE_FILE
494-
495-
dev_service_name = get_dev_service_name()
496-
container = opener.resolve_container_name(dev_service_name, project_name,
497-
open_index, compose_file=compose_file)
498-
workdir = opener.resolve_workdir(os.environ, project_name)
499-
content = opener.build_folder_open_tasks_json()
500-
501-
# 既存があれば書かず、無ければ stdin から書き込む (冪等)。workdir は引数で渡し
502-
# シェル内クォートを避ける ($1)。
503-
script = (
504-
'set -e; d="$1/.vscode"; mkdir -p "$d"; '
505-
'if [ -e "$d/tasks.json" ]; then echo keep; '
506-
'else cat > "$d/tasks.json"; echo placed; fi'
507-
)
508-
try:
509-
proc = subprocess.run(
510-
["docker", "exec", "-i", container, "sh", "-c", script, "_", workdir],
511-
input=content, text=True, capture_output=True, timeout=15,
512-
)
513-
except Exception as e: # noqa: BLE001 - 配置失敗で up を倒さない
514-
logger.warning("ターミナル用 tasks.json の配置に失敗しましたが続行します: %s", e)
515-
return container # 名前解決は済んでいるのでエディタ側で再利用させる
516-
if proc.returncode != 0:
517-
logger.warning("ターミナル用 tasks.json の配置に失敗しましたが続行します: %s",
518-
(proc.stderr or "").strip())
519-
return container
520-
if (proc.stdout or "").strip() == "placed":
521-
logger.info("[6/6] 統合ターミナル自動表示用 tasks.json を配置: %s/.vscode/tasks.json",
522-
workdir)
523-
return container
524-
525-
526465
def cmd_up(project_name: str = None, scale: int = None,
527466
open_editor: Optional[bool] = None,
528-
open_index: Optional[int] = None,
529-
open_terminal: Optional[bool] = None) -> int:
467+
open_index: Optional[int] = None) -> int:
530468
"""Deploy containers with specified scale"""
531469
if project_name is None:
532470
project_name = get_project_name()
@@ -593,12 +531,8 @@ def cmd_up(project_name: str = None, scale: int = None,
593531
if deploy_script.exists() and deploy_script.is_file():
594532
_run_deploy_script_for_instances(deploy_script, range(1, scale + 1))
595533

596-
# エディタを開く前に tasks.json を置く (開いた瞬間に folderOpen が効くように)。
597-
# 解決済みコンテナ名を editor 側へ渡し docker compose ps の二重実行を避ける。
598-
dev_container = _maybe_place_terminal_task(project_name, open_terminal, open_index,
599-
scale, compose_file=override_file)
600534
_maybe_open_editor(project_name, open_editor, open_index, scale,
601-
compose_file=override_file, container_name=dev_container)
535+
compose_file=override_file)
602536

603537
logger.info("=== Deploy completed successfully ===")
604538
return 0

lib/devbase/editor/opener.py

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -128,56 +128,6 @@ def is_open_enabled(environ=None) -> bool:
128128
return value.strip().lower() in _TRUTHY
129129

130130

131-
def is_open_terminal_enabled(environ=None) -> bool:
132-
"""``DEVBASE_OPEN_TERMINAL`` env が真か (**未設定は True = 既定 ON**)。
133-
134-
``DEVBASE_OPEN_EDITOR`` (既定 OFF) と既定が逆である点に注意。up 時の tasks.json 配置は
135-
暴発リスクが低く、ユーザ要望で既定 ON とする (PLAN31_3)。
136-
"""
137-
env = os.environ if environ is None else environ
138-
value = env.get("DEVBASE_OPEN_TERMINAL")
139-
if value is None:
140-
return True
141-
return value.strip().lower() in _TRUTHY
142-
143-
144-
def build_folder_open_tasks_json() -> str:
145-
"""フォルダを開いた時に統合ターミナルを表示する folderOpen タスク (.vscode/tasks.json)。
146-
147-
VS Code 公式には「起動時にターミナルを開く」単独設定が無く (``hideOnStartup`` は復元
148-
された永続セッションを隠すか否かに過ぎず新規生成はしない)、``runOn: folderOpen`` の
149-
タスクが新規ターミナルを出せる唯一の方法 (docs/terminal/*, docs/debugtest/tasks)。
150-
``reveal: always`` でパネルを前面に出し対話シェルを起動する。``type: process`` で
151-
``/bin/sh -lc 'exec "${SHELL:-/bin/sh}"'`` を直接起動し、``$SHELL`` 未設定のコンテナでも
152-
``/bin/sh`` にフォールバックして必ずシェルが立ち上がるようにする (``type: shell`` +
153-
``${env:SHELL}`` だと未設定時に command が空になりタスクが即失敗する)。
154-
155-
.. note:: 自動実行には2つの user 設定ゲートがあり devbase からは制御できない:
156-
Workspace Trust (信頼済みフォルダのみ自動実行) と ``task.allowAutomaticTasks``
157-
(既定 off = フォルダ毎に1回許可確認)。いずれも application/user スコープ専用。
158-
"""
159-
tasks = {
160-
"version": "2.0.0",
161-
"tasks": [
162-
{
163-
"label": "devbase: open terminal",
164-
"type": "process",
165-
"command": "/bin/sh",
166-
"args": ["-lc", 'exec "${SHELL:-/bin/sh}"'],
167-
"isBackground": True,
168-
"problemMatcher": [],
169-
"presentation": {
170-
"reveal": "always",
171-
"panel": "dedicated",
172-
"focus": True,
173-
},
174-
"runOptions": {"runOn": "folderOpen"},
175-
}
176-
],
177-
}
178-
return json.dumps(tasks, indent=2, ensure_ascii=False) + "\n"
179-
180-
181131
def resolve_editor_cmd(environ=None) -> Optional[list]:
182132
"""起動に使うエディタコマンド (argv list) を解決する。
183133

lib/devbase/env/keys.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,3 @@ def gcp_credentials_key(profile: str) -> str:
6161
# Remote-SSH 跨ホスト構成 (Windows VS Code → ssh → Mac のコンテナ) 用。
6262
DEVBASE_EDITOR_SSH_HOST = "DEVBASE_EDITOR_SSH_HOST" # 任意。ssh-remote ホスト名 (例 mac2)。通常は ~/.vscode-server から自動検出
6363
DEVBASE_EDITOR_DOCKER_CONTEXT = "DEVBASE_EDITOR_DOCKER_CONTEXT" # 任意。ssh 先 docker context (既定: docker context show)
64-
DEVBASE_OPEN_TERMINAL = "DEVBASE_OPEN_TERMINAL" # 真偽。up 後に folderOpen ターミナル tasks.json を配置 (既定 ON)

tests/cli/test_project_dispatch.py

Lines changed: 6 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -340,19 +340,17 @@ def test_up_parser_open_flags_tri_state():
340340

341341

342342
def test_lifecycle_propagates_open_args_to_cmd_up(monkeypatch):
343-
"""up の open_editor / open_index / open_terminal が cmd_up まで伝播する。"""
343+
"""up の open_editor / open_index が cmd_up まで伝播する。"""
344344
from devbase.commands import container
345345
captured = {}
346346
monkeypatch.setattr(
347347
container, 'cmd_up',
348-
lambda project_name=None, scale=None, open_editor=None, open_index=None,
349-
open_terminal=None: captured.update(
350-
open_editor=open_editor, open_index=open_index,
351-
open_terminal=open_terminal) or 0)
352-
args = _args(subcommand='up', scale=None, open_editor=True, open_index=2,
353-
open_terminal=False)
348+
lambda project_name=None, scale=None, open_editor=None,
349+
open_index=None: captured.update(
350+
open_editor=open_editor, open_index=open_index) or 0)
351+
args = _args(subcommand='up', scale=None, open_editor=True, open_index=2)
354352
assert container._dispatch_lifecycle(args) == 0
355-
assert captured == {'open_editor': True, 'open_index': 2, 'open_terminal': False}
353+
assert captured == {'open_editor': True, 'open_index': 2}
356354

357355

358356
def test_maybe_open_editor_disabled_by_default(monkeypatch):
@@ -423,79 +421,6 @@ def test_maybe_open_editor_valid_index_within_scale(monkeypatch):
423421
assert called[0]['index'] == 2
424422

425423

426-
class _DockerProc:
427-
"""subprocess.run 互換スタブ (docker exec 用)。"""
428-
def __init__(self, returncode=0, stdout="placed", stderr=""):
429-
self.returncode = returncode
430-
self.stdout = stdout
431-
self.stderr = stderr
432-
433-
434-
def test_maybe_place_terminal_task_disabled(monkeypatch):
435-
"""DEVBASE_OPEN_TERMINAL 無効時は docker exec を呼ばない。"""
436-
from devbase.commands import container
437-
from devbase.editor import opener
438-
monkeypatch.setattr(opener, 'is_open_terminal_enabled', lambda environ=None: False)
439-
calls = []
440-
monkeypatch.setattr(container.subprocess, 'run',
441-
lambda *a, **k: calls.append(a) or _DockerProc())
442-
container._maybe_place_terminal_task('carmo', None, None, 1)
443-
assert calls == []
444-
445-
446-
def test_maybe_place_terminal_task_flag_off_overrides_env(monkeypatch):
447-
"""open_flag=False なら env が ON でも置かない。"""
448-
from devbase.commands import container
449-
from devbase.editor import opener
450-
monkeypatch.setattr(opener, 'is_open_terminal_enabled', lambda environ=None: True)
451-
calls = []
452-
monkeypatch.setattr(container.subprocess, 'run',
453-
lambda *a, **k: calls.append(a) or _DockerProc())
454-
container._maybe_place_terminal_task('carmo', False, None, 1)
455-
assert calls == []
456-
457-
458-
def test_maybe_place_terminal_task_runs_docker_exec(monkeypatch):
459-
"""既定 ON で docker exec -i <container> へ tasks.json を stdin 投入する。"""
460-
from devbase.commands import container
461-
from devbase.editor import opener
462-
monkeypatch.setattr(opener, 'is_open_terminal_enabled', lambda environ=None: True)
463-
monkeypatch.setattr(opener, 'resolve_container_name', lambda *a, **k: 'carmo-dev-1')
464-
monkeypatch.setattr(opener, 'resolve_workdir', lambda *a, **k: '/work/carmo')
465-
monkeypatch.setattr(container, 'get_dev_service_name', lambda: 'dev')
466-
captured = {}
467-
468-
def fake_run(cmd, **kw):
469-
captured['cmd'] = cmd
470-
captured['input'] = kw.get('input')
471-
return _DockerProc(returncode=0, stdout="placed")
472-
473-
monkeypatch.setattr(container.subprocess, 'run', fake_run)
474-
result = container._maybe_place_terminal_task('carmo', None, 1, 1)
475-
cmd = captured['cmd']
476-
assert cmd[:4] == ['docker', 'exec', '-i', 'carmo-dev-1']
477-
assert cmd[-1] == '/work/carmo' # workdir は $1 として末尾に渡す
478-
assert '"runOn": "folderOpen"' in captured['input']
479-
# 解決済みコンテナ名を返し editor 側で再利用させる (docker compose ps 二重実行回避)
480-
assert result == 'carmo-dev-1'
481-
482-
483-
def test_maybe_place_terminal_task_failure_does_not_raise(monkeypatch):
484-
"""docker exec が例外でも up を倒さない (握り潰す)。"""
485-
from devbase.commands import container
486-
from devbase.editor import opener
487-
monkeypatch.setattr(opener, 'is_open_terminal_enabled', lambda environ=None: True)
488-
monkeypatch.setattr(opener, 'resolve_container_name', lambda *a, **k: 'carmo-dev-1')
489-
monkeypatch.setattr(opener, 'resolve_workdir', lambda *a, **k: '/work/carmo')
490-
monkeypatch.setattr(container, 'get_dev_service_name', lambda: 'dev')
491-
492-
def boom(*a, **k):
493-
raise OSError("docker missing")
494-
495-
monkeypatch.setattr(container.subprocess, 'run', boom)
496-
container._maybe_place_terminal_task('carmo', None, 1, 1) # 例外が出なければ OK
497-
498-
499424
def test_maybe_open_editor_forwards_compose_file(monkeypatch):
500425
"""compose_file 引数が open_editor まで伝播する (実コンテナ名問い合わせ用)。"""
501426
from devbase.commands import container

tests/editor/test_opener.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -290,35 +290,6 @@ def boom(cmd, **kw):
290290
assert opener.resolve_docker_context({}, runner=boom) is None
291291

292292

293-
# ---------------------------------------------------------------------------
294-
# is_open_terminal_enabled (既定 ON)
295-
# ---------------------------------------------------------------------------
296-
297-
@pytest.mark.parametrize("value,expected", [
298-
(None, True), ("", False), ("0", False), ("false", False), ("no", False),
299-
("1", True), ("true", True), ("on", True), ("YES", True),
300-
])
301-
def test_is_open_terminal_enabled(value, expected):
302-
env = {} if value is None else {"DEVBASE_OPEN_TERMINAL": value}
303-
assert opener.is_open_terminal_enabled(env) is expected
304-
305-
306-
# ---------------------------------------------------------------------------
307-
# build_folder_open_tasks_json
308-
# ---------------------------------------------------------------------------
309-
310-
def test_build_folder_open_tasks_json_is_valid_folderopen_task():
311-
data = json.loads(opener.build_folder_open_tasks_json())
312-
assert data["version"] == "2.0.0"
313-
task = data["tasks"][0]
314-
assert task["runOptions"]["runOn"] == "folderOpen"
315-
assert task["presentation"]["reveal"] == "always"
316-
# SHELL 未設定でも /bin/sh にフォールバックして必ずシェルを起動する (空 command 回避)
317-
assert task["type"] == "process"
318-
assert task["command"] == "/bin/sh"
319-
assert "SHELL:-/bin/sh" in " ".join(task["args"])
320-
321-
322293
# ---------------------------------------------------------------------------
323294
# resolve_container_name / resolve_workdir
324295
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)