Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 14 additions & 74 deletions cli/python/base_dev/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import json
import sys
from dataclasses import dataclass
from pathlib import Path

import base_cli
from base_setup.artifacts import reconcile_artifact, resolve_artifact_definitions
from base_setup.artifacts import reconcile_artifact
from base_setup.artifacts import resolve_artifact_definitions # pylint: disable=unused-import
from base_setup.checks import DIAGNOSTIC_JSON_SCHEMA_VERSION
from base_setup.errors import ArtifactError
from base_setup.manifest import ArtifactRequest, BaseManifest, ManifestError, read_manifest
from base_setup.manifest import ArtifactRequest, BaseManifest, ManifestError
from base_setup.manifest import read_manifest # pylint: disable=unused-import
from base_setup.prerequisites import GitHubCliAuthCheckRequest
from base_setup.prerequisites import HomebrewPackageCheckRequest
from base_setup.prerequisites import PrerequisiteCheck
Expand Down Expand Up @@ -38,27 +39,19 @@
from .linux_profile import profile_setup_fix
from .linux_profile import reconcile_linux_debian_apt_artifact
from .linux_profile import reconcile_linux_debian_github_cli_artifact
from .profiles import SUPPORTED_PROFILES # pylint: disable=unused-import
from .profiles import ProfileError
from .profiles import ProfileManifest
from .profiles import ProfileRuntime
from .profiles import dev_manifest_path # pylint: disable=unused-import
from .profiles import normalize_profiles
from .profiles import profile_manifest_path
from .profiles import read_dev_manifest # pylint: disable=unused-import
from .profiles import read_profile_manifest # pylint: disable=unused-import
from .profiles import read_profile_manifests


app = base_cli.App(name="base_dev")
SUPPORTED_PROFILES = ("dev", "sre", "ai", "linux-lab")


@dataclass(frozen=True)
class ProfileManifest:
name: str
manifest: BaseManifest
definitions: tuple[ArtifactDefinition, ...]


@dataclass(frozen=True)
class ProfileRuntime:
profile: str
project: str


class ProfileError(ValueError):
pass


