These helpers read and write project-level and bot-level .env files.
openenv.envfiles¶
openenv.envfiles
¶
Helpers for project and bot environment files.
openenv.envfiles.project_env¶
openenv.envfiles.project_env
¶
Helpers for loading and updating the project root .env file.
get_project_env_value(root, key)
¶
Resolve a configuration value from the OS env first, then the project .env.
Source code in src/openenv/envfiles/project_env.py
def get_project_env_value(root: str | Path, key: str) -> str | None:
"""Resolve a configuration value from the OS env first, then the project .env."""
if value := os.environ.get(key):
return value
return load_project_env(project_env_path(root)).get(key)
load_project_env(path)
¶
Load KEY=VALUE pairs from a project .env file.
Source code in src/openenv/envfiles/project_env.py
def load_project_env(path: str | Path) -> dict[str, str]:
"""Load KEY=VALUE pairs from a project .env file."""
env_path = Path(path)
if not env_path.exists():
return {}
return dict(parse_project_env_text(env_path.read_text(encoding="utf-8"), label=str(env_path)))
parse_project_env_text(text, *, label)
¶
Parse a project .env file.
Source code in src/openenv/envfiles/project_env.py
def parse_project_env_text(text: str, *, label: str) -> list[tuple[str, str]]:
"""Parse a project .env file."""
entries: list[tuple[str, str]] = []
seen: set[str] = set()
for line_no, raw_line in enumerate(text.splitlines(), start=1):
stripped = raw_line.strip()
if not stripped or stripped.startswith("#"):
continue
if "=" not in raw_line:
raise ValidationError(f"{label}:{line_no} must use KEY=VALUE syntax.")
key, value = raw_line.split("=", 1)
key = key.strip()
if not _ENV_KEY_PATTERN.match(key):
raise ValidationError(
f"{label}:{line_no} has an invalid env var name: {key!r}"
)
if key in seen:
raise ValidationError(f"{label}:{line_no} duplicates env var {key}.")
seen.add(key)
entries.append((key, value))
return entries
project_env_path(root)
¶
Return the project-level .env path.
Source code in src/openenv/envfiles/project_env.py
def project_env_path(root: str | Path) -> Path:
"""Return the project-level .env path."""
return Path(root).resolve() / PROJECT_ENV_FILENAME
upsert_project_env_text(text, key, value)
¶
Insert or replace a KEY=VALUE line while preserving other content.
Source code in src/openenv/envfiles/project_env.py
def upsert_project_env_text(text: str, key: str, value: str) -> str:
"""Insert or replace a KEY=VALUE line while preserving other content."""
lines = text.splitlines()
rendered_line = f"{key}={value}"
updated = False
result: list[str] = []
for raw_line in lines:
stripped = raw_line.strip()
if stripped and not stripped.startswith("#") and "=" in raw_line:
current_key, _ = raw_line.split("=", 1)
if current_key.strip() == key:
result.append(rendered_line)
updated = True
continue
result.append(raw_line)
if not updated:
if result and result[-1] != "":
result.append("")
result.append(rendered_line)
return "\n".join(result).rstrip() + "\n"
write_project_env_value(root, key, value)
¶
Upsert a single KEY=VALUE entry in the project root .env file.
Source code in src/openenv/envfiles/project_env.py
def write_project_env_value(root: str | Path, key: str, value: str) -> Path:
"""Upsert a single KEY=VALUE entry in the project root .env file."""
if not _ENV_KEY_PATTERN.match(key):
raise ValidationError(f"Invalid env var name: {key!r}")
env_path = project_env_path(root)
existing_text = env_path.read_text(encoding="utf-8") if env_path.exists() else ""
rendered = upsert_project_env_text(existing_text, key, value)
env_path.write_text(rendered, encoding="utf-8")
return env_path
openenv.envfiles.secret_env¶
openenv.envfiles.secret_env
¶
Helpers for sidecar .env secret reference files.
load_secret_refs(path)
¶
Load secret refs from a sidecar .env file.
Source code in src/openenv/envfiles/secret_env.py
def load_secret_refs(path: str | Path) -> list[SecretRef]:
"""Load secret refs from a sidecar .env file."""
return [
SecretRef(name=name, source=f"env:{name}", required=True)
for name in load_secret_values(path)
]
load_secret_values(path)
¶
Load env key/value pairs from a sidecar .env file.
Source code in src/openenv/envfiles/secret_env.py
def load_secret_values(path: str | Path) -> dict[str, str]:
"""Load env key/value pairs from a sidecar .env file."""
env_path = Path(path)
if not env_path.exists():
return {}
return dict(parse_secret_env_text(env_path.read_text(encoding="utf-8"), label=str(env_path)))
parse_secret_env_text(text, *, label)
¶
Parse a bot sidecar .env file.
Source code in src/openenv/envfiles/secret_env.py
def parse_secret_env_text(text: str, *, label: str) -> list[tuple[str, str]]:
"""Parse a bot sidecar .env file."""
entries: list[tuple[str, str]] = []
seen: set[str] = set()
for line_no, raw_line in enumerate(text.splitlines(), start=1):
stripped = raw_line.strip()
if not stripped or stripped.startswith("#"):
continue
if "=" not in raw_line:
raise ValidationError(f"{label}:{line_no} must use KEY=VALUE syntax.")
name, value = raw_line.split("=", 1)
name = name.strip()
if not _ENV_KEY_PATTERN.match(name):
raise ValidationError(
f"{label}:{line_no} has an invalid env var name: {name!r}"
)
if name in seen:
raise ValidationError(f"{label}:{line_no} duplicates env var {name}.")
seen.add(name)
entries.append((name, value))
return entries
render_secret_env(secret_names, *, existing_values=None, display_name=None)
¶
Render the canonical bot .env file.
Source code in src/openenv/envfiles/secret_env.py
def render_secret_env(
secret_names: list[str],
*,
existing_values: dict[str, str] | None = None,
display_name: str | None = None,
) -> str:
"""Render the canonical bot .env file."""
values = existing_values or {}
names = _unique_preserving_order(secret_names)
header = [
(
f"# Secret references for {display_name}"
if display_name
else "# Secret references"
),
"# Keys declared here are synthesized into runtime.secret_refs for the bot.",
]
lines = list(header)
for name in names:
lines.append("")
lines.append(f"{name}={values.get(name, '')}")
return "\n".join(lines).rstrip() + "\n"
secret_env_path(directory)
¶
Return the canonical sidecar env path for a manifest directory.
Source code in src/openenv/envfiles/secret_env.py
def secret_env_path(directory: str | Path) -> Path:
"""Return the canonical sidecar env path for a manifest directory."""
return Path(directory) / BOT_SECRET_ENV_FILENAME
write_secret_env(path, secret_names, *, existing_values=None, display_name=None)
¶
Write the canonical bot .env file.
Source code in src/openenv/envfiles/secret_env.py
def write_secret_env(
path: str | Path,
secret_names: list[str],
*,
existing_values: dict[str, str] | None = None,
display_name: str | None = None,
) -> None:
"""Write the canonical bot .env file."""
Path(path).write_text(
render_secret_env(
secret_names,
existing_values=existing_values,
display_name=display_name,
),
encoding="utf-8",
)