Skip to content

feat: add setup phase execution - #138

Merged
delete-cloud merged 5 commits into
mainfrom
feat/setup-agent-phase-execution
May 12, 2026
Merged

delete-cloud merged 5 commits into
mainfrom
feat/setup-agent-phase-execution

Conversation

@delete-cloud

@delete-cloud delete-cloud commented May 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • execute server-configured remote setup commands in a separate one-shot Docker setup container before the agent container starts
  • keep request-provided setup commands rejected while allowing production validation for trusted server-configured commands
  • clean setup and agent phase containers during rollback, manual cleanup, and GC

Test Plan

  • uv run pytest tests/coding_agent/environment/test_docker_workspace_provider.py tests/ui/test_http_server.py tests/cli/test_remote_client.py -q
  • uv run basedpyright --level error src/coding_agent/environment/docker_workspace_provider.py src/coding_agent/ui/http_server.py tests/coding_agent/environment/test_docker_workspace_provider.py tests/ui/test_http_server.py
  • uv run ruff check src/coding_agent/environment/docker_workspace_provider.py src/coding_agent/ui/http_server.py tests/coding_agent/environment/test_docker_workspace_provider.py tests/ui/test_http_server.py
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Pre-start setup step: server-configured setup commands run in a dedicated one-shot container before the main workspace; host secret env vars are injected only into that setup run; failures surface redacted stdout/stderr and prevent agent start.
  • Documentation

    • Production validation clarified: setup must be enabled with a non-empty commands list, network limited (none/bridge), timeout positive, secret allowlist validated, and request-provided setup commands are rejected.
  • Tests

    • Added/expanded tests for provisioning, validation, secret injection, failure handling, and container cleanup.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 12, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds server-only production validation for remote_phases.setup; provider runs a restricted one-shot *-setup Docker container (with allowed host secret env vars) before agent startup; cleanup removes both setup and agent containers; tests updated/added accordingly.

Changes

Docker remote_phases.setup provisioning and cleanup

Layer / File(s) Summary
Production setup validation contract
coding-agent/docs/remote-sandbox-production.md, coding-agent/src/coding_agent/ui/http_server.py, coding-agent/tests/ui/test_http_server.py
HTTP validation enforces that setup is server-configured only, network must be "none" or "bridge", timeout_seconds must be a positive integer, commands must be a non-empty list of non-empty strings, and secret_env_allowlist must be a list of non-empty strings. Documentation updated and tests verify acceptance/rejection cases.
Docker setup container execution
coding-agent/src/coding_agent/environment/docker_workspace_provider.py, coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py
Provider optionally runs a restricted one-shot *-setup container before the agent: derives setup container name, validates setup config, loads allowed host secret env vars, composes sh -c command list, and runs docker run --rm with dropped capabilities, no-new-privileges, resource limits, workspace mount, and configured network. Tests assert setup runs with configured network/commands, failures prevent agent start, secrets injected only into setup, and allowlist validations.
Docker cleanup and GC updates
coding-agent/src/coding_agent/environment/docker_workspace_provider.py, coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py
Removal logic refactored to attempt removal of both *-setup and agent containers via a centralized helper that does docker rm -f plus inspect polling; failures are logged per-container and the first error is re-raised after best-effort cleanup. Tests/GC expectations updated to include *-setup removal and a new cleanup test verifies both containers are removed and workspace directory is deleted.

Sequence Diagram

sequenceDiagram
  participant Client as HTTP client
  participant HTTPServer as _validate_production_remote_phases
  participant Provision as provision_cloud_workspace_binding
  participant Setup as _run_docker_setup_phase_if_configured
  participant Engine as Docker engine
  participant Agent as Agent container
  participant FS as Workspace FS

  Client->>HTTPServer: send production config with remote_phases.setup
  HTTPServer->>HTTPServer: validate network, timeout_seconds, commands, secret_env_allowlist
  HTTPServer-->>Client: accept / reject
  Client->>Provision: request workspace provision
  Provision->>Setup: run setup phase if configured
  Setup->>Engine: docker run --rm --name ws-...-setup (setup cmd + allowed env)
  Engine-->>Setup: exit 0 / non-zero
  Setup-->>Provision: success / raise (redacted stdout/stderr on error)
  Provision->>Engine: docker run (agent container)  -- only on setup success
  Engine-->>Agent: agent running
  Provision->>FS: workspace ready
  Client->>Provision: request cleanup
  Provision->>Engine: docker rm -f ws-...-setup
  Provision->>Engine: docker rm -f ws-...
  Engine-->>Provision: containers removed
  Provision->>FS: rmdir workspace
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Poem

