Skip to content
Closed
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
25 changes: 20 additions & 5 deletions cloudsmith_cli/cli/commands/credential_helper/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,23 @@
return names if isinstance(names, str) else names[0]


def _launcher_settings_from_cli(ctx) -> dict[str, str]:
def _launcher_value(
option: click.Parameter, ctx: click.Context, value: object
) -> str | None:
"""Return *value* as the text that *option* accepts, or None to skip it."""
if isinstance(value, bool):
return str(value).lower()
# A launcher variable holds one value, so it cannot keep a list of values.
if isinstance(value, tuple):
return None
if isinstance(option.type, click.Choice):
return option.type.normalize_choice(value, ctx)
if isinstance(value, (str, int, float)):
return str(value)
return None


def _launcher_settings_from_cli(ctx: click.Context) -> dict[str, str]:
"""Return the ``CLOUDSMITH_*`` settings that a launcher keeps.

An option with a ``CLOUDSMITH_*`` environment variable is kept when its
Expand All @@ -158,7 +174,7 @@
contexts = []
while ctx is not None:
contexts.insert(0, ctx)
ctx = ctx.parent

Check failure on line 177 in cloudsmith_cli/cli/commands/credential_helper/manage.py

View workflow job for this annotation

GitHub Actions / ty

ty (invalid-assignment)

cloudsmith_cli/cli/commands/credential_helper/manage.py:177:15: invalid-assignment: Object of type `Context | None` is not assignable to `Context` cloudsmith_cli/cli/commands/credential_helper/manage.py:166:38: Declared type info: element `None` of union `Context | None` is not assignable to `Context`

supplied = (ParameterSource.COMMANDLINE, ParameterSource.ENVIRONMENT)
settings = {}
Expand All @@ -169,10 +185,9 @@
envvar = _main_envvar(option)
source = context.get_parameter_source(option.name)
if envvar.startswith("CLOUDSMITH_") and source in supplied:
value = context.params[option.name]
settings[envvar] = (
str(value).lower() if isinstance(value, bool) else value
)
value = _launcher_value(option, context, context.params[option.name])
if value is not None:
settings[envvar] = value
return settings


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from cloudsmith_cli.credential_helpers.generic import PartialInstallError
from cloudsmith_cli.credential_helpers.pnpm.installer import PNPMInstaller

from ....cli.commands.credential_helper.manage import _launcher_settings_from_cli
from ....core.credentials.models import CredentialResult
from ....core.utils import ColorMode
from ....credential_helpers.docker.installer import DockerInstaller
from ....credential_helpers.launchers import (
UnsafeLauncherValue,
Expand Down Expand Up @@ -136,6 +138,39 @@ def test_windows_launcher_refuses_a_value_a_batch_file_cannot_hold(monkeypatch,
_launcher_content("cmd", windows=True, env={"CLOUDSMITH_PROFILE": value})


def test_launcher_refuses_a_value_that_is_not_a_string():
with pytest.raises(TypeError, match="CLOUDSMITH_COLOR"):
_launcher_content(
"cmd", windows=False, env={"CLOUDSMITH_COLOR": ColorMode.NEVER}
)


@pytest.mark.parametrize(
"option_kwargs,value,kept",
[
({"type": click.Choice(ColorMode, case_sensitive=False)}, "NEVER", "never"),
({"type": int}, "3", "3"),
({"type": click.Choice(["a", "b"]), "multiple": True}, "a", None),
],
)
def test_launcher_keeps_a_setting_as_the_text_its_option_accepts(
runner, option_kwargs, value, kept
):
"""A launcher sets one text value per variable, so it skips a list of values."""

@click.command()
@click.option("--setting", envvar="CLOUDSMITH_SETTING", **option_kwargs)
@click.pass_context
def command(ctx, setting):
click.echo(json.dumps(_launcher_settings_from_cli(ctx)))

result = runner.invoke(command, ["--setting", value], catch_exceptions=False)

assert json.loads(result.stdout) == (
{} if kept is None else {"CLOUDSMITH_SETTING": kept}
)


def test_install_passes_flag_and_environment_settings_to_the_launcher(
runner, monkeypatch, tmp_path
):
Expand Down
6 changes: 6 additions & 0 deletions cloudsmith_cli/credential_helpers/launchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ def _launcher_content(
same result as the install.
"""
env = env or {}
for name, value in env.items():
if not isinstance(value, str):
raise TypeError(
f"{name} {value!r} cannot go into the launcher: the value must "
f"be a string, not {type(value).__name__}."
)
if windows:
settings = "".join(
f'set "{name}={batch_value(name, value)}"\r\n'
Expand Down
Loading