Skip to content

harbor_cli.harbor

Attributes

AttrType = TypeVar('AttrType') module-attribute

Classes

ArtifactNameFormatError

Bases: HarborCLIError

Source code in harbor_cli/exceptions.py
class ArtifactNameFormatError(HarborCLIError):
    def __init__(self, s: str) -> None:
        super().__init__(
            f"Artifact string {s} is not in the correct format. "
            "Expected 'project/repo:tag' OR 'project/repo@sha256:digest'",
        )

Functions

__init__(s: str) -> None
Source code in harbor_cli/exceptions.py
def __init__(self, s: str) -> None:
    super().__init__(
        f"Artifact string {s} is not in the correct format. "
        "Expected 'project/repo:tag' OR 'project/repo@sha256:digest'",
    )

ArtifactName

Bases: NamedTuple

Source code in harbor_cli/harbor/artifact.py
class ArtifactName(NamedTuple):
    domain: str | None
    project: str
    repository: str
    reference: str

Attributes

domain: str | None instance-attribute
project: str instance-attribute
repository: str instance-attribute
reference: str instance-attribute

Functions

parse_artifact_name(s: str) -> ArtifactName

Splits an artifact string into domain name (optional), project, repo, and reference (tag or digest).

Raises ValueError if the string is not in the correct format.

Parameters:

Name Type Description Default
s str

Artifact string in the form of [domain/]/{@sha256:,:}

required

Returns:

Type Description
ArtifactName

Named tuple of domain name (optional), project, repo, and reference (tag or digest).

Source code in harbor_cli/harbor/artifact.py
def parse_artifact_name(s: str) -> ArtifactName:
    """Splits an artifact string into domain name (optional), project,
    repo, and reference (tag or digest).

    Raises ValueError if the string is not in the correct format.

    Parameters
    ----------
    s : str
        Artifact string in the form of [domain/]<project>/<repo>{@sha256:<digest>,:<tag>}

    Returns
    -------
    ArtifactName
        Named tuple of domain name (optional), project, repo, and reference (tag or digest).
    """
    parts = s.split("/")
    if len(parts) == 3:
        domain, project, rest = parts
    elif len(parts) == 2:
        project, rest = parts
        domain = None
    else:
        raise ArtifactNameFormatError(s)

    # TODO: make this more robust
    if "@" in rest:
        repo, tag_or_digest = rest.split("@")
    elif ":" in rest:
        parts = rest.split(":")
        if len(parts) != 2:
            raise ArtifactNameFormatError(s)
        repo, tag_or_digest = parts
    else:
        raise ArtifactNameFormatError(s)

    return ArtifactName(domain, project, repo, tag_or_digest)

get_artifact_architecture(artifact: Artifact) -> str | None

Source code in harbor_cli/harbor/artifact.py
def get_artifact_architecture(artifact: Artifact) -> str | None:
    try:
        return str(artifact.extra_attrs["architecture"])  # type: ignore[index]
    except (TypeError, KeyError):
        return None

get_artifact_os(artifact: Artifact) -> str | None

Source code in harbor_cli/harbor/artifact.py
def get_artifact_os(artifact: Artifact) -> str | None:
    try:
        return str(artifact.extra_attrs["os"])  # type: ignore[index]
    except (TypeError, KeyError):
        return None

get_artifact_severity(artifact: Artifact) -> str | None

Attempt to get the severity string for an artifact. Not every artifact has a scan overview, and not every scan overview has a severity string.

Source code in harbor_cli/harbor/artifact.py
def get_artifact_severity(artifact: Artifact) -> str | None:
    """Attempt to get the severity string for an artifact.
    Not every artifact has a scan overview, and not every scan overview
    has a severity string.
    """
    try:
        return artifact.scan.severity  # type: ignore[attr-defined]
    except AttributeError:
        return None

no_headless(f: Callable[P, T]) -> Callable[P, T]

Decorator that causes application to exit if called from a headless environment.

Source code in harbor_cli/output/prompts.py
def no_headless(f: Callable[P, T]) -> Callable[P, T]:
    """Decorator that causes application to exit if called from a headless environment."""

    @wraps(f)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
        if is_headless():
            # TODO: determine caller etc. via the stack
            # If a default argument was passed in, we can return that:
            # NOTE: this disallows None as a default value, but that is consistent
            # with the type annotations of the prompt functions, so it's fine...?
            default = cast(Union[T, EllipsisType], kwargs.get("default"))
            if "default" in kwargs and default not in [None, ...]:
                return default
            prompt = args[0] if args else kwargs.get("prompt") or ""
            exit_err(
                f"Headless session detected; user input required when prompting for {prompt!r}.",
                prompt_args=args,
                prompt_kwargs=kwargs,
            )
        return f(*args, **kwargs)

    return wrapper

