refactor(env): stop mutating os.environ in resolve_env
test / unit (push) Successful in 14s
test / integration (push) Failing after 13s

ResolvedEnv.forwarded now carries name->value pairs instead of names
whose values had been side-loaded into os.environ. The Docker backend
collects the dict (plus the renamed OAuth token) and passes it via
subprocess.run(env=...) so docker run -e NAME forwards by-name from
the child's environment, not the parent's.

Values are excluded from the dataclass repr (forwarded on ResolvedEnv,
forwarded_env on DockerBottlePlan) so accidental logging cannot leak
secret or interpolated values.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 10:37:01 -04:00
parent 95a14bb8d2
commit 5f29fd10e2
3 changed files with 41 additions and 33 deletions
+6 -2
View File
@@ -8,7 +8,7 @@ further resolution; show_plan-style rendering is the `print` method.
from __future__ import annotations
import sys
from dataclasses import dataclass
from dataclasses import dataclass, field
from pathlib import Path
from ...log import info
@@ -42,7 +42,11 @@ class DockerBottlePlan(BottlePlan):
derived_image: str # "" -> no derived image
runtime_image: str # image actually launched (derived or base)
env_file: Path # docker --env-file: NAME=VALUE literals
forwarded_env: tuple[str, ...] # docker -e <NAME>: forwarded by-name
# name -> value for vars forwarded into the docker-run child process
# via subprocess env (so values never land on argv or in a file).
# repr=False keeps secret/interpolated/OAuth values out of any
# accidental log of the plan dataclass.
forwarded_env: dict[str, str] = field(repr=False)
prompt_file: Path
proxy_plan: PipelockProxyPlan
allowlist_summary: str