fix: container mount permissions, terminal shift, ESC capture
Bug 1 — in-container repo mounting: - docker-compose.yml: added /data/working-copies:/data/working-copies mount to API container so workspace dirs are visible on host filesystem - Dockerfile: create /data/working-copies dir in image Bug 2 — /home/user not writable: - workspace_manager.py: chmod 777 workspace dirs + 666 files after clone and after sync, so any container user can write - manifest_compiler.py: explicit mkdir + chown + chmod 755 for home dir in generated Dockerfile Bug 3 — terminal text shifts left on typing: - terminal.tsx: removed manual term.refresh() after fit (caused reflow) - Track lastSentCols/lastSentRows and only send resize when dimensions actually changed, preventing resize feedback loops Bug 4 — ESC key captured by terminal: - terminal.tsx: attachCustomKeyEventHandler allows ESC to propagate to browser when not in alternate buffer (vim/tmux), so modals/navigation work; ESC still sent to PTY when in vim/tmux alternate screen Quality gates: ruff clean, tsc --noEmit clean, pytest workspaces (9 passed)
This commit is contained in:
+2
-2
@@ -50,8 +50,8 @@ ENV PATH=/root/.local/bin:$PATH
|
||||
# Copy application code
|
||||
COPY --chown=appuser:appgroup . .
|
||||
|
||||
# Create directories for repo and instance storage
|
||||
RUN mkdir -p /data/repos /data/instances && chown -R appuser:appgroup /data
|
||||
# Create directories for repo, instance, and workspace storage
|
||||
RUN mkdir -p /data/repos /data/instances /data/working-copies && chown -R appuser:appgroup /data
|
||||
|
||||
# Copy wait-for-db script
|
||||
COPY wait-for-db.sh /usr/local/bin/wait-for-db.sh
|
||||
|
||||
@@ -26,7 +26,7 @@ def resolve_base(manifest: dict) -> dict:
|
||||
result = deepcopy(manifest)
|
||||
|
||||
base_definition_id = result.pop("base_definition_id", None)
|
||||
base_version = result.pop("base_version", "latest")
|
||||
result.pop("base_version", None)
|
||||
|
||||
if base_definition_id:
|
||||
# This will be provided by the caller (they have the DB session)
|
||||
@@ -167,8 +167,11 @@ def compile_dockerfile(manifest: dict) -> str:
|
||||
lines.append(f"ENV HOME={home}")
|
||||
lines.append(f"ENV USER={name}")
|
||||
lines.append("")
|
||||
# Ensure home directory exists and is writable by the user
|
||||
lines.append(f"RUN mkdir -p {home} && chown {name}:{name} {home} && chmod 755 {home}")
|
||||
lines.append("")
|
||||
|
||||
# Build scripts
|
||||
# Build scripts
|
||||
build_scripts = manifest.get("scripts", {}).get("build", [])
|
||||
for script in build_scripts:
|
||||
# Normalize multi-line scripts into single RUN command
|
||||
|
||||
@@ -73,7 +73,13 @@ class WorkspaceManager:
|
||||
RuntimeError: If git clone fails.
|
||||
"""
|
||||
path = self._workspace_path(repo.id, name)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
parent = os.path.dirname(path)
|
||||
os.makedirs(parent, exist_ok=True)
|
||||
# Ensure container users (various UIDs) can write to workspace dirs
|
||||
try:
|
||||
os.chmod(parent, 0o777)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
logger.info(
|
||||
"Creating workspace: name=%s, repo=%s, branch=%s", name, repo.id, branch
|
||||
@@ -99,6 +105,23 @@ class WorkspaceManager:
|
||||
|
||||
await GitService.clone(repo.remote_url, branch, path, ssh_key=ssh_key)
|
||||
|
||||
# Make workspace writable for any container user
|
||||
try:
|
||||
os.chmod(path, 0o777)
|
||||
for root, dirs, files in os.walk(path):
|
||||
for d in dirs:
|
||||
try:
|
||||
os.chmod(os.path.join(root, d), 0o777)
|
||||
except OSError:
|
||||
pass
|
||||
for f in files:
|
||||
try:
|
||||
os.chmod(os.path.join(root, f), 0o666)
|
||||
except OSError:
|
||||
pass
|
||||
except OSError:
|
||||
logger.warning("Failed to chmod workspace path: %s", path)
|
||||
|
||||
workspace = Workspace(
|
||||
name=name,
|
||||
repo_id=repo.id,
|
||||
@@ -190,6 +213,24 @@ class WorkspaceManager:
|
||||
return SyncResult(branch_deleted=True)
|
||||
|
||||
await GitService.pull(workspace.path, workspace.branch, ssh_key=ssh_key)
|
||||
|
||||
# Re-apply permissive permissions after sync
|
||||
try:
|
||||
os.chmod(workspace.path, 0o777)
|
||||
for root, dirs, files in os.walk(workspace.path):
|
||||
for d in dirs:
|
||||
try:
|
||||
os.chmod(os.path.join(root, d), 0o777)
|
||||
except OSError:
|
||||
pass
|
||||
for f in files:
|
||||
try:
|
||||
os.chmod(os.path.join(root, f), 0o666)
|
||||
except OSError:
|
||||
pass
|
||||
except OSError:
|
||||
logger.warning("Failed to chmod workspace after sync: %s", workspace.path)
|
||||
|
||||
workspace.last_sync_at = datetime.now()
|
||||
logger.info("Workspace synced: %s", workspace.id)
|
||||
return SyncResult(branch_deleted=False)
|
||||
|
||||
Reference in New Issue
Block a user