feat(terminal): use project name for in-container cwd and clone directory
This commit is contained in:
@@ -236,18 +236,16 @@ def compile_dockerfile(manifest: dict) -> str:
|
||||
lines.append(f"RUN chown -R {user['name']}:{user['name']} {dir_str}")
|
||||
lines.append("")
|
||||
|
||||
# Create the /workspace compatibility symlink only when the workspace name
|
||||
# is known at image-build time. Otherwise the entrypoint creates it at
|
||||
# runtime from the WORKSPACE_NAME environment variable.
|
||||
# Ensure the project directory exists so the WORKDIR below succeeds. The
|
||||
# compatibility /workspace symlink is no longer created for new images.
|
||||
workspace_target = f"{home_dir}/{workspace_name}"
|
||||
if "{{WORKSPACE_NAME}}" not in workspace_name:
|
||||
workspace_is_placeholder = "{{WORKSPACE_NAME}}" in workspace_name
|
||||
if not workspace_is_placeholder:
|
||||
lines.append(f"RUN mkdir -p {workspace_target}")
|
||||
if user:
|
||||
lines.append(
|
||||
f"RUN ln -sfn {workspace_target} /workspace && chown -R {user['name']}:{user['name']} {home_dir}"
|
||||
f"RUN chown -R {user['name']}:{user['name']} {home_dir}"
|
||||
)
|
||||
else:
|
||||
lines.append(f"RUN ln -sfn {workspace_target} /workspace")
|
||||
lines.append("")
|
||||
|
||||
# Entrypoint for startup scripts
|
||||
@@ -260,18 +258,21 @@ def compile_dockerfile(manifest: dict) -> str:
|
||||
lines.append("")
|
||||
|
||||
# Do not switch to the runtime user in the Dockerfile. The entrypoint
|
||||
# starts as root so it can create the /workspace compatibility symlink
|
||||
# (which lives under /) and fix mount ownership, then it drops privileges
|
||||
# starts as root so it can fix mount ownership, then it drops privileges
|
||||
# to the container user before exec-ing the real command.
|
||||
|
||||
# Set WORKDIR to the configured home directory unless runtime.working_dir
|
||||
# explicitly overrides it.
|
||||
# Set WORKDIR to the project directory unless runtime.working_dir
|
||||
# explicitly overrides it. When the workspace name is a runtime
|
||||
# placeholder, the Dockerfile cannot know the literal directory, so fall
|
||||
# back to the home directory; compose supplies the exact working_dir.
|
||||
runtime = manifest.get("runtime", {})
|
||||
working_dir = runtime.get("working_dir")
|
||||
if working_dir:
|
||||
lines.append(f"WORKDIR {expand_container_path(working_dir, home_dir)}")
|
||||
else:
|
||||
elif workspace_is_placeholder:
|
||||
lines.append(f"WORKDIR {home_dir}")
|
||||
else:
|
||||
lines.append(f"WORKDIR {workspace_target}")
|
||||
lines.append("")
|
||||
|
||||
# Entrypoint and CMD
|
||||
@@ -290,8 +291,8 @@ def compile_entrypoint(manifest: dict) -> str:
|
||||
|
||||
Injects a permission-fixer preamble that runs as root (or via sudo) before
|
||||
any user-defined startup script. It chowns the home directory and a safe
|
||||
subset of mount parents to the container user, creates the /workspace
|
||||
compatibility symlink, and avoids recursive chown of large repo subtrees.
|
||||
subset of mount parents to the container user, ensures the project
|
||||
directory exists, and avoids recursive chown of large repo subtrees.
|
||||
|
||||
Args:
|
||||
manifest: Fully resolved manifest JSON.
|
||||
@@ -346,7 +347,7 @@ def compile_entrypoint(manifest: dict) -> str:
|
||||
lines.append('mkdir -p "$HOME_DIR"')
|
||||
lines.append('fix_owner "$HOME_DIR"')
|
||||
lines.append("")
|
||||
lines.append("# Ensure workspace target exists and is owned by the container user")
|
||||
lines.append("# Ensure project directory exists and is owned by the container user")
|
||||
lines.append('mkdir -p "$WORKSPACE_TARGET"')
|
||||
lines.append('fix_owner "$WORKSPACE_TARGET"')
|
||||
lines.append("")
|
||||
@@ -355,16 +356,6 @@ def compile_entrypoint(manifest: dict) -> str:
|
||||
lines.append(' rm -rf "${HOME_DIR}/{{WORKSPACE_NAME}}"')
|
||||
lines.append('fi')
|
||||
lines.append("")
|
||||
lines.append("# Create /workspace compatibility symlink")
|
||||
lines.append("# / is owned by root, so we need root or passwordless sudo.")
|
||||
lines.append('if [ "$(id -u)" = "0" ]; then')
|
||||
lines.append(' ln -sfn "$WORKSPACE_TARGET" /workspace')
|
||||
lines.append('elif [ -n "$SUDO" ]; then')
|
||||
lines.append(' sudo ln -sfn "$WORKSPACE_TARGET" /workspace')
|
||||
lines.append('else')
|
||||
lines.append(' ln -sfn "$WORKSPACE_TARGET" /workspace 2>/dev/null || true')
|
||||
lines.append('fi')
|
||||
lines.append("")
|
||||
lines.append("# Fix ownership of declared mount targets (top-level only)")
|
||||
for mount in manifest.get("mounts", []):
|
||||
target = mount.get("target")
|
||||
@@ -442,11 +433,13 @@ def compile_compose(manifest: dict, variables: dict[str, Any]) -> str:
|
||||
service["working_dir"] = expand_container_path(
|
||||
runtime["working_dir"], home_dir
|
||||
)
|
||||
else:
|
||||
service["working_dir"] = f"{home_dir}/{workspace_name}"
|
||||
|
||||
# The entrypoint starts as root (Dockerfile does not set USER) so it can
|
||||
# create the /workspace compatibility symlink and fix mount ownership. It
|
||||
# drops privileges to the container user internally before exec-ing the
|
||||
# real command, so do not set compose-level user override here.
|
||||
# fix mount ownership. It drops privileges to the container user internally
|
||||
# before exec-ing the real command, so do not set compose-level user
|
||||
# override here.
|
||||
if user:
|
||||
service["user"] = "0:0"
|
||||
|
||||
@@ -460,8 +453,8 @@ def compile_compose(manifest: dict, variables: dict[str, Any]) -> str:
|
||||
if env:
|
||||
service["environment"] = dict(env)
|
||||
|
||||
# Expose the workspace/repo name so the entrypoint can finalize the
|
||||
# /workspace compatibility symlink at container startup.
|
||||
# Expose the project name so the entrypoint can create the project
|
||||
# directory at container startup.
|
||||
if "environment" not in service:
|
||||
service["environment"] = {}
|
||||
service["environment"]["WORKSPACE_NAME"] = workspace_name
|
||||
|
||||
Reference in New Issue
Block a user