mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
## Description In persistent-docker deployments a stale host env var can silently override the value the deployment manifest pinned for the container. `build_runtime_command` builds the `docker run` argv in two passes: 1. It emits the manifest's pinned env as `--env NAME=VALUE` (from `base_env` plus the deployment env). 2. It then walks `os.environ` and, for every name matching a `PASSTHROUGH_ENV_PREFIXES` prefix, appends a bare `--env NAME` so the host value is forwarded into the container. A manifest-pinned name and a host-exported name can collide when they share a passthrough prefix. `HEADROOM_BACKEND` is the clearest case: the manifest pins `--env HEADROOM_BACKEND=anthropic` in pass 1, and pass 2 also matches the `HEADROOM_` prefix and appends a bare `--env HEADROOM_BACKEND`. Docker resolves duplicate `--env` flags last-wins, and the bare passthrough comes last, so a stale host export `HEADROOM_BACKEND=anyllm` wins and the container runs a different backend than its deployment config says. `start_persistent_docker` runs the resulting command through `subprocess.run` with the parent process environment, so whatever the operator happened to have exported leaks in and overrides the manifest. The fix skips the bare passthrough for any name the manifest already pins, so the pinned value stands while unrelated host secrets (API keys and so on) are still passed through as before. Closes # ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - `headroom/install/runtime.py`: skip the bare `--env NAME` passthrough when `NAME` is already pinned by the manifest (`and name not in runtime_env`). - `tests/test_install/test_runtime.py`: add `test_build_runtime_command_docker_manifest_env_beats_host_passthrough`, which exports a conflicting `HEADROOM_BACKEND` and asserts the command keeps the manifest value and emits no bare passthrough for it. - `CHANGELOG.md`: Bug Fixes entry. ## Testing - [ ] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text $ uvx ruff@0.15.17 format headroom/install/runtime.py tests/test_install/test_runtime.py 2 files left unchanged $ uvx ruff@0.15.17 check headroom/install/runtime.py tests/test_install/test_runtime.py All checks passed! $ python -m py_compile headroom/install/runtime.py tests/test_install/test_runtime.py OK ``` ## Real Behavior Proof - Environment: local checkout, Python 3.11, `uvx ruff@0.15.17`. - Exact command / steps: ran a standalone script that reproduces the two-pass argv build and models Docker's duplicate `--env` last-wins resolution, with the manifest pinning `HEADROOM_BACKEND=anthropic` and the host exporting `HEADROOM_BACKEND=anyllm`. - Observed result: the old build resolves the effective `HEADROOM_BACKEND` to the host value `anyllm` (bare passthrough wins); the new build keeps the manifest value `anthropic` and emits no bare `HEADROOM_BACKEND` token, while a non-pinned passthrough (`ANTHROPIC_API_KEY`) is still forwarded. - Not tested: I did not run the full `pytest` suite locally because it pulls in the ML stack; the new regression test is left for CI. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [x] I have updated the CHANGELOG.md if applicable ## Additional Notes The "unit tests pass locally" and "type checking" boxes are unchecked because the full suite imports the ML dependencies, which I can't run in this environment; the change is a pure function over `build_runtime_command`, verified by the standalone proof above and covered by the new regression test for CI. Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|---|---|---|
| .. | ||
| test_health.py | ||
| test_native_installers.py | ||
| test_paths.py | ||
| test_planner.py | ||
| test_providers.py | ||
| test_runtime.py | ||
| test_state.py | ||
| test_supervisors.py | ||