cloudsmith-cli 1.29.0__tar.gz → 1.30.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cloudsmith_cli-1.29.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.30.1}/PKG-INFO +16 -28
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/README.md +12 -25
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/command.py +43 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/auth.py +33 -11
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/manage.py +73 -10
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/terraform.py +12 -11
- cloudsmith_cli-1.30.1/cloudsmith_cli/cli/commands/domains.py +305 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/download.py +7 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/login.py +76 -6
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/logout.py +7 -7
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/main.py +4 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/mcp.py +10 -7
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/policy/deny.py +2 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/policy/license.py +3 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/policy/vulnerability.py +3 -3
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/repos.py +2 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/tokens.py +20 -24
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/update.py +4 -4
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/upstream.py +2 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/vulnerabilities.py +27 -8
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/whoami.py +7 -7
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/config.py +137 -9
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/decorators.py +182 -32
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/exceptions.py +16 -6
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/saml.py +0 -24
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/utils.py +3 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/validators.py +16 -10
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/webserver.py +5 -4
- cloudsmith_cli-1.30.1/cloudsmith_cli/conftest.py +90 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/exceptions.py +26 -5
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/files.py +20 -8
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/init.py +18 -5
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/orgs.py +5 -9
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/user.py +3 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/vulnerabilities.py +31 -17
- cloudsmith_cli-1.30.1/cloudsmith_cli/core/api/workspaces.py +22 -0
- cloudsmith_cli-1.30.1/cloudsmith_cli/core/certificate_errors.py +80 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/config.py +32 -19
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/models.py +2 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/cache.py +11 -9
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +20 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/gcp.py +8 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +1 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/download.py +4 -4
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/keyring.py +41 -17
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/mcp/server.py +6 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/pagination.py +16 -13
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/session.py +91 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/sso.py +11 -11
- cloudsmith_cli-1.30.1/cloudsmith_cli/core/telemetry.py +841 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/update_check.py +27 -8
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/utils.py +5 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/cargo/installer.py +26 -27
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/cargo/runtime.py +4 -11
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/common.py +27 -3
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/docker/installer.py +29 -28
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/docker/runtime.py +3 -10
- cloudsmith_cli-1.30.1/cloudsmith_cli/credential_helpers/domains.py +399 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/generic.py +4 -9
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/launchers.py +25 -33
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/pnpm/installer.py +18 -29
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/pnpm/rc.py +7 -4
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/pnpm/runtime.py +3 -10
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/terraform/installer.py +5 -20
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/terraform/runtime.py +28 -15
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/terraform/terraformrc.py +1 -1
- cloudsmith_cli-1.30.1/cloudsmith_cli/data/VERSION +1 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/data/config.ini +11 -5
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1/cloudsmith_cli.egg-info}/PKG-INFO +16 -28
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli.egg-info/SOURCES.txt +4 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli.egg-info/requires.txt +3 -2
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/pyproject.toml +7 -5
- cloudsmith_cli-1.29.0/cloudsmith_cli/cli/commands/domains.py +0 -304
- cloudsmith_cli-1.29.0/cloudsmith_cli/conftest.py +0 -16
- cloudsmith_cli-1.29.0/cloudsmith_cli/credential_helpers/custom_domains.py +0 -378
- cloudsmith_cli-1.29.0/cloudsmith_cli/credential_helpers/default_domains.py +0 -305
- cloudsmith_cli-1.29.0/cloudsmith_cli/data/VERSION +0 -1
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/LICENSE +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/MANIFEST.in +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/__main__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/check.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/copy.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/cargo.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/docker.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/generic.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/delete.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/docs.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/entitlements.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/help_.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/list_.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/metadata.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/move.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/push.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/registry.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/resync.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/status.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/commands/tags.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/dsc_parser.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/metadata_common.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/table.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/cli/types.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/distros.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/entitlements.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/metadata.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/metrics.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/packages.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/quota.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/rates.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/repos.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/status.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/upstreams.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/v2.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/api/version.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/cache_utils.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/chain.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/buildkite.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/provider.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/installation.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/macos_keychain.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/mcp/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/mcp/data.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/ratelimits.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/rest.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/self_update.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/core/version.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/backends.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/cargo/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/pnpm/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/credential_helpers/terraform/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/data/credentials.ini +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/templates/__init__.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/templates/auth_error.html +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/templates/auth_success.html +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/templates/auth_two_factor.html +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli/wrapper.py +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/cloudsmith_cli.egg-info/top_level.txt +0 -0
- {cloudsmith_cli-1.29.0 → cloudsmith_cli-1.30.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudsmith-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.30.1
|
|
4
4
|
Summary: Cloudsmith Command-Line Interface (CLI)
|
|
5
5
|
Author-email: Cloudsmith Ltd <support@cloudsmith.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Utilities
|
|
|
22
22
|
Requires-Python: >=3.11.0
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: certifi>=
|
|
25
|
+
Requires-Dist: certifi>=2026.10.23
|
|
26
26
|
Requires-Dist: click!=8.3.0,>=8.2
|
|
27
27
|
Requires-Dist: click-configfile>=0.2.3
|
|
28
28
|
Requires-Dist: click-didyoumean>=0.0.3
|
|
@@ -37,13 +37,14 @@ Requires-Dist: mcp==2.3.0
|
|
|
37
37
|
Requires-Dist: pydantic-settings>=2.14.2
|
|
38
38
|
Requires-Dist: PyJWT[crypto]>=2.13.0
|
|
39
39
|
Requires-Dist: python-toon==0.2.0
|
|
40
|
-
Requires-Dist: requests>=2.
|
|
40
|
+
Requires-Dist: requests>=2.32.5
|
|
41
41
|
Requires-Dist: requests_toolbelt>=1.0.0
|
|
42
42
|
Requires-Dist: rich>=13.0.0
|
|
43
43
|
Requires-Dist: semver>=2.7.9
|
|
44
44
|
Requires-Dist: starlette>=1.3.1
|
|
45
45
|
Requires-Dist: urllib3>=2.5
|
|
46
46
|
Requires-Dist: tomlkit>=0.15.0
|
|
47
|
+
Requires-Dist: sentry-sdk<3,>=2
|
|
47
48
|
Provides-Extra: aws
|
|
48
49
|
Requires-Dist: boto3[crt]>=1.26.0; extra == "aws"
|
|
49
50
|
Provides-Extra: gcp
|
|
@@ -102,8 +103,8 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
102
103
|
- `entitlements`|`ents`: List entitlements for a repository.
|
|
103
104
|
- `packages`: List packages for a repository. (Aliases `repos list`)
|
|
104
105
|
- `repos`: List repositories for a namespace (owner).
|
|
105
|
-
- `login`|`token`: Retrieve your API authentication token/key via login.
|
|
106
|
-
- `logout`: Clear stored authentication credentials and
|
|
106
|
+
- `login`|`token`: [DEPRECATED] Retrieve your API authentication token/key via login.
|
|
107
|
+
- `logout`: Clear stored authentication credentials and SAML tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
|
|
107
108
|
- `metadata`: Manage arbitrary JSON metadata (SBOM, BuildInfo, custom) attached to a package.
|
|
108
109
|
- `add`: Attach a new metadata entry to a package.
|
|
109
110
|
- `list`|`ls`: List metadata entries for a package, or fetch a single entry by slug.
|
|
@@ -367,7 +368,7 @@ Both configuration files use the simple INI format, such as:
|
|
|
367
368
|
api_key=1234567890abcdef1234567890abcdef
|
|
368
369
|
```
|
|
369
370
|
|
|
370
|
-
Additionally, the CLI will store
|
|
371
|
+
Additionally, the CLI will store SAML access and refresh tokens in the system keyring
|
|
371
372
|
using the [`keyring`](https://github.com/jaraco/keyring) library.
|
|
372
373
|
|
|
373
374
|
|
|
@@ -378,8 +379,9 @@ See the [default config](https://raw.githubusercontent.com/cloudsmith-io/cloudsm
|
|
|
378
379
|
You can specify the following configuration options:
|
|
379
380
|
|
|
380
381
|
- `api_host`: The API host to connect to.
|
|
381
|
-
- `
|
|
382
|
-
- `
|
|
382
|
+
- `proxy`: The proxy to connect through for all HTTP requests, including API, SAML, upload, download, MCP, update, credential helper, OIDC, and error report connections. Use `--proxy` or `CLOUDSMITH_PROXY` to set it for one command. The config key `api_proxy`, flag `--api-proxy`, and environment variable `CLOUDSMITH_API_PROXY` remain supported for compatibility. In config and environment variables, the new name takes precedence over the alias. Without `proxy`, the CLI uses the `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` environment variables. When `proxy` is set, it takes precedence over them.
|
|
383
|
+
- `ssl_verify`: Whether or not to verify SSL certificates for HTTPS requests, including API, SAML, uploads, downloads, and MCP (default: `true`). Use `CLOUDSMITH_SSL_VERIFY=1` to enable verification or `CLOUDSMITH_SSL_VERIFY=0` / `--without-ssl-verify` to disable it. Disabling verification is dangerous; prefer disabling only strict checks where possible. The config key `api_ssl_verify`, flag `--without-api-ssl-verify` / `-S`, and environment variable `CLOUDSMITH_WITHOUT_API_SSL_VERIFY` remain supported for compatibility. Command-line settings override environment variables, which override config; within the same source, the new spelling takes precedence.
|
|
384
|
+
- `ssl_verify_strict`: Whether or not to apply strict X.509 certificate verification (`VERIFY_X509_STRICT`, enabled by default on Python 3.13+). Set to `false` (or use `--without-ssl-verify-strict` / `CLOUDSMITH_SSL_VERIFY_STRICT=0`) if a TLS-intercepting proxy issues certificates that fail these checks. `CLOUDSMITH_SSL_VERIFY_STRICT=1` keeps strict verification enabled (the default). Certificates are still verified.
|
|
383
385
|
- `api_user_agent`: The user agent to use for requests.
|
|
384
386
|
|
|
385
387
|
### Credentials (credentials.ini)
|
|
@@ -418,29 +420,15 @@ cloudsmith auth --owner example --no-browser
|
|
|
418
420
|
|
|
419
421
|
#### Getting Your API Key
|
|
420
422
|
|
|
421
|
-
|
|
423
|
+
Get your API key from the Cloudsmith web app: click your user icon in the top right corner, then click **Personal API Keys**. See [API Key](https://docs.cloudsmith.com/accounts-and-teams/api-key) for more information. If you authenticated with `cloudsmith auth`, you can also use `cloudsmith tokens create` to create an API key.
|
|
422
424
|
|
|
423
|
-
|
|
424
|
-
cloudsmith login
|
|
425
|
-
Login: you@example.com
|
|
426
|
-
Password:
|
|
427
|
-
Repeat for confirmation:
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
*Note:* Please ensure you use your email for the 'Login' prompt and not your user slug/identifier.
|
|
431
|
-
|
|
432
|
-
The resulting output looks something like:
|
|
433
|
-
|
|
434
|
-
```
|
|
435
|
-
Retrieving API token for 'you@example.com' ... OK
|
|
436
|
-
Your API token is: 1234567890abcdef1234567890abcdef
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
Once you have your API key you can then put this into your `credentials.ini`, use it as an environment variable `export CLOUDSMITH_API_KEY=your_key_here` or pass it to the CLI using the `-k your_key_here` flag.
|
|
425
|
+
Give your API key to the CLI in one of these ways:
|
|
440
426
|
|
|
441
|
-
|
|
427
|
+
- Set the `CLOUDSMITH_API_KEY` environment variable: `export CLOUDSMITH_API_KEY=your_key_here`.
|
|
428
|
+
- Put the key into your `credentials.ini` file.
|
|
429
|
+
- Pass the key to the CLI with the `-k your_key_here` flag.
|
|
442
430
|
|
|
443
|
-
|
|
431
|
+
*Note:* The `cloudsmith login` command and its `cloudsmith token` alias are deprecated. They retrieve your API key with your email address and password, and they will stop working on November 20, 2026, when Cloudsmith disables username and password login. See the [Cloudsmith changelog](https://cloudsmith.com/changelog). After that, both commands fail with a message that explains how to get an API key instead.
|
|
444
432
|
|
|
445
433
|
|
|
446
434
|
## Uploading Packages
|
|
@@ -47,8 +47,8 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
47
47
|
- `entitlements`|`ents`: List entitlements for a repository.
|
|
48
48
|
- `packages`: List packages for a repository. (Aliases `repos list`)
|
|
49
49
|
- `repos`: List repositories for a namespace (owner).
|
|
50
|
-
- `login`|`token`: Retrieve your API authentication token/key via login.
|
|
51
|
-
- `logout`: Clear stored authentication credentials and
|
|
50
|
+
- `login`|`token`: [DEPRECATED] Retrieve your API authentication token/key via login.
|
|
51
|
+
- `logout`: Clear stored authentication credentials and SAML tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
|
|
52
52
|
- `metadata`: Manage arbitrary JSON metadata (SBOM, BuildInfo, custom) attached to a package.
|
|
53
53
|
- `add`: Attach a new metadata entry to a package.
|
|
54
54
|
- `list`|`ls`: List metadata entries for a package, or fetch a single entry by slug.
|
|
@@ -312,7 +312,7 @@ Both configuration files use the simple INI format, such as:
|
|
|
312
312
|
api_key=1234567890abcdef1234567890abcdef
|
|
313
313
|
```
|
|
314
314
|
|
|
315
|
-
Additionally, the CLI will store
|
|
315
|
+
Additionally, the CLI will store SAML access and refresh tokens in the system keyring
|
|
316
316
|
using the [`keyring`](https://github.com/jaraco/keyring) library.
|
|
317
317
|
|
|
318
318
|
|
|
@@ -323,8 +323,9 @@ See the [default config](https://raw.githubusercontent.com/cloudsmith-io/cloudsm
|
|
|
323
323
|
You can specify the following configuration options:
|
|
324
324
|
|
|
325
325
|
- `api_host`: The API host to connect to.
|
|
326
|
-
- `
|
|
327
|
-
- `
|
|
326
|
+
- `proxy`: The proxy to connect through for all HTTP requests, including API, SAML, upload, download, MCP, update, credential helper, OIDC, and error report connections. Use `--proxy` or `CLOUDSMITH_PROXY` to set it for one command. The config key `api_proxy`, flag `--api-proxy`, and environment variable `CLOUDSMITH_API_PROXY` remain supported for compatibility. In config and environment variables, the new name takes precedence over the alias. Without `proxy`, the CLI uses the `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` environment variables. When `proxy` is set, it takes precedence over them.
|
|
327
|
+
- `ssl_verify`: Whether or not to verify SSL certificates for HTTPS requests, including API, SAML, uploads, downloads, and MCP (default: `true`). Use `CLOUDSMITH_SSL_VERIFY=1` to enable verification or `CLOUDSMITH_SSL_VERIFY=0` / `--without-ssl-verify` to disable it. Disabling verification is dangerous; prefer disabling only strict checks where possible. The config key `api_ssl_verify`, flag `--without-api-ssl-verify` / `-S`, and environment variable `CLOUDSMITH_WITHOUT_API_SSL_VERIFY` remain supported for compatibility. Command-line settings override environment variables, which override config; within the same source, the new spelling takes precedence.
|
|
328
|
+
- `ssl_verify_strict`: Whether or not to apply strict X.509 certificate verification (`VERIFY_X509_STRICT`, enabled by default on Python 3.13+). Set to `false` (or use `--without-ssl-verify-strict` / `CLOUDSMITH_SSL_VERIFY_STRICT=0`) if a TLS-intercepting proxy issues certificates that fail these checks. `CLOUDSMITH_SSL_VERIFY_STRICT=1` keeps strict verification enabled (the default). Certificates are still verified.
|
|
328
329
|
- `api_user_agent`: The user agent to use for requests.
|
|
329
330
|
|
|
330
331
|
### Credentials (credentials.ini)
|
|
@@ -363,29 +364,15 @@ cloudsmith auth --owner example --no-browser
|
|
|
363
364
|
|
|
364
365
|
#### Getting Your API Key
|
|
365
366
|
|
|
366
|
-
|
|
367
|
+
Get your API key from the Cloudsmith web app: click your user icon in the top right corner, then click **Personal API Keys**. See [API Key](https://docs.cloudsmith.com/accounts-and-teams/api-key) for more information. If you authenticated with `cloudsmith auth`, you can also use `cloudsmith tokens create` to create an API key.
|
|
367
368
|
|
|
368
|
-
|
|
369
|
-
cloudsmith login
|
|
370
|
-
Login: you@example.com
|
|
371
|
-
Password:
|
|
372
|
-
Repeat for confirmation:
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
*Note:* Please ensure you use your email for the 'Login' prompt and not your user slug/identifier.
|
|
376
|
-
|
|
377
|
-
The resulting output looks something like:
|
|
378
|
-
|
|
379
|
-
```
|
|
380
|
-
Retrieving API token for 'you@example.com' ... OK
|
|
381
|
-
Your API token is: 1234567890abcdef1234567890abcdef
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
Once you have your API key you can then put this into your `credentials.ini`, use it as an environment variable `export CLOUDSMITH_API_KEY=your_key_here` or pass it to the CLI using the `-k your_key_here` flag.
|
|
369
|
+
Give your API key to the CLI in one of these ways:
|
|
385
370
|
|
|
386
|
-
|
|
371
|
+
- Set the `CLOUDSMITH_API_KEY` environment variable: `export CLOUDSMITH_API_KEY=your_key_here`.
|
|
372
|
+
- Put the key into your `credentials.ini` file.
|
|
373
|
+
- Pass the key to the CLI with the `-k your_key_here` flag.
|
|
387
374
|
|
|
388
|
-
|
|
375
|
+
*Note:* The `cloudsmith login` command and its `cloudsmith token` alias are deprecated. They retrieve your API key with your email address and password, and they will stop working on November 20, 2026, when Cloudsmith disables username and password login. See the [Cloudsmith changelog](https://cloudsmith.com/changelog). After that, both commands fail with a message that explains how to get an API key instead.
|
|
389
376
|
|
|
390
377
|
|
|
391
378
|
## Uploading Packages
|
|
@@ -6,6 +6,8 @@ from collections import OrderedDict
|
|
|
6
6
|
import click.exceptions
|
|
7
7
|
from click_didyoumean import DYMGroup
|
|
8
8
|
|
|
9
|
+
from ..core.certificate_errors import describe_certificate_error
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
def _is_json_output_requested(exception):
|
|
11
13
|
"""Determine if JSON output was requested, checking context and argv."""
|
|
@@ -35,6 +37,19 @@ def _is_json_output_requested(exception):
|
|
|
35
37
|
return False
|
|
36
38
|
|
|
37
39
|
|
|
40
|
+
def _is_debug_requested(args=None):
|
|
41
|
+
"""Determine if --debug was passed (the click context is gone by now).
|
|
42
|
+
|
|
43
|
+
``args`` are the arguments given to ``main``; click falls back to
|
|
44
|
+
``sys.argv`` when they are ``None``, so we do the same.
|
|
45
|
+
"""
|
|
46
|
+
import sys
|
|
47
|
+
|
|
48
|
+
if args is None:
|
|
49
|
+
args = sys.argv[1:]
|
|
50
|
+
return any(arg in ("-d", "--debug") for arg in args)
|
|
51
|
+
|
|
52
|
+
|
|
38
53
|
def _format_click_exception_as_json(exception):
|
|
39
54
|
"""Format a ClickException as a JSON error dict."""
|
|
40
55
|
return {
|
|
@@ -45,11 +60,19 @@ def _format_click_exception_as_json(exception):
|
|
|
45
60
|
},
|
|
46
61
|
"help": {
|
|
47
62
|
"context": "Invalid usage",
|
|
48
|
-
"hint":
|
|
63
|
+
"hint": describe_certificate_error(exception)
|
|
64
|
+
or "Check your command arguments/flags.",
|
|
49
65
|
},
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
|
|
69
|
+
def _echo_certificate_hint(exception):
|
|
70
|
+
"""Explain a TLS certificate failure behind ``exception`` on stderr."""
|
|
71
|
+
hint = describe_certificate_error(exception)
|
|
72
|
+
if hint:
|
|
73
|
+
click.echo(f"Hint: {click.style(hint, fg='yellow')}", err=True)
|
|
74
|
+
|
|
75
|
+
|
|
53
76
|
class AliasGroup(DYMGroup):
|
|
54
77
|
"""A command group with DYM, alias and lazy-subcommand support.
|
|
55
78
|
|
|
@@ -177,4 +200,23 @@ class AliasGroup(DYMGroup):
|
|
|
177
200
|
if not original_standalone_mode:
|
|
178
201
|
raise
|
|
179
202
|
e.show()
|
|
203
|
+
_echo_certificate_hint(e)
|
|
180
204
|
sys.exit(e.exit_code)
|
|
205
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
206
|
+
# Anything reaching here is unexpected: click's own errors and
|
|
207
|
+
# handled API errors (which exit via ctx.exit) are dealt with
|
|
208
|
+
# above or never get this far.
|
|
209
|
+
if not original_standalone_mode:
|
|
210
|
+
raise
|
|
211
|
+
main_args = kwargs.get("args", args[0] if args else None)
|
|
212
|
+
if _is_debug_requested(main_args):
|
|
213
|
+
import traceback
|
|
214
|
+
|
|
215
|
+
click.echo(traceback.format_exc(), err=True, nl=False)
|
|
216
|
+
from ..core import telemetry
|
|
217
|
+
|
|
218
|
+
# Reporting is silent: the user sees the same line either way.
|
|
219
|
+
telemetry.report_exception(e)
|
|
220
|
+
click.echo(f"Error: {telemetry.format_exception_summary(e)}", err=True)
|
|
221
|
+
_echo_certificate_hint(e)
|
|
222
|
+
sys.exit(1)
|
|
@@ -12,7 +12,7 @@ from ...core.sso import (
|
|
|
12
12
|
)
|
|
13
13
|
from .. import decorators, utils, validators
|
|
14
14
|
from ..exceptions import handle_api_exceptions
|
|
15
|
-
from ..saml import
|
|
15
|
+
from ..saml import get_idp_url
|
|
16
16
|
from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
|
|
17
17
|
from .main import main
|
|
18
18
|
from .tokens import create, request_api_key
|
|
@@ -53,7 +53,7 @@ def _create_auth_server(opts, owner, session, enable_token_creation, profile):
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def _renew_existing_sso_session(opts, profile):
|
|
56
|
-
"""Return a usable existing
|
|
56
|
+
"""Return a usable existing SAML session, or fall back to browser auth."""
|
|
57
57
|
renewal = renew_sso_session(opts.api_config.host, opts.session, profile=profile)
|
|
58
58
|
if renewal.status in (
|
|
59
59
|
SsoRenewalStatus.RENEWED,
|
|
@@ -64,14 +64,15 @@ def _renew_existing_sso_session(opts, profile):
|
|
|
64
64
|
renewal.error, requests.RequestException
|
|
65
65
|
):
|
|
66
66
|
raise click.ClickException(
|
|
67
|
-
"The
|
|
67
|
+
"The SAML session has expired and Cloudsmith could not be reached: "
|
|
68
|
+
f"{renewal.error}\n"
|
|
68
69
|
"Check your connection, then run 'cloudsmith auth' again."
|
|
69
70
|
) from renewal.error
|
|
70
71
|
return None
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
def _report_active_sso_session(opts, renewal, use_stderr=False):
|
|
74
|
-
"""Report the usable
|
|
75
|
+
"""Report the usable SAML session without exposing its access token."""
|
|
75
76
|
expires_at = get_access_token_expiry(renewal.access_token)
|
|
76
77
|
data = {
|
|
77
78
|
"authenticated": True,
|
|
@@ -84,16 +85,16 @@ def _report_active_sso_session(opts, renewal, use_stderr=False):
|
|
|
84
85
|
return
|
|
85
86
|
|
|
86
87
|
if renewal.status == SsoRenewalStatus.RENEWED:
|
|
87
|
-
click.secho("
|
|
88
|
+
click.secho("SAML session renewed.", fg="green", err=use_stderr)
|
|
88
89
|
elif renewal.error:
|
|
89
90
|
click.secho(
|
|
90
|
-
"The
|
|
91
|
+
"The SAML session could not be renewed; the existing access token "
|
|
91
92
|
"remains active.",
|
|
92
93
|
fg="yellow",
|
|
93
94
|
err=use_stderr,
|
|
94
95
|
)
|
|
95
96
|
else:
|
|
96
|
-
click.secho("
|
|
97
|
+
click.secho("SAML session is active.", fg="green", err=use_stderr)
|
|
97
98
|
if expires_at:
|
|
98
99
|
click.echo(
|
|
99
100
|
f"Access token expires at {utils.fmt_datetime(expires_at)}.",
|
|
@@ -110,7 +111,7 @@ def _perform_saml_authentication(
|
|
|
110
111
|
profile=None,
|
|
111
112
|
):
|
|
112
113
|
"""Perform SAML authentication via web browser and local web server."""
|
|
113
|
-
session =
|
|
114
|
+
session = opts.session
|
|
114
115
|
api_host = opts.api_config.host
|
|
115
116
|
|
|
116
117
|
auth_server, port = _create_auth_server(
|
|
@@ -157,10 +158,28 @@ def _perform_saml_authentication(
|
|
|
157
158
|
)
|
|
158
159
|
|
|
159
160
|
auth_server.handle_request()
|
|
161
|
+
return auth_server.sso_access_token is not None
|
|
160
162
|
finally:
|
|
161
163
|
auth_server.server_close()
|
|
162
164
|
|
|
163
165
|
|
|
166
|
+
def _save_workspace(ctx, opts, workspace, use_stderr):
|
|
167
|
+
"""Save the Workspace to config.ini, or warn when the CLI cannot write it."""
|
|
168
|
+
try:
|
|
169
|
+
saved_path = opts.get_config_reader().save_workspace(
|
|
170
|
+
workspace, profile=ctx.meta.get("profile"), path=ctx.meta.get("config_file")
|
|
171
|
+
)
|
|
172
|
+
except (OSError, ValueError) as exc:
|
|
173
|
+
click.secho(
|
|
174
|
+
f"Could not save the {workspace} Workspace to config.ini: {exc}",
|
|
175
|
+
fg="yellow",
|
|
176
|
+
err=True,
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
if saved_path:
|
|
180
|
+
click.echo(f"Saved the {workspace} Workspace to {saved_path}.", err=use_stderr)
|
|
181
|
+
|
|
182
|
+
|
|
164
183
|
@main.command(aliases=["auth"])
|
|
165
184
|
@click.option(
|
|
166
185
|
"-t",
|
|
@@ -270,12 +289,12 @@ def authenticate(
|
|
|
270
289
|
err=use_stderr,
|
|
271
290
|
)
|
|
272
291
|
|
|
273
|
-
# Determine if we need to refresh API after
|
|
292
|
+
# Determine if we need to refresh API after SAML (required for token operations)
|
|
274
293
|
enable_token_creation = token or request_api_key_flag
|
|
275
294
|
|
|
276
|
-
context_message = "Failed to authenticate via
|
|
295
|
+
context_message = "Failed to authenticate via SAML!"
|
|
277
296
|
with handle_api_exceptions(ctx, opts=opts, context_msg=context_message):
|
|
278
|
-
_perform_saml_authentication(
|
|
297
|
+
authenticated = _perform_saml_authentication(
|
|
279
298
|
opts,
|
|
280
299
|
workspace,
|
|
281
300
|
enable_token_creation=enable_token_creation,
|
|
@@ -284,6 +303,9 @@ def authenticate(
|
|
|
284
303
|
profile=ctx.meta.get("profile"),
|
|
285
304
|
)
|
|
286
305
|
|
|
306
|
+
if authenticated:
|
|
307
|
+
_save_workspace(ctx, opts, workspace, use_stderr)
|
|
308
|
+
|
|
287
309
|
if request_api_key_flag:
|
|
288
310
|
# Non-interactive token retrieval
|
|
289
311
|
new_token = request_api_key(ctx, opts, save_config=save_config)
|
|
@@ -9,6 +9,7 @@ Provides ``credential-helper install``, ``credential-helper uninstall``, and
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import sys
|
|
12
|
+
from urllib.parse import urlsplit
|
|
12
13
|
|
|
13
14
|
import click
|
|
14
15
|
|
|
@@ -78,10 +79,14 @@ def _get_installer(name: str):
|
|
|
78
79
|
def _terraform_helper_args(ctx, opts, repo: str | None = None) -> tuple[str, ...]:
|
|
79
80
|
"""Build the terraformrc ``args`` list from the resolved config.
|
|
80
81
|
|
|
81
|
-
Baking
|
|
82
|
+
Baking ``-w``/``-r``/``-P`` into the block means ``terraform init`` works
|
|
82
83
|
with no environment variables and no hand-edited config. Only values
|
|
83
|
-
actually supplied are written, so an install without
|
|
84
|
-
``-P`` leaves ``args = []``.
|
|
84
|
+
actually supplied are written, so an install without ``-w``/``--repo``/
|
|
85
|
+
``-P`` leaves ``args = []``. The API host and proxy are baked in too, so
|
|
86
|
+
the helper looks up domains on the same API host, through the same proxy,
|
|
87
|
+
as the install. A proxy URL with credentials is never baked in: Terraform
|
|
88
|
+
passes ``args`` on the helper's command line, where any local user can read
|
|
89
|
+
them.
|
|
85
90
|
|
|
86
91
|
A non-default ``--config-file``/``--credentials-file`` is also baked in: the
|
|
87
92
|
helper is later invoked by ``terraform init`` in a fresh process with none
|
|
@@ -91,7 +96,7 @@ def _terraform_helper_args(ctx, opts, repo: str | None = None) -> tuple[str, ...
|
|
|
91
96
|
"""
|
|
92
97
|
args: list[str] = []
|
|
93
98
|
if opts.org:
|
|
94
|
-
args.extend(["
|
|
99
|
+
args.extend(["-w", opts.org])
|
|
95
100
|
if repo:
|
|
96
101
|
args.extend(["-r", repo])
|
|
97
102
|
profile = ctx.meta.get("profile")
|
|
@@ -103,9 +108,32 @@ def _terraform_helper_args(ctx, opts, repo: str | None = None) -> tuple[str, ...
|
|
|
103
108
|
creds_file = ctx.meta.get("creds_file")
|
|
104
109
|
if creds_file:
|
|
105
110
|
args.extend(["--credentials-file", creds_file])
|
|
111
|
+
if opts.api_host:
|
|
112
|
+
args.extend(["--api-host", opts.api_host])
|
|
113
|
+
if opts.proxy and not _proxy_has_credentials(opts.proxy):
|
|
114
|
+
args.extend(["--proxy", opts.proxy])
|
|
106
115
|
return tuple(args)
|
|
107
116
|
|
|
108
117
|
|
|
118
|
+
def _proxy_has_credentials(proxy: str) -> bool:
|
|
119
|
+
"""Return True if the proxy URL carries a username or password."""
|
|
120
|
+
parsed = urlsplit(proxy if "://" in proxy else f"//{proxy}")
|
|
121
|
+
return parsed.username is not None or parsed.password is not None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _terraform_proxy_next_steps(opts) -> list[str]:
|
|
125
|
+
"""Return the post-install guidance when the proxy was not baked in."""
|
|
126
|
+
if not opts.proxy or not _proxy_has_credentials(opts.proxy):
|
|
127
|
+
return []
|
|
128
|
+
return [
|
|
129
|
+
(
|
|
130
|
+
"Next steps: the proxy URL contains credentials, so it was not"
|
|
131
|
+
" written to the Terraform config. export CLOUDSMITH_PROXY=<proxy-url>"
|
|
132
|
+
" in the environment that runs `terraform init`."
|
|
133
|
+
),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
|
|
109
137
|
_REPO_FLAGS = ("-r", "--repo", "--repository")
|
|
110
138
|
|
|
111
139
|
|
|
@@ -141,6 +169,31 @@ def _terraform_next_steps(helper_args: tuple[str, ...]) -> list[str]:
|
|
|
141
169
|
]
|
|
142
170
|
|
|
143
171
|
|
|
172
|
+
def _terraform_workspace_next_steps(helper_args: tuple[str, ...]) -> list[str]:
|
|
173
|
+
"""Return the post-install guidance when no Workspace is baked in.
|
|
174
|
+
|
|
175
|
+
The helper needs a Workspace to match custom domains and to scope the
|
|
176
|
+
token for a Cloudsmith-owned host.
|
|
177
|
+
"""
|
|
178
|
+
if "-w" in helper_args:
|
|
179
|
+
return []
|
|
180
|
+
rc_path = _terraformrc_path()
|
|
181
|
+
return [
|
|
182
|
+
(
|
|
183
|
+
"Next steps: the Terraform helper needs a Workspace to match custom"
|
|
184
|
+
" domains and to scope tokens. Provide it in one of these ways:"
|
|
185
|
+
),
|
|
186
|
+
(
|
|
187
|
+
" - export CLOUDSMITH_WORKSPACE=<your-workspace> in the environment"
|
|
188
|
+
" that runs `terraform init`, or"
|
|
189
|
+
),
|
|
190
|
+
(
|
|
191
|
+
f" - add it to the args in {rc_path}, e.g."
|
|
192
|
+
' args = ["-w", "<your-workspace>", ...].'
|
|
193
|
+
),
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
|
|
144
197
|
# ---------------------------------------------------------------------------
|
|
145
198
|
# install
|
|
146
199
|
# ---------------------------------------------------------------------------
|
|
@@ -149,7 +202,10 @@ def _terraform_next_steps(helper_args: tuple[str, ...]) -> list[str]:
|
|
|
149
202
|
@click.command("install")
|
|
150
203
|
@click.argument("helper")
|
|
151
204
|
@click.option(
|
|
152
|
-
"--bin-dir",
|
|
205
|
+
"--bin-dir",
|
|
206
|
+
default=None,
|
|
207
|
+
help="Directory to install the launcher binary. "
|
|
208
|
+
"[default: ~/.cloudsmith/bin, or Terraform's plugin directory]",
|
|
153
209
|
)
|
|
154
210
|
@click.option(
|
|
155
211
|
"--domain",
|
|
@@ -231,7 +287,7 @@ def install_cmd(
|
|
|
231
287
|
\b
|
|
232
288
|
# Terraform: bake the repository into the terraformrc args so
|
|
233
289
|
# `terraform init` needs no CLOUDSMITH_REPO env var
|
|
234
|
-
$ cloudsmith credential-helper install terraform
|
|
290
|
+
$ cloudsmith credential-helper install terraform -w acme --repo my-repo
|
|
235
291
|
"""
|
|
236
292
|
installer = _get_installer(helper)
|
|
237
293
|
|
|
@@ -278,7 +334,11 @@ def install_cmd(
|
|
|
278
334
|
|
|
279
335
|
next_steps: list[str] = []
|
|
280
336
|
if helper == "terraform":
|
|
281
|
-
next_steps =
|
|
337
|
+
next_steps = (
|
|
338
|
+
_terraform_workspace_next_steps(helper_args)
|
|
339
|
+
+ _terraform_next_steps(helper_args)
|
|
340
|
+
+ _terraform_proxy_next_steps(opts)
|
|
341
|
+
)
|
|
282
342
|
|
|
283
343
|
data = {
|
|
284
344
|
"helper": helper,
|
|
@@ -291,7 +351,7 @@ def install_cmd(
|
|
|
291
351
|
sys.exit(ec)
|
|
292
352
|
|
|
293
353
|
if dry_run:
|
|
294
|
-
click.echo("Dry run
|
|
354
|
+
click.echo("Dry run - no changes will be made:", err=use_stderr)
|
|
295
355
|
for action in normal:
|
|
296
356
|
click.echo(f" {action}" if dry_run else action, err=use_stderr)
|
|
297
357
|
for warning in warnings:
|
|
@@ -309,7 +369,10 @@ def install_cmd(
|
|
|
309
369
|
@click.command("uninstall")
|
|
310
370
|
@click.argument("helper")
|
|
311
371
|
@click.option(
|
|
312
|
-
"--bin-dir",
|
|
372
|
+
"--bin-dir",
|
|
373
|
+
default=None,
|
|
374
|
+
help="Directory where the launcher binary was installed. "
|
|
375
|
+
"[default: ~/.cloudsmith/bin, or Terraform's plugin directory]",
|
|
313
376
|
)
|
|
314
377
|
@click.option(
|
|
315
378
|
"--dry-run",
|
|
@@ -356,7 +419,7 @@ def uninstall_cmd(ctx, opts, helper: str, bin_dir: str | None, dry_run: bool) ->
|
|
|
356
419
|
return
|
|
357
420
|
|
|
358
421
|
if dry_run:
|
|
359
|
-
click.echo("Dry run
|
|
422
|
+
click.echo("Dry run - no changes will be made:", err=use_stderr)
|
|
360
423
|
for action in normal:
|
|
361
424
|
click.echo(f" {action}" if dry_run else action, err=use_stderr)
|
|
362
425
|
for warning in warnings:
|
|
@@ -46,8 +46,9 @@ def terraform(opts, repo, params):
|
|
|
46
46
|
|
|
47
47
|
Provides credentials for all Cloudsmith Terraform registries:
|
|
48
48
|
``*.cloudsmith.io``, ``*.cloudsmith.com``, and any custom domains
|
|
49
|
-
configured for the
|
|
50
|
-
|
|
49
|
+
configured for the Workspace (requires a Workspace - ``-w/--workspace``,
|
|
50
|
+
CLOUDSMITH_WORKSPACE or ``workspace`` in ``config.ini`` - and a valid API
|
|
51
|
+
key/token).
|
|
51
52
|
|
|
52
53
|
Accepts Terraform's calling convention — an optional verb (``get``,
|
|
53
54
|
``store``, ``forget``) followed by the hostname — so the on-PATH launcher
|
|
@@ -76,18 +77,18 @@ def terraform(opts, repo, params):
|
|
|
76
77
|
0: Token returned, or the host is not a Cloudsmith registry
|
|
77
78
|
1: Cloudsmith host with no credentials available, or an error occurred
|
|
78
79
|
|
|
79
|
-
The token is scoped to the repository. On a
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
The token is scoped to the repository. On a Cloudsmith-owned host it is
|
|
81
|
+
``{workspace}/{repo}/{token}`` and the Workspace is required
|
|
82
|
+
(``-w/--workspace``, ``CLOUDSMITH_WORKSPACE`` or ``workspace`` in
|
|
83
|
+
``config.ini``); a Cloudsmith-owned host requested without a Workspace is a
|
|
83
84
|
non-zero exit. On a custom domain — which is already bound to a single
|
|
84
|
-
|
|
85
|
+
Workspace — the Workspace is omitted and the token is ``{repo}/{token}``. The
|
|
85
86
|
repository is always required (``-r/--repo/--repository`` or
|
|
86
87
|
``CLOUDSMITH_REPO``): Terraform does not tell a credentials helper which
|
|
87
88
|
repository is being requested. A non-Cloudsmith host still returns ``{}``
|
|
88
89
|
and exit 0. The profile can also be supplied with ``-P/--profile``. The
|
|
89
90
|
launcher forwards Terraform's ``args`` verbatim, so a terraformrc block such
|
|
90
|
-
as ``credentials_helper "cloudsmith" { args = ["
|
|
91
|
+
as ``credentials_helper "cloudsmith" { args = ["-w", "acme",
|
|
91
92
|
"--repo=my-repo", "-P", "ci"] }`` reaches this command as those options.
|
|
92
93
|
|
|
93
94
|
\b
|
|
@@ -101,13 +102,13 @@ def terraform(opts, repo, params):
|
|
|
101
102
|
# Terraform's calling convention (verb + hostname)
|
|
102
103
|
$ cloudsmith credential-helper terraform --repo my-repo get terraform.cloudsmith.io
|
|
103
104
|
\b
|
|
104
|
-
# Select
|
|
105
|
-
$ cloudsmith credential-helper terraform
|
|
105
|
+
# Select a Workspace and profile explicitly (no env vars needed)
|
|
106
|
+
$ cloudsmith credential-helper terraform -w acme -P ci get terraform.cloudsmith.io
|
|
106
107
|
|
|
107
108
|
\b
|
|
108
109
|
Environment variables:
|
|
109
110
|
CLOUDSMITH_API_KEY: API key for authentication (optional)
|
|
110
|
-
|
|
111
|
+
CLOUDSMITH_WORKSPACE: Workspace slug (required to scope the token)
|
|
111
112
|
CLOUDSMITH_REPO: Repository slug the registry serves (required)
|
|
112
113
|
CLOUDSMITH_PROFILE: Configuration profile to load (optional)
|
|
113
114
|
"""
|