🐰 I hop in early, tidy and neat,
I run safe commands with secrets discreet.
Then agent wakes and takes its cue,
we both step out — workspace like new.
Server-led setup, quiet and sweet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add setup phase execution' accurately and concisely describes the main change: implementing setup phase execution. It directly reflects the core functionality added across multiple files (Docker workspace provider, HTTP validation, and tests).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/setup-agent-phase-execution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py (1)

813-869: 💤 Low value

Strengthen failure-path cleanup assertion to verify container identities and order.

The current assertions only check the count of rm -f calls (== 2) and that no run -d was attempted. They won't catch regressions like duplicate removal of the same container, removal in the wrong order (agent before setup), or removal of an unrelated container — all of which the neighboring test_docker_workspace_provider_cleanup_removes_setup_and_agent_containers test (lines 1500-1561) guards against with a precise list assertion.

Consider tracking the removed container names and asserting the exact sequence, mirroring the cleanup test:

♻️ Proposed refinement
     workspace_root = tmp_path / "workspaces"
     workspace_root.mkdir()
     commands: list[list[str]] = []
+    removed_containers: list[str] = []

     def fake_run(
         command: list[str], **kwargs: object
     ) -> subprocess.CompletedProcess[str]:
         del kwargs
         commands.append(command)
         if command[1:3] == ["run", "--rm"]:
             raise subprocess.CalledProcessError(
                 returncode=1,
                 cmd=command,
                 output="",
                 stderr="setup failed",
             )
         if command[1:3] == ["rm", "-f"]:
+            removed_containers.append(command[3])
             return subprocess.CompletedProcess(command, 0, stdout="", stderr="")
         ...

     assert [command[1:3] for command in commands].count(["run", "-d"]) == 0
-    assert [command[1:3] for command in commands].count(["rm", "-f"]) == 2
+    # Setup must be cleaned up before the (never-started) agent container.
+    assert removed_containers[0].endswith("-setup")
+    assert len(removed_containers) == 2
     assert not any(workspace_root.iterdir())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py`
around lines 813 - 869, Replace the loose count-based assertion in
test_docker_workspace_provider_setup_failure_does_not_start_agent_container with
a strict check of which containers were removed and in what order: after calling
provision_cloud_binding_from_config (used in this test), extract the container
names from the captured commands list (commands where command[1:3] == ["rm",
"-f"]) and assert that their sequence equals the exact expected list of removed
container names (constructed from the test's container_name_prefix and expected
setup/agent suffixes), while still asserting that no ["run","-d"] occurred;
update the test to build the expected names consistently with how
_docker_config/provision_cloud_binding_from_config derive them so the assertion
verifies identity and order rather than just count.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@coding-agent/src/coding_agent/environment/docker_workspace_provider.py`:
- Around line 1071-1074: The code silently replaces non-list
secret_env_allowlist values with [], which hides misconfigurations; update the
handling around the setup_phase variable so you validate that
setup_phase.get("secret_env_allowlist") is a list before calling
_setup_phase_env — if it's None allow [], but if it's present and not a list
raise a clear ValueError (or TypeError) with the actual type and context (e.g.,
include the offending value or type and mention "secret_env_allowlist" in the
message) so callers know the policy is misconfigured; reference the
secret_env_allowlist variable, the _setup_phase_env call, and the setup_phase
source in your change.

---

Nitpick comments:
In
`@coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py`:
- Around line 813-869: Replace the loose count-based assertion in
test_docker_workspace_provider_setup_failure_does_not_start_agent_container with
a strict check of which containers were removed and in what order: after calling
provision_cloud_binding_from_config (used in this test), extract the container
names from the captured commands list (commands where command[1:3] == ["rm",
"-f"]) and assert that their sequence equals the exact expected list of removed
container names (constructed from the test's container_name_prefix and expected
setup/agent suffixes), while still asserting that no ["run","-d"] occurred;
update the test to build the expected names consistently with how
_docker_config/provision_cloud_binding_from_config derive them so the assertion
verifies identity and order rather than just count.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 15c5e329-d46f-449d-ba07-b4280fe8b42b

