feat(terminal): use project name for in-container cwd and clone directory
This commit is contained in:
@@ -53,37 +53,17 @@ class TestModifyComposeFile:
|
||||
class TestGetRepositoryMountName:
|
||||
"""Tests for _get_repository_mount_name."""
|
||||
|
||||
def test_prefers_remote_url_name_over_user_provided_name(self):
|
||||
def test_uses_project_name(self):
|
||||
project = MagicMock()
|
||||
project.name = "My Project"
|
||||
repo = MagicMock()
|
||||
repo.name = "src"
|
||||
repo.remote_url = "git@git.example.com:acme/headquarter.git"
|
||||
assert _get_repository_mount_name(repo) == "headquarter"
|
||||
assert _get_repository_mount_name(project, repo) == "my-project"
|
||||
|
||||
def test_parses_browser_url_to_repo_name(self):
|
||||
def test_slugifies_project_name(self):
|
||||
project = MagicMock()
|
||||
project.name = "Project v2.0!"
|
||||
repo = MagicMock()
|
||||
repo.name = "src"
|
||||
repo.remote_url = "https://github.com/acme/headquarter/tree/main"
|
||||
assert _get_repository_mount_name(repo) == "headquarter"
|
||||
|
||||
def test_uses_workspace_path_basename_when_workspace_provided(self):
|
||||
repo = MagicMock()
|
||||
repo.name = "src"
|
||||
repo.remote_url = "git@git.example.com:acme/headquarter.git"
|
||||
workspace = MagicMock()
|
||||
workspace.path = "/data/working-copies/uuid/headquarter"
|
||||
assert _get_repository_mount_name(repo, workspace) == "headquarter"
|
||||
|
||||
def test_falls_back_to_repo_name_when_remote_url_missing(self):
|
||||
repo = MagicMock()
|
||||
repo.name = "my-cool-repo"
|
||||
repo.remote_url = None
|
||||
assert _get_repository_mount_name(repo) == "my-cool-repo"
|
||||
|
||||
def test_falls_back_to_repo_name_for_unparseable_url(self):
|
||||
repo = MagicMock()
|
||||
repo.name = "my-cool-repo"
|
||||
repo.remote_url = ""
|
||||
assert _get_repository_mount_name(repo) == "my-cool-repo"
|
||||
assert _get_repository_mount_name(project, repo) == "project-v2-0"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -277,6 +257,9 @@ async def test_prepare_manifest_instance_uses_workspace_path_basename():
|
||||
|
||||
session = AsyncMock()
|
||||
|
||||
project = MagicMock()
|
||||
project.name = "acme"
|
||||
|
||||
async def session_get(model, obj_id):
|
||||
if model.__name__ == "ToolType":
|
||||
return tool_type
|
||||
@@ -286,6 +269,8 @@ async def test_prepare_manifest_instance_uses_workspace_path_basename():
|
||||
return repo
|
||||
if model.__name__ == "Workspace":
|
||||
return workspace
|
||||
if model.__name__ == "Project":
|
||||
return project
|
||||
return None
|
||||
|
||||
session.get.side_effect = session_get
|
||||
@@ -324,5 +309,5 @@ async def test_prepare_manifest_instance_uses_workspace_path_basename():
|
||||
finally:
|
||||
instance_service.subprocess.run = original_run
|
||||
|
||||
assert "WORKSPACE_NAME: headquarter" in compose_content
|
||||
assert "/data/working-copies/workspace-uuid/headquarter:/home/user/headquarter" in compose_content
|
||||
assert "WORKSPACE_NAME: acme" in compose_content
|
||||
assert "/data/working-copies/workspace-uuid/headquarter:/home/user/acme" in compose_content
|
||||
|
||||
@@ -78,20 +78,7 @@ class TestGetManifestHomeDir:
|
||||
class TestCompileDockerfileHomeDirectory:
|
||||
"""Tests that compile_dockerfile honors manifest.home_directory."""
|
||||
|
||||
def test_env_home_and_workdir_use_home_directory(self) -> None:
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
"home_directory": "/home/custom",
|
||||
"user": {"name": "dev", "uid": 1000, "gid": 1000},
|
||||
}
|
||||
dockerfile = compile_dockerfile(manifest)
|
||||
|
||||
assert "ENV HOME=/home/custom" in dockerfile
|
||||
assert "WORKDIR /home/custom" in dockerfile
|
||||
|
||||
def test_workspace_symlink_created(self) -> None:
|
||||
"""When workspace name is known at build time, create /workspace symlink."""
|
||||
def test_env_home_and_workdir_use_project_directory(self) -> None:
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
@@ -101,12 +88,26 @@ class TestCompileDockerfileHomeDirectory:
|
||||
}
|
||||
dockerfile = compile_dockerfile(manifest)
|
||||
|
||||
assert "mkdir -p /home/custom" in dockerfile
|
||||
assert "ln -sfn /home/custom/my-app /workspace" in dockerfile
|
||||
assert "ENV HOME=/home/custom" in dockerfile
|
||||
assert "WORKDIR /home/custom/my-app" in dockerfile
|
||||
|
||||
def test_project_directory_created(self) -> None:
|
||||
"""When workspace name is known at build time, create the project directory."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
"home_directory": "/home/custom",
|
||||
"workspace_name": "my-app",
|
||||
"user": {"name": "dev", "uid": 1000, "gid": 1000},
|
||||
}
|
||||
dockerfile = compile_dockerfile(manifest)
|
||||
|
||||
assert "mkdir -p /home/custom/my-app" in dockerfile
|
||||
assert "ln -sfn" not in dockerfile
|
||||
|
||||
def test_runtime_workspace_not_baked_into_image(self) -> None:
|
||||
"""When workspace name is a runtime placeholder, do not create literal
|
||||
{{WORKSPACE_NAME}} directories or symlinks in the image."""
|
||||
{{WORKSPACE_NAME}} directories in the image."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
@@ -118,6 +119,8 @@ class TestCompileDockerfileHomeDirectory:
|
||||
|
||||
assert "{{WORKSPACE_NAME}}" not in dockerfile
|
||||
assert "ln -sfn" not in dockerfile
|
||||
assert "WORKDIR /home/custom/{{WORKSPACE_NAME}}" not in dockerfile
|
||||
assert "WORKDIR /home/custom\n" in dockerfile
|
||||
|
||||
def test_runtime_working_dir_overrides_home_workdir(self) -> None:
|
||||
manifest = {
|
||||
@@ -191,7 +194,7 @@ class TestCompileComposeHomeDirectory:
|
||||
compose = compile_compose(manifest, variables)
|
||||
|
||||
assert "/host/repos/my-app:/opt/code:ro" in compose
|
||||
assert "/home/custom/my-app" not in compose
|
||||
assert "/host/repos/my-app:/home/custom/my-app" not in compose
|
||||
|
||||
def test_workspace_name_substituted_in_mount_target(self) -> None:
|
||||
manifest = {
|
||||
@@ -276,7 +279,7 @@ def test_compile_dockerfile_starts_as_root_and_drops_privileges() -> None:
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_compile_compose_runs_as_root() -> None:
|
||||
"""The compose service must start as root so the entrypoint can fix /workspace."""
|
||||
"""The compose service must start as root so the entrypoint can fix ownership."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
@@ -300,7 +303,7 @@ def test_compile_compose_runs_as_root() -> None:
|
||||
class TestCompileEntrypoint:
|
||||
"""Tests for the generated permission-fixing entrypoint."""
|
||||
|
||||
def test_entrypoint_creates_home_and_workspace(self) -> None:
|
||||
def test_entrypoint_creates_home_and_project_directory(self) -> None:
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
@@ -311,7 +314,7 @@ class TestCompileEntrypoint:
|
||||
|
||||
assert 'mkdir -p "$HOME_DIR"' in entrypoint
|
||||
assert 'mkdir -p "$WORKSPACE_TARGET"' in entrypoint
|
||||
assert 'ln -sfn "$WORKSPACE_TARGET" /workspace' in entrypoint
|
||||
assert 'ln -sfn' not in entrypoint
|
||||
assert 'WORKSPACE_NAME="${WORKSPACE_NAME:-workspace}"' in entrypoint
|
||||
|
||||
def test_entrypoint_removes_stale_placeholder_directory(self) -> None:
|
||||
@@ -327,8 +330,8 @@ class TestCompileEntrypoint:
|
||||
assert 'if [ -d "${HOME_DIR}/{{WORKSPACE_NAME}}" ]; then' in entrypoint
|
||||
assert 'rm -rf "${HOME_DIR}/{{WORKSPACE_NAME}}"' in entrypoint
|
||||
|
||||
def test_entrypoint_uses_root_then_sudo_for_workspace_symlink(self) -> None:
|
||||
"""/workspace is under /, so root takes precedence; non-root falls back to sudo."""
|
||||
def test_entrypoint_does_not_create_workspace_symlink(self) -> None:
|
||||
"""The /workspace compatibility symlink is no longer created."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
@@ -337,12 +340,7 @@ class TestCompileEntrypoint:
|
||||
}
|
||||
entrypoint = compile_entrypoint(manifest)
|
||||
|
||||
root_idx = entrypoint.find('if [ "$(id -u)" = "0" ]; then')
|
||||
sudo_idx = entrypoint.find('elif [ -n "$SUDO" ]; then')
|
||||
assert root_idx != -1
|
||||
assert sudo_idx != -1
|
||||
assert root_idx < sudo_idx
|
||||
assert 'sudo ln -sfn "$WORKSPACE_TARGET" /workspace' in entrypoint
|
||||
assert "/workspace" not in entrypoint
|
||||
|
||||
def test_entrypoint_fixes_mount_owners(self) -> None:
|
||||
manifest = {
|
||||
|
||||
Reference in New Issue
Block a user