diff --git a/packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/compose.py b/packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/compose.py index 9300245..24a7259 100644 --- a/packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/compose.py +++ b/packages/razorback-plugin-dab/src/razorback_plugin_dab/generate/compose.py @@ -153,6 +153,13 @@ def generate_compose( main_service: dict[str, Any] = { "image": docker_image, "working_dir": container_workdir, + # Run as root. Harbor's codex runtime pre-creates root-owned + # /logs/agent and $CODEX_HOME during setup, then runs the agent as the + # image's default USER. dab-agent:latest ships USER exedev (non-root), + # so the agent cannot write those dirs and codex aborts with + # "Permission denied (os error 13)". ade-bench's images run as root and + # never hit this; pin main to root to match. + "user": "0:0", "networks": ["dab-net"] if networks_used else [], } if main_file_volumes: diff --git a/packages/razorback-plugin-dab/tests/unit/test_compose_postgres.py b/packages/razorback-plugin-dab/tests/unit/test_compose_postgres.py index bd6b4d1..b7d9975 100644 --- a/packages/razorback-plugin-dab/tests/unit/test_compose_postgres.py +++ b/packages/razorback-plugin-dab/tests/unit/test_compose_postgres.py @@ -63,6 +63,22 @@ def test_sqlite_does_not_spawn_service(tmp_path: Path): assert "dab-sqlite" not in compose["services"] +def test_main_service_runs_as_root(tmp_path: Path): + # The codex runtime's setup (run as root) pre-creates root-owned + # /logs/agent and $CODEX_HOME, then harbor runs the agent as the image's + # default USER. dab-agent:latest is USER exedev (non-root), so the agent + # cannot write those dirs -> codex aborts with "Permission denied + # (os error 13)". Pin the main service to root so it can write them, + # matching the root images ade-bench runs successfully. + compose_text = generate_compose( + db_config=_BOOKREVIEW_LIKE, + dataset_name="bookreview", + data_root=tmp_path, + ) + compose = yaml.safe_load(compose_text) + assert compose["services"]["main"]["user"] == "0:0" + + def test_dab_net_declared(tmp_path: Path): compose_text = generate_compose( db_config=_BOOKREVIEW_LIKE,