path_prompt(prompt: str, default: Any = ..., show_default: bool = True, exist_ok: bool = True, must_exist: bool = False, **kwargs: Any) -> Path

Source code in harbor_cli/output/prompts.py
@no_headless
def path_prompt(
    prompt: str,
    default: Any = ...,
    show_default: bool = True,
    exist_ok: bool = True,
    must_exist: bool = False,
    **kwargs: Any,
) -> Path:
    if isinstance(default, Path):
        default_arg = str(default)
    elif default is None:
        default_arg = ...  # type: ignore
    else:
        default_arg = default

    while True:
        path_str = str_prompt(
            prompt,
            default=default_arg,
            show_default=show_default,
            **kwargs,
        )
        path = Path(path_str)

        if must_exist and not path.exists():
            error(f"Path does not exist: {path_link(path)}")
        elif not exist_ok and path.exists():
            error(f"Path already exists: {path_link(path)}")
        else:
            return path

str_prompt(prompt: str, default: Any = ..., password: bool = False, show_default: bool = True, choices: list[str] | None = None, empty_ok: bool = False, **kwargs: Any) -> str

Prompts the user for a string input. Optionally controls for empty input. Loops until a valid input is provided.

Parameters:

Name Type Description Default
prompt str

Prompt to display to the user.

required
default Any

Default value to use if the user does not provide input. If not provided, the user will be required to provide input.

...
password bool

Whether to hide the input, by default False

False
show_default bool

Whether to show the default value, by default True password=True supercedes this option, and sets it to False.

True
empty_ok bool

Whether to allow input consisting of only whitespace, by default False

False
Source code in harbor_cli/output/prompts.py
@no_headless
def str_prompt(
    prompt: str,
    default: Any = ...,
    password: bool = False,
    show_default: bool = True,
    choices: list[str] | None = None,
    empty_ok: bool = False,
    **kwargs: Any,
) -> str:
    """Prompts the user for a string input. Optionally controls
    for empty input. Loops until a valid input is provided.

    Parameters
    ----------
    prompt : str
        Prompt to display to the user.
    default : Any, optional
        Default value to use if the user does not provide input.
        If not provided, the user will be required to provide input.
    password : bool, optional
        Whether to hide the input, by default False
    show_default : bool, optional
        Whether to show the default value, by default True
        `password=True` supercedes this option, and sets it to False.
    empty_ok : bool, optional
        Whether to allow input consisting of only whitespace, by default False

    """
    # Don't permit secrets to be shown ever
    if password:
        show_default = False

    # Notify user that a default secret will be used,
    # but don't actually show the secret
    if password and default not in (None, ..., ""):
        _prompt_add = "(leave empty to use existing value)"
    else:
        _prompt_add = ""
    msg = prompt_msg(prompt, _prompt_add)

    inp = None
    while not inp:
        inp = Prompt.ask(
            msg,
            console=err_console,
            password=password,
            show_default=show_default,
            default=default,
            choices=choices,
            **kwargs,
        )
        if empty_ok:  # nothing else to check
            break

        if not inp:
            error("Input cannot be empty.")
        elif inp.isspace() and inp != default:
            error("Input cannot solely consist of whitespace.")
        else:
            break
    return inp

prompt_username_secret(default_username: str | None = None, default_secret: str | None = None) -> tuple[str, str]

Source code in harbor_cli/harbor/common.py
@no_headless
def prompt_username_secret(
    default_username: str | None = None, default_secret: str | None = None
) -> tuple[str, str]:
    username = str_prompt(
        "Harbor username",
        default=default_username,
    )
    secret = str_prompt(
        "Harbor secret",
        default=default_secret,
        password=True,
    )
    return username, secret

prompt_basicauth(default: str | None = None) -> str

Source code in harbor_cli/harbor/common.py
def prompt_basicauth(default: str | None = None) -> str:
    return str_prompt(
        "Harbor Base64 Basic Auth (e.g. dXNlcjpwYXNzd29yZA==)",
        default=default,
        password=True,
    )

prompt_credentials_file(default: Path | None = None) -> Path

Source code in harbor_cli/harbor/common.py
def prompt_credentials_file(default: Path | None = None) -> Path:
    return path_prompt(
        "Harbor credentials file (e.g. /path/to/robot.json)",
        default=default,
        show_default=True,
        must_exist=True,
        exist_ok=True,
    )

prompt_url(default: str | None = None) -> str

Source code in harbor_cli/harbor/common.py
def prompt_url(default: str | None = None) -> str:
    return str_prompt(
        "Harbor API URL (e.g. https://harbor.example.com/api/v2.0)",
        default=default,
    )