def main(argv: list[str] | None = None) -> int:
Expand Down Expand Up @@ -97,59 +90,6 @@ def run(ctx: base_cli.Context, action: str, dry_run: bool, output_format: str, p
return base_cli.ExitCode.USAGE_ERROR


def normalize_profiles(profiles: tuple[str, ...]) -> tuple[str, ...]:
if not profiles:
return ("dev",)

normalized: list[str] = []
for profile_list in profiles:
for raw_profile in profile_list.split(","):
profile = raw_profile.strip().lower()
if not profile:
raise ProfileError("Profile list must not contain empty entries.")
if profile not in SUPPORTED_PROFILES:
display_profile = raw_profile.strip() or raw_profile
raise ProfileError(
f"Unsupported profile '{display_profile}'. Expected one of: {', '.join(SUPPORTED_PROFILES)}."
)
if profile not in normalized:
normalized.append(profile)
return tuple(normalized)


def read_profile_manifests(ctx: base_cli.Context, profiles: tuple[str, ...]) -> tuple[ProfileManifest, ...]:
profile_manifests: list[ProfileManifest] = []
for profile in profiles:
if profile in {"ai", "linux-lab"}:
continue
manifest = read_profile_manifest(ctx, profile)
definitions = resolve_artifact_definitions(manifest.artifacts)
profile_manifests.append(ProfileManifest(profile, manifest, definitions))
return tuple(profile_manifests)


def read_profile_manifest(ctx: base_cli.Context, profile: str) -> BaseManifest:
if ctx.base_home is None:
raise ManifestError("BASE_HOME is required to load Base's prerequisite profile manifests.")
return read_manifest(profile_manifest_path(ctx.base_home, profile))


def read_dev_manifest(ctx: base_cli.Context) -> BaseManifest:
if ctx.base_home is None:
raise ManifestError("BASE_HOME is required to load Base's developer prerequisite manifest.")
return read_profile_manifest(ctx, "dev")


def dev_manifest_path(base_home: Path) -> Path:
return base_home / "lib" / "base" / "dev_manifest.yaml"


def profile_manifest_path(base_home: Path, profile: str) -> Path:
if profile == "dev":
return dev_manifest_path(base_home)
return base_home / "lib" / "base" / f"{profile}_manifest.yaml"


def setup_profile_manifests(
ctx: base_cli.Context,
profile_manifests: tuple[ProfileManifest, ...],
Expand Down
81 changes: 81 additions & 0 deletions cli/python/base_dev/profiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

import base_cli
from base_setup.artifacts import resolve_artifact_definitions
from base_setup.manifest import BaseManifest, ManifestError, read_manifest
from base_setup.registry import ArtifactDefinition

SUPPORTED_PROFILES = ("dev", "sre", "ai", "linux-lab")


@dataclass(frozen=True)
class ProfileManifest:
name: str
manifest: BaseManifest
definitions: tuple[ArtifactDefinition, ...]


@dataclass(frozen=True)
class ProfileRuntime:
profile: str
project: str


class ProfileError(ValueError):
pass


def normalize_profiles(profiles: tuple[str, ...]) -> tuple[str, ...]:
if not profiles:
return ("dev",)

normalized: list[str] = []
for profile_list in profiles:
for raw_profile in profile_list.split(","):
profile = raw_profile.strip().lower()
if not profile:
raise ProfileError("Profile list must not contain empty entries.")
if profile not in SUPPORTED_PROFILES:
display_profile = raw_profile.strip() or raw_profile
raise ProfileError(
f"Unsupported profile '{display_profile}'. Expected one of: {', '.join(SUPPORTED_PROFILES)}."
)
if profile not in normalized:
normalized.append(profile)
return tuple(normalized)


def read_profile_manifests(ctx: base_cli.Context, profiles: tuple[str, ...]) -> tuple[ProfileManifest, ...]:
profile_manifests: list[ProfileManifest] = []
for profile in profiles:
if profile in {"ai", "linux-lab"}:
continue
manifest = read_profile_manifest(ctx, profile)
definitions = resolve_artifact_definitions(manifest.artifacts)
profile_manifests.append(ProfileManifest(profile, manifest, definitions))
return tuple(profile_manifests)


def read_profile_manifest(ctx: base_cli.Context, profile: str) -> BaseManifest:
if ctx.base_home is None:
raise ManifestError("BASE_HOME is required to load Base's prerequisite profile manifests.")
return read_manifest(profile_manifest_path(ctx.base_home, profile))


def read_dev_manifest(ctx: base_cli.Context) -> BaseManifest:
if ctx.base_home is None:
raise ManifestError("BASE_HOME is required to load Base's developer prerequisite manifest.")
return read_profile_manifest(ctx, "dev")


def dev_manifest_path(base_home: Path) -> Path:
return base_home / "lib" / "base" / "dev_manifest.yaml"


def profile_manifest_path(base_home: Path, profile: str) -> Path:
if profile == "dev":
return dev_manifest_path(base_home)
return base_home / "lib" / "base" / f"{profile}_manifest.yaml"
20 changes: 20 additions & 0 deletions cli/python/base_dev/tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ def test_main_reports_missing_action_without_traceback(self) -> None:
self.assertIn("Missing argument", stderr)
self.assertNotIn("Traceback", stderr)

def test_engine_reexports_profile_helpers(self) -> None:
from base_dev import profiles

expected_names = (
"ProfileError",
"ProfileManifest",
"ProfileRuntime",
"SUPPORTED_PROFILES",
"dev_manifest_path",
"normalize_profiles",
"profile_manifest_path",
"read_dev_manifest",
"read_profile_manifest",
"read_profile_manifests",
)

for name in expected_names:
with self.subTest(name=name):
self.assertIs(getattr(engine, name), getattr(profiles, name))

def test_dev_manifest_declares_supported_developer_tools(self) -> None:
manifest = engine.read_manifest(Path(__file__).resolve().parents[4] / "lib" / "base" / "dev_manifest.yaml")
artifacts = {(artifact.artifact_type, artifact.name, artifact.version) for artifact in manifest.artifacts}
Expand Down
Loading