Source code for hubvault.entry.context

"""
CLI repository-context helpers for :mod:`hubvault.entry`.

This module centralizes repository-path resolution for CLI commands and keeps
repo discovery on top of the public :class:`hubvault.api.HubVaultApi`
surface. The CLI deliberately does not inspect private storage files directly
just to discover the default branch or other repository metadata.

The module contains:

* :class:`CliRepoContext` - Resolved CLI view of one local repository
* :func:`set_cli_repo_path` - Persist the global ``-C`` repo path in Click context
* :func:`get_cli_repo_path` - Resolve the repo path configured for the current CLI run
* :func:`load_cli_repo_context` - Build and cache repository metadata for CLI commands

Example::

    >>> import click
    >>> ctx = click.Context(click.Command("demo"))
    >>> with ctx:
    ...     set_cli_repo_path(ctx, None)
    ...     str(get_cli_repo_path(ctx)).endswith(str(get_cli_repo_path(ctx).name))
    True
"""

from dataclasses import dataclass
from pathlib import Path
from typing import Optional

import click

from .. import HubVaultApi


[docs] @dataclass(frozen=True) class CliRepoContext: """ Describe the repository selection used by CLI commands. :param repo_path: Filesystem path to the local repository root :type repo_path: pathlib.Path :param default_branch: Repository default branch resolved through the public API :type default_branch: str Example:: >>> context = CliRepoContext(repo_path=Path("/tmp/repo"), default_branch="main") >>> context.default_branch 'main' """ repo_path: Path default_branch: str
[docs] @classmethod def from_repo_path(cls, repo_path: Path) -> "CliRepoContext": """ Build CLI repository metadata from a local repository path. :param repo_path: Filesystem path to the local repository root :type repo_path: pathlib.Path :return: Resolved CLI repository context :rtype: CliRepoContext Example:: >>> context = CliRepoContext.from_repo_path # doctest: +SKIP """ probe_api = HubVaultApi(repo_path) refs = probe_api.list_repo_refs() if not refs.branches: raise click.ClickException("Repository does not expose any branches.") candidate_branch = None for ref in refs.branches: if ref.name == "main": candidate_branch = ref.name break if candidate_branch is None: candidate_branch = refs.branches[0].name info = probe_api.repo_info(revision=candidate_branch) return cls(repo_path=repo_path, default_branch=info.default_branch)
[docs] def create_api(self, revision: Optional[str] = None) -> HubVaultApi: """ Build a public API wrapper scoped to this repository context. :param revision: Optional default revision for subsequent API calls. When omitted, the repository default branch is used. :type revision: Optional[str] :return: Public repository API wrapper :rtype: hubvault.api.HubVaultApi Example:: >>> context = CliRepoContext(repo_path=Path("/tmp/repo"), default_branch="main") >>> context.create_api("main").__class__.__name__ 'HubVaultApi' """ return HubVaultApi(self.repo_path, revision=revision or self.default_branch)
[docs] def set_cli_repo_path(ctx: click.Context, repo_path: Optional[str]) -> None: """ Persist the global CLI repo path in the current Click context. :param ctx: Click context for the current CLI invocation :type ctx: click.Context :param repo_path: Repo path from the global ``-C`` option, or ``None`` to use the current working directory :type repo_path: Optional[str] :return: ``None``. :rtype: None Example:: >>> ctx = click.Context(click.Command("demo")) >>> with ctx: ... set_cli_repo_path(ctx, None) ... "repo_path" in ctx.obj True """ ctx.ensure_object(dict) resolved = Path(repo_path or ".").expanduser().resolve() ctx.obj["repo_path"] = str(resolved)
[docs] def get_cli_repo_path(ctx: click.Context) -> Path: """ Return the repo path configured for the current CLI invocation. :param ctx: Click context for the current CLI invocation :type ctx: click.Context :return: Resolved repository path :rtype: pathlib.Path Example:: >>> ctx = click.Context(click.Command("demo")) >>> with ctx: ... set_cli_repo_path(ctx, None) ... isinstance(get_cli_repo_path(ctx), Path) True """ ctx.ensure_object(dict) return Path(ctx.obj.get("repo_path", str(Path.cwd()))).expanduser().resolve()
[docs] def load_cli_repo_context(ctx: click.Context) -> CliRepoContext: """ Build and cache repository metadata for CLI commands. :param ctx: Click context for the current CLI invocation :type ctx: click.Context :return: Cached repository context :rtype: CliRepoContext Example:: >>> context_loader = load_cli_repo_context # doctest: +SKIP """ ctx.ensure_object(dict) cached = ctx.obj.get("repo_context") if isinstance(cached, CliRepoContext): return cached repo_context = CliRepoContext.from_repo_path(get_cli_repo_path(ctx)) ctx.obj["repo_context"] = repo_context return repo_context