From 750f049fcd7c045e50b2a70090667dff73383c95 Mon Sep 17 00:00:00 2001 From: "takemi.ohama" Date: Sat, 27 Jun 2026 09:26:49 +0900 Subject: [PATCH] =?UTF-8?q?feat(up):=20DEVBASE=5FWORKSPACE=20=E3=81=A7=20V?= =?UTF-8?q?S=20Code=20=E3=83=AF=E3=83=BC=E3=82=AF=E3=82=B9=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=82=92=E9=96=8B=E3=81=8F=20+=20=E5=85=B1?= =?UTF-8?q?=E6=9C=89=E3=83=87=E3=82=A3=E3=83=AC=E3=82=AF=E3=83=88=E3=83=AA?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devbase up のエディタ自動オープンと AI 設定 symlink に 2 機能を追加。 1. DEVBASE_WORKSPACE 対応 (opener) - コンテナ内の *.code-workspace 絶対パスを env で指定すると、フォルダ (--folder-uri) ではなくワークスペースファイル (--file-uri) を開く。 - 未指定時は従来どおり /work/$GIT_REPO フォルダを開く。 - env 名は汎用 WORKSPACE (CI が設定し得る) との衝突回避と DEVBASE_EDITOR* 系との命名統一のため DEVBASE_WORKSPACE とする。 2. AI 設定 symlink に share / .kiro を追加 (entrypoint.sh) - /home/ubuntu/share -> /persistent/ai/share (全コンテナ共有の置き場)。 /persistent/ai は全インスタンス共通 volume (devbase_home_ubuntu) のため どのコンテナからも同一実体を参照する。 - /home/ubuntu/.kiro -> /persistent/ai/.kiro。 テスト: resolve_workspace と open_editor の file-uri 経路を追加 (139 passed)。 ドキュメント: docs/user/environment-variables.md に DEVBASE_WORKSPACE を追記。 Co-Authored-By: Claude Opus 4.8 (1M context) --- containers/base/entrypoint.sh | 2 ++ docs/user/environment-variables.md | 3 +- lib/devbase/editor/opener.py | 32 +++++++++++++++++-- tests/editor/test_opener.py | 51 ++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/containers/base/entrypoint.sh b/containers/base/entrypoint.sh index 4c816ed..77db99e 100644 --- a/containers/base/entrypoint.sh +++ b/containers/base/entrypoint.sh @@ -211,6 +211,8 @@ AI_SETTINGS=( ".gemini" ".serena" ".ssh" + ".kiro" + "share" ) # Ensure /persistent/ai directory exists diff --git a/docs/user/environment-variables.md b/docs/user/environment-variables.md index 9c346c3..100665d 100644 --- a/docs/user/environment-variables.md +++ b/docs/user/environment-variables.md @@ -140,7 +140,7 @@ devbase はホストマシンの認証情報を自動収集し、コンテナ内 ## `devbase up` 後のエディタ自動オープン -`devbase up` 完了後、dev コンテナへ接続した VS Code を自動で開けます(VS Code の「Attach to Running Container」を CLI から起動)。`/work/$GIT_REPO` をワークスペースとして開きます。 +`devbase up` 完了後、dev コンテナへ接続した VS Code を自動で開けます(VS Code の「Attach to Running Container」を CLI から起動)。既定では `/work/$GIT_REPO` フォルダを開きます(`--folder-uri`)。`DEVBASE_WORKSPACE` を指定するとフォルダの代わりに `*.code-workspace` ワークスペースファイルを開きます(`--file-uri`)。 これらは `devbase env init` の収集対象外で、プロジェクトの `env` か `$DEVBASE_ROOT/.env` に手書きする devbase 動作設定です。 @@ -148,6 +148,7 @@ devbase はホストマシンの認証情報を自動収集し、コンテナ内 |------|------| | `DEVBASE_OPEN_EDITOR` | 真(`1`/`true`/`yes`/`on`)で `up` 後にエディタを開く(既定: OFF) | | `DEVBASE_EDITOR` | 起動コマンド(既定: `code`)。`cursor` / `code-insiders` 等も可 | +| `DEVBASE_WORKSPACE` | 開く `*.code-workspace` ファイルの**コンテナ内絶対パス**(例 `/home/ubuntu/share/work/uttarov2-doc.workspace`)。指定時はフォルダではなくワークスペースを開く。未設定なら従来どおり `/work/$GIT_REPO` フォルダ。共有マウント `/home/ubuntu/share` 配下に置けば全コンテナで共用可 | | `DEVBASE_OPEN_INDEX` | scale 時に開く dev インスタンス番号(既定: `1`) | | `DEVBASE_EDITOR_SSH_HOST` | Remote-SSH 跨ホスト構成での ssh-remote ホスト名(例 `mac2`)。**通常は `~/.vscode-server` から自動検出**され不要。検出が外れる場合のみ明示。下記「跨ホスト」参照 | | `DEVBASE_EDITOR_DOCKER_CONTEXT` | 跨ホスト時に ssh 先で使う docker context(既定: ホストの `docker context show`) | diff --git a/lib/devbase/editor/opener.py b/lib/devbase/editor/opener.py index 5ad20bf..0b9c2af 100644 --- a/lib/devbase/editor/opener.py +++ b/lib/devbase/editor/opener.py @@ -301,6 +301,26 @@ def resolve_workdir(environ=None, project_name: Optional[str] = None) -> str: return f"/work/{repo}" if repo else "/work" +def resolve_workspace(environ=None) -> Optional[str]: + """開く VS Code ワークスペースファイル (``*.code-workspace``) のコンテナ内パス。 + + ``DEVBASE_WORKSPACE`` env にコンテナ内の絶対パス (例 + ``/home/ubuntu/share/work/uttarov2-doc.workspace``) が指定されていればそれを返す。 + 未設定・空文字なら None を返し、呼び出し側 (:func:`open_editor`) は従来どおり + :func:`resolve_workdir` のフォルダを ``--folder-uri`` で開く。 + + ワークスペースファイルはコンテナ内に実在するパスを指す前提 (attach 先は + コンテナ authority のため)。``/home/ubuntu/share`` 等の共有マウント配下に置けば + 全コンテナで共用できる。env 名はホスト CI が設定し得る汎用 ``WORKSPACE`` との + 衝突を避けるため ``DEVBASE_*`` 接頭辞付きにしている。 + """ + env = os.environ if environ is None else environ + value = env.get("DEVBASE_WORKSPACE") + if value is None: + return None + return value.strip() or None + + def _detect_ssh_host_from_dirs(server_dirs) -> Optional[str]: """複数の VS Code 系サーバーディレクトリの File History を横断して ssh-remote authority ラベルを推測する。 @@ -493,7 +513,13 @@ def open_editor(*, project_name: str, dev_service_name: str, workdir: str, ssh_host = (resolve_editor_ssh_host(env, auto_detect=ctx.in_vscode) if ctx.is_ssh else None) docker_context = resolve_docker_context(env) if ssh_host else None - uri = build_attach_uri(container, workdir, + # DEVBASE_WORKSPACE があれば *.code-workspace をワークスペースとして開く。VS Code は + # `--file-uri` に渡したパスが .code-workspace 拡張子なら multi-root ワークスペースとして + # 開くため、フォルダを開く `--folder-uri` と URI ターゲット・フラグの両方を切り替える。 + workspace = resolve_workspace(env) + open_target = workspace or workdir + uri_flag = "--file-uri" if workspace else "--folder-uri" + uri = build_attach_uri(container, open_target, ssh_host=ssh_host, docker_context=docker_context) if plan.action == "print_command": @@ -505,12 +531,12 @@ def open_editor(*, project_name: str, dev_service_name: str, workdir: str, "手元の VS Code で次を実行するか、VS Code の Remote-SSH 統合ターミナルから " "`devbase up` を実行すると自動で開きます:" ) - logger.info(" %s --folder-uri '%s'", quoted, uri) + logger.info(" %s %s '%s'", quoted, uri_flag, uri) return "print_command" quoted = " ".join(shlex.quote(c) for c in editor) logger.info("[editor] %s を起動します (%s)", quoted, plan.reason) - cmd = [*editor, "--folder-uri", uri] + cmd = [*editor, uri_flag, uri] try: (launcher or _launch)(cmd, dict(env)) except Exception as e: # noqa: BLE001 - 起動失敗で up を倒さない diff --git a/tests/editor/test_opener.py b/tests/editor/test_opener.py index 7770b3a..69f8ec7 100644 --- a/tests/editor/test_opener.py +++ b/tests/editor/test_opener.py @@ -402,6 +402,20 @@ def test_resolve_workdir_fallback_project_name(): assert opener.resolve_workdir({}, "proj") == "/work/proj" +def test_resolve_workspace_none_when_unset(): + assert opener.resolve_workspace({}) is None + + +def test_resolve_workspace_blank_is_none(): + assert opener.resolve_workspace({"DEVBASE_WORKSPACE": " "}) is None + + +def test_resolve_workspace_returns_path(): + env = {"DEVBASE_WORKSPACE": "/home/ubuntu/share/work/uttarov2-doc.workspace"} + assert opener.resolve_workspace(env) == \ + "/home/ubuntu/share/work/uttarov2-doc.workspace" + + # --------------------------------------------------------------------------- # decide_action (§2.4 マトリクス全分岐) # --------------------------------------------------------------------------- @@ -472,6 +486,43 @@ def test_open_editor_launch_invokes_launcher(monkeypatch): assert cmd[2].endswith("/work/carmo") +def test_open_editor_launch_uses_file_uri_for_workspace(monkeypatch): + """DEVBASE_WORKSPACE 指定時は --file-uri でワークスペースファイルを開く。""" + monkeypatch.setattr(opener.shutil, "which", lambda c: "/usr/bin/code") + calls = [] + result = opener.open_editor( + project_name="uttarov2-doc", dev_service_name="dev", + workdir="/work/uttarov2-doc", + environ={"DEVBASE_WORKSPACE": + "/home/ubuntu/share/work/uttarov2-doc.workspace"}, + isatty=True, launcher=lambda cmd, env: calls.append(cmd), + ) + assert result == "launch" + cmd = calls[0] + assert cmd[1] == "--file-uri" + assert cmd[2].startswith("vscode-remote://attached-container+") + assert cmd[2].endswith("/home/ubuntu/share/work/uttarov2-doc.workspace") + + +def test_open_editor_print_command_file_uri_for_workspace(monkeypatch, caplog): + """plain SSH の提示コマンドも DEVBASE_WORKSPACE 指定時は --file-uri になる。""" + import logging + monkeypatch.setattr(opener.shutil, "which", lambda c: "/usr/bin/code") + with caplog.at_level(logging.INFO): + result = opener.open_editor( + project_name="uttarov2-doc", dev_service_name="dev", + workdir="/work/uttarov2-doc", + environ={"SSH_CONNECTION": "1.2.3.4 5 6.7.8.9 22", + "DEVBASE_WORKSPACE": + "/home/ubuntu/share/work/uttarov2-doc.workspace"}, + isatty=True, launcher=lambda cmd, env: None, + ) + assert result == "print_command" + text = "\n".join(r.getMessage() for r in caplog.records) + assert "code --file-uri" in text + assert "uttarov2-doc.workspace" in text + + def test_open_editor_launch_nested_uri_under_remote_ssh(monkeypatch): """Remote-SSH (in_vscode + ssh) かつ DEVBASE_EDITOR_SSH_HOST 設定時はネスト URI で launch。""" monkeypatch.setattr(opener.shutil, "which", lambda c: "/usr/bin/code")