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
2 changes: 1 addition & 1 deletion .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Set up Python ${{ matrix.python-version }}
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Set up Python
Expand Down Expand Up @@ -69,7 +69,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Expose standalone Python package checkout as sibling
Expand Down Expand Up @@ -121,7 +121,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Expose standalone Python package checkout as sibling
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Expose standalone Python package checkout as sibling
Expand Down Expand Up @@ -275,7 +275,7 @@ jobs:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
repository: basefoundry/base-cli
ref: 5828f2d829dd9eb7e392455e454768cff7db7aa6
ref: 8c3854fc69564a264bf66e27a4c046f28ebfcfc5
path: .dependencies/base-cli

- name: Expose reusable Bash library checkout as sibling
Expand Down
45 changes: 38 additions & 7 deletions cli/python/base_cli_adapters/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

import os
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import base_cli
from base_cli.config import UserConfig
from base_cli.config import UserGithubConfig
from base_cli.config import UserIdeConfig
from base_cli.config import UserIdePreference
from base_cli.config import UserWorkspaceConfig
from base_cli.config import load_yaml_file as load_cli_yaml_file
from base_cli.ide_schema import parse_ide_extensions
from base_cli.ide_schema import parse_ide_settings

Expand All @@ -19,12 +15,47 @@
from .paths import base_state_root


@dataclass(frozen=True)
class UserIdePreference:
enabled: bool | None
install: bool | None
extra_extensions: tuple[str, ...]
settings: dict[str, Any]


@dataclass(frozen=True)
class UserIdeConfig:
enabled: bool | None
preferences: dict[str, UserIdePreference]


@dataclass(frozen=True)
class UserWorkspaceConfig:
root: Path | None
manifest: Path | None = None
manifest_source: str | None = None


@dataclass(frozen=True)
class UserGithubConfig:
default_owner: str | None
clone_protocol: str | None


@dataclass(frozen=True)
class UserConfig:
raw: dict[str, Any]
ide: UserIdeConfig
workspace: UserWorkspaceConfig = UserWorkspaceConfig(root=None)
github: UserGithubConfig = UserGithubConfig(default_owner=None, clone_protocol=None)


def user_config_path(home: Path | None = None) -> Path:
return base_state_root(home) / "config.yaml"


def load_yaml_file(path: Path) -> dict[str, Any]:
return base_cli.config.load_yaml_file(path)
return load_cli_yaml_file(path)


def load_user_config(home: Path | None = None) -> dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_cli_adapters/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def build_finished_record(
)
if "project" not in record:
record["project"] = project_name(context)
version = base_version(context.base_home)
version = base_version(context.application_home)
if version:
record["base_version"] = version
return {key: value for key, value in record.items() if value}
Expand Down
10 changes: 9 additions & 1 deletion cli/python/base_cli_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from base_cli_adapters.config import load_config
from base_cli_adapters.config import load_yaml_file
from base_cli_adapters.config import read_user_config
from base_cli_adapters.config import UserConfig
from base_cli_adapters.history import HISTORY_SCOPE_INTERNAL
from base_cli_adapters.history import write_finished_record
from base_cli_adapters.paths import base_cache_root
Expand Down Expand Up @@ -105,16 +106,23 @@ def resolve_runtime(
explicit,
),
resolve_runtime=resolve_runtime,
resolve_workspace_root=_resolve_workspace_root,
history_writer=_write_finished_record,
display_command=_display_command,
history_display_command=history_display_command,
)


def _read_user_config() -> base_cli.UserConfig:
def _read_user_config() -> UserConfig:
return read_user_config(supported_ides=SUPPORTED_IDES)


def _resolve_workspace_root(user_config: object | None) -> Path | None:
if isinstance(user_config, UserConfig):
return user_config.workspace.root
return None


def _write_finished_record(*args: Any) -> None:
"""Resolve the adapter at call time so consumers can test the lifecycle."""
write_finished_record(*args)
Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_config/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from typing import Any

import base_cli
from base_cli.config import UserConfig
from base_cli.redaction import REDACTED, is_secret_key, redact_text_value
from base_cli_adapters.config import load_user_config, read_user_config, user_config_path
from base_cli_adapters.config import UserConfig
from base_cli_profile import base_cli_app
from base_setup.ide_schema import SUPPORTED_IDES

Expand Down
6 changes: 3 additions & 3 deletions cli/python/base_dev/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ def read_profile_manifests(ctx: base_cli.Context, profiles: tuple[str, ...]) ->


def read_profile_manifest(ctx: base_cli.Context, profile: str) -> BaseManifest:
if ctx.base_home is None:
if ctx.application_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))
return read_manifest(profile_manifest_path(ctx.application_home, profile))


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

Expand Down
4 changes: 2 additions & 2 deletions cli/python/base_projects/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -879,8 +879,8 @@ def bind_project_context(ctx: base_cli.Context, project: Project) -> Project:


def find_named_project(ctx: base_cli.Context, project_name: str, workspace: str | None) -> Project | None:
if workspace is None and project_name == "base" and ctx.base_home is not None:
return read_project(ctx.base_home / "base_manifest.yaml")
if workspace is None and project_name == "base" and ctx.application_home is not None:
return read_project(ctx.application_home / "base_manifest.yaml")

if workspace is None:
active_project = resolve_active_project(project_name)
Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_projects/tests/test_workspace_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def test_workspace_init_remote_dry_run_without_local_manifest_stops_after_config
def test_resolve_workspace_config_repo_path_uses_explicit_repo_name_guard(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
workspace_root = Path(tmpdir) / "workspace"
ctx = SimpleNamespace(workspace_root=workspace_root, base_home=None)
ctx = SimpleNamespace(workspace_root=workspace_root, application_home=None)
source = workspace_init_module.WorkspaceInitSource(
display="codeforester/base-workspace",
repo_spec="codeforester/base-workspace",
Expand Down
4 changes: 2 additions & 2 deletions cli/python/base_projects/workspace_clone_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ def workspace_clone_command(ctx: base_cli.Context, options: WorkspaceCloneOption

workspace_root = workspace_root.resolve(strict=False)

if ctx.base_home is None:
if ctx.application_home is None:
ctx.log.error("BASE_HOME is required to clone workspace repositories.")
return base_cli.ExitCode.FAILURE

basectl = ctx.base_home / "bin" / "basectl"
basectl = ctx.application_home / "bin" / "basectl"
print(f"Workspace clone: {workspace_root} ({len(manifest.repos)} repositories)")
print(f"Workspace manifest: {manifest.path} ({manifest.name})")

Expand Down
4 changes: 2 additions & 2 deletions cli/python/base_projects/workspace_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ def workspace_configure_command(
*,
dry_run: bool,
) -> int:
if ctx.base_home is None:
if ctx.application_home is None:
ctx.log.error("BASE_HOME is required to configure workspace repositories.")
return base_cli.ExitCode.FAILURE

basectl = ctx.base_home / "bin" / "basectl"
basectl = ctx.application_home / "bin" / "basectl"
targets = workspace_configure_targets(workspace_root, workspace_manifest)
print_workspace_configure_header(workspace_root, workspace_manifest, len(targets))

Expand Down
4 changes: 2 additions & 2 deletions cli/python/base_projects/workspace_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def resolve_workspace_root(ctx: base_cli.Context, workspace: str | None) -> Path
return Path(workspace).expanduser().resolve()
if ctx.workspace_root is not None:
return ctx.workspace_root
if ctx.base_home is None:
if ctx.application_home is None:
raise ProjectDiscoveryError("BASE_HOME is required to discover workspace projects.")
return ctx.base_home.parent.resolve()
return ctx.application_home.parent.resolve()


def effective_workspace_manifest(ctx: base_cli.Context, workspace_manifest: str | None) -> str | None:
Expand Down
8 changes: 4 additions & 4 deletions cli/python/base_projects/workspace_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,16 @@ def resolve_workspace_init_root(ctx: base_cli.Context, workspace: str | None, co
if ctx.workspace_root is not None:
return ctx.workspace_root
if config_repo is None:
if ctx.base_home is None:
if ctx.application_home is None:
raise ProjectDiscoveryError("BASE_HOME is required to resolve the default workspace root.")
return ctx.base_home.parent.resolve(strict=False)
return ctx.application_home.parent.resolve(strict=False)
return config_repo.parent.resolve(strict=False)


def clone_workspace_config_repo(ctx: base_cli.Context, repo_spec: str, target: Path, *, dry_run: bool) -> None:
if ctx.base_home is None:
if ctx.application_home is None:
raise WorkspaceManifestError("BASE_HOME is required to clone the workspace configuration repository.")
basectl = ctx.base_home / "bin" / "basectl"
basectl = ctx.application_home / "bin" / "basectl"
command = [str(basectl), "repo", "clone", repo_spec, "--path", str(target)]
if dry_run:
command.append("--dry-run")
Expand Down
6 changes: 3 additions & 3 deletions cli/python/base_projects/workspace_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ def workspace_setup_command(
print_workspace_setup_header(workspace_root, workspace_manifest, len(targets))

counts = WorkspaceSetupCounts()
if not dry_run and ctx.base_home is None:
if not dry_run and ctx.application_home is None:
ctx.log.error("BASE_HOME is required to execute workspace setup.")
return base_cli.ExitCode.FAILURE

basectl = ctx.base_home / "bin" / "basectl" if ctx.base_home is not None else None
basectl = ctx.application_home / "bin" / "basectl" if ctx.application_home is not None else None
if not dry_run and (basectl is None or not basectl.is_file() or not os.access(basectl, os.X_OK)):
ctx.log.error("Base CLI '%s' is missing or is not executable.", basectl)
return base_cli.ExitCode.FAILURE
Expand Down Expand Up @@ -241,7 +241,7 @@ def execute_workspace_setup_target(
command.append(target.project_name)

env = os.environ.copy()
env["BASE_HOME"] = str(ctx.base_home)
env["BASE_HOME"] = str(ctx.application_home)
for variable in ("BASE_PROJECT", "BASE_PROJECT_ROOT", "BASE_PROJECT_MANIFEST", "BASE_PROJECT_VENV_DIR"):
env.pop(variable, None)

Expand Down
4 changes: 2 additions & 2 deletions cli/python/base_prompt/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ def prompt_definition(name: str) -> PromptDefinition:


def require_base_home(ctx: base_cli.Context) -> Path:
if ctx.base_home is None:
if ctx.application_home is None:
raise PromptError("Base home is unavailable. Run this command through 'basectl prompt'.")
return ctx.base_home
return ctx.application_home


def render_prompt(base_home: Path, prompt: PromptDefinition) -> str:
Expand Down
6 changes: 3 additions & 3 deletions cli/python/base_setup/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import base_cli
from base_cli_profile import base_cli_app
from base_cli.config import UserConfig
from base_cli_adapters.config import UserConfig
from base_cli_adapters.paths import discover_manifest
from base_devcontainer.export import DevcontainerExportError
from base_devcontainer.export import build_devcontainer_export
Expand Down Expand Up @@ -246,9 +246,9 @@ def validate_project_name(manifest: BaseManifest, expected_project: str | None)


def read_default_manifest(ctx: base_cli.Context) -> BaseManifest:
if ctx.base_home is None:
if ctx.application_home is None:
raise ManifestError("BASE_HOME is required to load Base's default artifact manifest.")
default_manifest_path = ctx.base_home / "lib" / "base" / "default_manifest.yaml"
default_manifest_path = ctx.application_home / "lib" / "base" / "default_manifest.yaml"
return read_manifest(default_manifest_path)


Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_setup/ide.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import base_cli
from base_cli.config import UserConfig
from base_cli_adapters.config import UserConfig
from .ide_schema import IDE_DEFINITIONS
from .ide_schema import IdeDefinition

Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_setup/manifest_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import os

from base_cli.config import UserConfig, UserIdeConfig
from base_cli_adapters.config import UserConfig, UserIdeConfig

from .artifacts import check_artifact
from .artifacts import resolve_artifact_definitions
Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_setup/setup_reconcile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from dataclasses import replace

import base_cli
from base_cli.config import UserConfig
from base_cli_adapters.config import UserConfig

from .artifacts import merge_artifacts
from .artifacts import ProjectRuntimeConfig
Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_setup/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from pathlib import Path
from unittest import mock

from base_cli.config import UserConfig, UserIdeConfig
from base_cli_adapters.config import UserConfig, UserIdeConfig
from base_setup.engine import main


Expand Down
2 changes: 1 addition & 1 deletion cli/python/base_setup/tests/test_user_ide_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pathlib import Path
from unittest import mock

from base_cli.config import UserConfig, UserIdeConfig, UserIdePreference
from base_cli_adapters.config import UserConfig, UserIdeConfig, UserIdePreference
from base_setup import engine, ide
from base_setup.github_manifest import GithubConfig, GithubPrConfig
from base_setup.manifest import BaseManifest, IdeConfig, read_manifest
Expand Down
6 changes: 3 additions & 3 deletions cli/python/base_trust/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def allow_command(ctx: base_cli.Context, project: str, workspace: str | None, ma
return base_cli.ExitCode.USAGE_ERROR

print_identity("Allowing manifest command trust", identity)
ManifestCommandTrustStore().allow(identity, base_version=read_base_version(ctx.base_home))
ManifestCommandTrustStore().allow(identity, base_version=read_base_version(ctx.application_home))
print(f"Allowed manifest commands for project '{identity.project_name}'.")
return base_cli.ExitCode.SUCCESS

Expand Down Expand Up @@ -220,8 +220,8 @@ def workspace_status_projects(ctx: base_cli.Context, workspace: str | None) -> t
if active_project is not None:
projects_by_name[active_project.name] = active_project

if ctx.base_home is not None:
base_manifest = ctx.base_home / "base_manifest.yaml"
if ctx.application_home is not None:
base_manifest = ctx.application_home / "base_manifest.yaml"
if base_manifest.is_file():
base_project = read_project(base_manifest)
projects_by_name[base_project.name] = base_project
Expand Down
Loading