📥 Commits

Reviewing files that changed from the base of the PR and between 7723456 and 7faedb2.

📒 Files selected for processing (5)
  • coding-agent/docs/remote-sandbox-production.md
  • coding-agent/src/coding_agent/environment/docker_workspace_provider.py
  • coding-agent/src/coding_agent/ui/http_server.py
  • coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py
  • coding-agent/tests/ui/test_http_server.py

Comment thread coding-agent/src/coding_agent/environment/docker_workspace_provider.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
coding-agent/src/coding_agent/environment/docker_workspace_provider.py (1)

1108-1116: ⚡ Quick win

Surface setup container output when docker run fails.

This call captures stdout/stderr, but a non-zero exit or timeout will bubble up without those streams being included in the outer provisioning log. That makes setup-phase failures much harder to debug in production.

Suggested fix
-    _ = subprocess.run(
-        setup_command,
-        shell=False,
-        capture_output=True,
-        text=True,
-        timeout=setup_timeout,
-        env=None,
-        check=True,
-    )
+    try:
+        _ = subprocess.run(
+            setup_command,
+            shell=False,
+            capture_output=True,
+            text=True,
+            timeout=setup_timeout,
+            env=None,
+            check=True,
+        )
+    except subprocess.CalledProcessError as exc:
+        if exc.stdout:
+            exc.add_note(f"setup phase stdout:\n{exc.stdout}")
+        if exc.stderr:
+            exc.add_note(f"setup phase stderr:\n{exc.stderr}")
+        raise
+    except subprocess.TimeoutExpired as exc:
+        if exc.stdout:
+            exc.add_note(f"setup phase stdout:\n{exc.stdout}")
+        if exc.stderr:
+            exc.add_note(f"setup phase stderr:\n{exc.stderr}")
+        raise
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@coding-agent/src/coding_agent/environment/docker_workspace_provider.py`
around lines 1108 - 1116, The subprocess.run call that executes setup_command
(with shell=False, capture_output=True, text=True, timeout=setup_timeout,
check=True) swallows stdout/stderr on failure; update the code around that call
(in docker_workspace_provider.py where setup_command is executed) to catch
subprocess.CalledProcessError and subprocess.TimeoutExpired, extract and surface
the captured stdout/stderr (from e.stdout/e.stderr or the CompletedProcess) into
the provisioning log or include them in the raised error message, then re-raise
or raise a new exception so the original failure still propagates while
preserving the output for debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@coding-agent/src/coding_agent/environment/docker_workspace_provider.py`:
- Around line 1121-1129: Validate each entry in secret_env_allowlist against the
existing _ENV_NAME_RE before calling os.environ.get: inside the loop that
iterates secret_env_allowlist, first ensure item is a non-empty string, then
check _ENV_NAME_RE.match(item.strip()) and raise a clear ValueError (e.g.
"remote_phases.setup.secret_env_allowlist entries must be valid environment
variable names") for invalid names; only after that read value =
os.environ.get(item.strip()) and continue populating env as before.

---

Nitpick comments:
In `@coding-agent/src/coding_agent/environment/docker_workspace_provider.py`:
- Around line 1108-1116: The subprocess.run call that executes setup_command
(with shell=False, capture_output=True, text=True, timeout=setup_timeout,
check=True) swallows stdout/stderr on failure; update the code around that call
(in docker_workspace_provider.py where setup_command is executed) to catch
subprocess.CalledProcessError and subprocess.TimeoutExpired, extract and surface
the captured stdout/stderr (from e.stdout/e.stderr or the CompletedProcess) into
the provisioning log or include them in the raised error message, then re-raise
or raise a new exception so the original failure still propagates while
preserving the output for debugging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1eb98d8e-4aca-4c2a-a1e5-94441b13a91a

📥 Commits

Reviewing files that changed from the base of the PR and between 7faedb2 and cc880eb.

📒 Files selected for processing (2)
  • coding-agent/src/coding_agent/environment/docker_workspace_provider.py
  • coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • coding-agent/tests/coding_agent/environment/test_docker_workspace_provider.py

Comment thread coding-agent/src/coding_agent/environment/docker_workspace_provider.py Outdated
@delete-cloud
delete-cloud merged commit 7172082 into main May 12, 2026
1 check was pending
@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@delete-cloud
delete-cloud deleted the feat/setup-agent-phase-execution branch May 12, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant