Skip to content

Commit 3eb76d8

Browse files
eoinfennessyclaude
andcommitted
refactor: migrate build config to pydantic-settings
Replace hand-rolled _load_env parser and scattered os.getenv calls with a pydantic-settings BuildConfig class. This gives all three config variables (OGX_VERSION, OGX_INSTALL_FROM_SOURCE, RHAI_INDEX_URL) consistent env-var-over-file precedence, automatic type coercion, and validation at construction time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3ae4cee commit 3eb76d8

2 files changed

Lines changed: 35 additions & 42 deletions

File tree

build/build.py

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from pathlib import Path
2222
from typing import NamedTuple
2323

24+
from pydantic import field_validator
25+
from pydantic_settings import BaseSettings, SettingsConfigDict
2426
from ruamel.yaml import YAML
2527

2628
# Allowed characters for version strings: alphanumeric, dots, hyphens, plus, underscores
@@ -29,7 +31,6 @@
2931

3032
OGX_GIT_REPO = "https://github.com/opendatahub-io/ogx.git"
3133

32-
3334
STRIPPED_PROVIDER_TYPES = {
3435
"inline::sentence-transformers",
3536
"inline::milvus",
@@ -48,18 +49,25 @@
4849
)
4950

5051

51-
def _validate_version(version: str) -> str:
52-
"""Validate a version string contains only safe characters.
52+
class BuildConfig(BaseSettings):
53+
model_config = SettingsConfigDict(
54+
env_file=Path(__file__).parent / "build.env",
55+
)
5356

54-
Raises ValueError if the version contains shell metacharacters or
55-
other unexpected characters that could lead to injection.
56-
"""
57-
if not version or not _VERSION_PATTERN.match(version):
58-
raise ValueError(
59-
f"Invalid version format: {version!r}. "
60-
"Only alphanumeric characters, dots, hyphens, plus signs, underscores, and slashes are allowed."
61-
)
62-
return version
57+
ogx_version: str
58+
ogx_install_from_source: bool = False
59+
rhai_index_url: str | None = None
60+
61+
@field_validator("ogx_version")
62+
@classmethod
63+
def check_version(cls, v: str) -> str:
64+
if not v or not _VERSION_PATTERN.match(v):
65+
raise ValueError(
66+
f"Invalid version format: {v!r}. "
67+
"Only alphanumeric characters, dots, hyphens, plus signs, "
68+
"underscores, and slashes are allowed."
69+
)
70+
return v
6371

6472

6573
def _resolve_ref_to_sha(repo_url: str, ref: str) -> str:
@@ -88,18 +96,6 @@ def _resolve_ref_to_sha(repo_url: str, ref: str) -> str:
8896
return sha
8997

9098

91-
def _load_env(path: Path) -> dict[str, str]:
92-
"""Load key=value pairs from an env file."""
93-
env = {}
94-
with open(path) as f:
95-
for line in f:
96-
line = line.strip()
97-
if line and not line.startswith("#") and "=" in line:
98-
key, value = line.split("=", 1)
99-
env[key.strip()] = value.strip()
100-
return env
101-
102-
10399
class OgxRequirements(NamedTuple):
104100
ogx_api: str
105101
ogx: str
@@ -120,18 +116,15 @@ class LockfileConfig(NamedTuple):
120116
index_config: IndexConfig
121117

122118

123-
def _get_ogx_requirements() -> OgxRequirements:
124-
"""Resolve ogx package specifiers from build.env and environment.
119+
def _get_ogx_requirements(
120+
version: str, install_from_source: bool
121+
) -> OgxRequirements:
122+
"""Resolve ogx package specifiers.
125123
126124
When installing from source, the git tag is resolved to an immutable
127125
commit SHA via git ls-remote.
128126
"""
129-
env = _load_env(Path(__file__).parent / "build.env")
130-
131-
version = os.getenv("OGX_VERSION") or env["OGX_VERSION"]
132-
_validate_version(version)
133-
134-
if env.get("OGX_INSTALL_FROM_SOURCE", "").lower() == "true":
127+
if install_from_source:
135128
sha = _resolve_ref_to_sha(OGX_GIT_REPO, version)
136129
return OgxRequirements(
137130
ogx_api=f"ogx-api @ git+{OGX_GIT_REPO}@{sha}#subdirectory=src/ogx_api",
@@ -379,7 +372,7 @@ def _write_temp_requirements(lines: list[str]) -> Path:
379372

380373

381374
def _get_lockfile_targets(
382-
install_from_source: bool, rhai_index_url: str
375+
install_from_source: bool, rhai_index_url: str | None
383376
) -> dict[LockfileType, LockfileConfig]:
384377
"""Determine which lock files to generate based on build configuration."""
385378
targets = {}
@@ -462,18 +455,20 @@ def generate_containerfile(version: str):
462455

463456

464457
def main():
465-
env = _load_env(Path(__file__).parent / "build.env")
466-
install_from_source = env.get("OGX_INSTALL_FROM_SOURCE", "").lower() == "true"
467-
rhai_index_url = os.getenv("RHAI_INDEX_URL") or env.get("RHAI_INDEX_URL", "")
458+
config = BuildConfig()
468459

469-
ogx_reqs = _get_ogx_requirements()
460+
ogx_reqs = _get_ogx_requirements(
461+
config.ogx_version, config.ogx_install_from_source
462+
)
470463

471464
assert_command_installed("uv")
472465

473466
print("Generating stripped config.yaml...")
474467
generate_stripped_config()
475468

476-
targets = _get_lockfile_targets(install_from_source, rhai_index_url)
469+
targets = _get_lockfile_targets(
470+
config.ogx_install_from_source, config.rhai_index_url
471+
)
477472

478473
for name, target in targets.items():
479474
print(f"Generating {target.output_path}...")
@@ -507,10 +502,8 @@ def main():
507502
finally:
508503
os.unlink(tmp_path)
509504

510-
version = _validate_version(os.getenv("OGX_VERSION") or env["OGX_VERSION"])
511-
512505
print("Generating Containerfile...")
513-
generate_containerfile(version)
506+
generate_containerfile(config.ogx_version)
514507

515508
print("Done!")
516509

build/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ exec "$runtime" run --rm \
1919
-v "$REPO_ROOT:/workspace:z" \
2020
-w /workspace \
2121
"$IMAGE" \
22-
uv run --with ruamel.yaml build/build.py
22+
uv run --with ruamel.yaml --with pydantic-settings build/build.py

0 commit comments

Comments
 (0)