cloudsmith-cli 1.26.0__tar.gz → 1.28.0__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.26.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.28.0}/PKG-INFO +10 -6
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/README.md +2 -2
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/auth.py +129 -34
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +8 -4
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +8 -1
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +127 -10
- cloudsmith_cli-1.28.0/cloudsmith_cli/cli/commands/credential_helper/terraform.py +153 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/domains.py +1 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/entitlements.py +9 -8
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/list_.py +1 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/main.py +11 -1
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/mcp.py +7 -5
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metadata.py +1 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/registry.py +2 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/cli/commands/update.py +170 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/config.py +13 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/decorators.py +67 -11
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/saml.py +2 -2
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/utils.py +8 -3
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/webserver.py +13 -10
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/models.py +6 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/core/credentials/providers/keyring_provider.py +95 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +81 -5
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/download.py +2 -1
- cloudsmith_cli-1.28.0/cloudsmith_cli/core/installation.py +156 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/keyring.py +76 -13
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/server.py +108 -15
- cloudsmith_cli-1.28.0/cloudsmith_cli/core/self_update.py +429 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/core/sso.py +224 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/core/update_check.py +474 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/utils.py +65 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/common.py +23 -3
- cloudsmith_cli-1.28.0/cloudsmith_cli/credential_helpers/terraform/__init__.py +4 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/credential_helpers/terraform/installer.py +496 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/credential_helpers/terraform/runtime.py +204 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/credential_helpers/terraform/terraformrc.py +165 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/data/VERSION +1 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/data/config.ini +3 -0
- cloudsmith_cli-1.28.0/cloudsmith_cli/wrapper.py +48 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0/cloudsmith_cli.egg-info}/PKG-INFO +10 -6
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/SOURCES.txt +10 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/entry_points.txt +1 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/requires.txt +7 -3
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/pyproject.toml +23 -13
- cloudsmith_cli-1.26.0/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -105
- cloudsmith_cli-1.26.0/cloudsmith_cli/core/sso.py +0 -49
- cloudsmith_cli-1.26.0/cloudsmith_cli/data/VERSION +0 -1
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/LICENSE +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/MANIFEST.in +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/__main__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/command.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/check.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/cargo.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/generic.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/download.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/login.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/logout.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/move.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/push.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/status.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/whoami.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/dsc_parser.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/exceptions.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/table.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/types.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/validators.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/conftest.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/distros.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/files.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/init.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/metadata.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/metrics.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/orgs.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/packages.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/quota.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/rates.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/repos.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/status.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/user.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/version.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/vulnerabilities.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/cache_utils.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/config.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/macos_keychain.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/data.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/pagination.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/ratelimits.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/rest.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/session.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/version.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/installer.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/runtime.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/custom_domains.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/default_domains.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/installer.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/generic.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/launchers.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/installer.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/rc.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/runtime.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/data/credentials.ini +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/__init__.py +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_error.html +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_success.html +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_two_factor.html +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
- {cloudsmith_cli-1.26.0 → cloudsmith_cli-1.28.0}/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.28.0
|
|
4
4
|
Summary: Cloudsmith Command-Line Interface (CLI)
|
|
5
5
|
Author-email: Cloudsmith Ltd <support@cloudsmith.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -23,6 +23,7 @@ Classifier: Topic :: Utilities
|
|
|
23
23
|
Requires-Python: >=3.10.0
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
+
Requires-Dist: certifi>=2024.7.4
|
|
26
27
|
Requires-Dist: click!=8.3.0,>=8.2
|
|
27
28
|
Requires-Dist: click-configfile>=0.2.3
|
|
28
29
|
Requires-Dist: click-didyoumean>=0.0.3
|
|
@@ -32,13 +33,16 @@ Requires-Dist: cloudsmith-api<3.0,>=2.0.31
|
|
|
32
33
|
Requires-Dist: keyring>=25.4.1
|
|
33
34
|
Requires-Dist: keyrings-alt>=5.0.2
|
|
34
35
|
Requires-Dist: keyrings-cryptfile>=1.3.9
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
36
|
+
Requires-Dist: cryptography>=50.0.0
|
|
37
|
+
Requires-Dist: mcp==2.2.0
|
|
38
|
+
Requires-Dist: pydantic-settings>=2.14.2
|
|
39
|
+
Requires-Dist: PyJWT[crypto]>=2.13.0
|
|
40
|
+
Requires-Dist: python-toon==0.1.3
|
|
38
41
|
Requires-Dist: requests>=2.18.4
|
|
39
42
|
Requires-Dist: requests_toolbelt>=1.0.0
|
|
40
43
|
Requires-Dist: rich>=13.0.0
|
|
41
44
|
Requires-Dist: semver>=2.7.9
|
|
45
|
+
Requires-Dist: starlette>=1.3.1
|
|
42
46
|
Requires-Dist: urllib3>=2.5
|
|
43
47
|
Requires-Dist: tomlkit>=0.15.0
|
|
44
48
|
Provides-Extra: aws
|
|
@@ -370,7 +374,7 @@ cloudsmith auth --owner example
|
|
|
370
374
|
Beginning authentication for the example org ...
|
|
371
375
|
Your organization's SAML IDP URL is: https://example.com/some-saml-idp
|
|
372
376
|
|
|
373
|
-
|
|
377
|
+
Waiting for the authentication callback on port 12400 ...
|
|
374
378
|
|
|
375
379
|
Authentication complete
|
|
376
380
|
```
|
|
@@ -381,7 +385,7 @@ The CLI opens the IDP URL in your browser automatically. If it can't (for exampl
|
|
|
381
385
|
cloudsmith auth --owner example --no-browser
|
|
382
386
|
```
|
|
383
387
|
|
|
384
|
-
*Note:* The authentication callback is served on `127.0.0.1
|
|
388
|
+
*Note:* The authentication callback is served on `127.0.0.1`, on the first free port between `12400` and `12404`. The browser you open the URL in must run on the same machine as the CLI.
|
|
385
389
|
|
|
386
390
|
#### Getting Your API Key
|
|
387
391
|
|
|
@@ -321,7 +321,7 @@ cloudsmith auth --owner example
|
|
|
321
321
|
Beginning authentication for the example org ...
|
|
322
322
|
Your organization's SAML IDP URL is: https://example.com/some-saml-idp
|
|
323
323
|
|
|
324
|
-
|
|
324
|
+
Waiting for the authentication callback on port 12400 ...
|
|
325
325
|
|
|
326
326
|
Authentication complete
|
|
327
327
|
```
|
|
@@ -332,7 +332,7 @@ The CLI opens the IDP URL in your browser automatically. If it can't (for exampl
|
|
|
332
332
|
cloudsmith auth --owner example --no-browser
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
*Note:* The authentication callback is served on `127.0.0.1
|
|
335
|
+
*Note:* The authentication callback is served on `127.0.0.1`, on the first free port between `12400` and `12404`. The browser you open the URL in must run on the same machine as the CLI.
|
|
336
336
|
|
|
337
337
|
#### Getting Your API Key
|
|
338
338
|
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
import webbrowser
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
|
+
import requests
|
|
6
7
|
|
|
8
|
+
from ...core.sso import (
|
|
9
|
+
SsoRenewalStatus,
|
|
10
|
+
get_access_token_expiry,
|
|
11
|
+
renew_sso_session,
|
|
12
|
+
)
|
|
7
13
|
from .. import decorators, utils, validators
|
|
8
14
|
from ..exceptions import handle_api_exceptions
|
|
9
15
|
from ..saml import create_configured_session, get_idp_url
|
|
@@ -13,7 +19,86 @@ from .tokens import create, request_api_key
|
|
|
13
19
|
|
|
14
20
|
# Authentication server configuration
|
|
15
21
|
AUTH_SERVER_HOST = "127.0.0.1"
|
|
16
|
-
|
|
22
|
+
AUTH_REDIRECT_HOST = "localhost"
|
|
23
|
+
AUTH_SERVER_PORTS = (12400, 12401, 12402, 12403, 12404)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _create_auth_server(opts, owner, session, enable_token_creation, profile):
|
|
27
|
+
"""Create an authentication server on the first available callback port."""
|
|
28
|
+
last_error = None
|
|
29
|
+
|
|
30
|
+
for port in AUTH_SERVER_PORTS:
|
|
31
|
+
try:
|
|
32
|
+
server = AuthenticationWebServer(
|
|
33
|
+
(AUTH_SERVER_HOST, port),
|
|
34
|
+
AuthenticationWebRequestHandler,
|
|
35
|
+
owner=owner,
|
|
36
|
+
session=session,
|
|
37
|
+
debug=opts.debug,
|
|
38
|
+
refresh_api_on_success=enable_token_creation,
|
|
39
|
+
api_opts=opts.api_config,
|
|
40
|
+
profile=profile,
|
|
41
|
+
)
|
|
42
|
+
return server, port
|
|
43
|
+
except OSError as exc:
|
|
44
|
+
last_error = exc
|
|
45
|
+
|
|
46
|
+
ports = ", ".join(str(port) for port in AUTH_SERVER_PORTS)
|
|
47
|
+
raise click.ClickException(
|
|
48
|
+
"Could not start the authentication callback server. "
|
|
49
|
+
f"Every candidate port is unavailable: {ports}. "
|
|
50
|
+
f"The last error was: {last_error}. "
|
|
51
|
+
"Stop the process that holds one of these ports, then try again."
|
|
52
|
+
) from last_error
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _renew_existing_sso_session(opts, profile):
|
|
56
|
+
"""Return a usable existing SSO session, or fall back to browser auth."""
|
|
57
|
+
renewal = renew_sso_session(opts.api_config.host, opts.session, profile=profile)
|
|
58
|
+
if renewal.status in (
|
|
59
|
+
SsoRenewalStatus.RENEWED,
|
|
60
|
+
SsoRenewalStatus.CURRENT,
|
|
61
|
+
):
|
|
62
|
+
return renewal
|
|
63
|
+
if renewal.status == SsoRenewalStatus.FAILED and isinstance(
|
|
64
|
+
renewal.error, requests.RequestException
|
|
65
|
+
):
|
|
66
|
+
raise click.ClickException(
|
|
67
|
+
"The SSO session has expired and Cloudsmith could not be reached. "
|
|
68
|
+
"Check your connection, then run 'cloudsmith auth' again."
|
|
69
|
+
) from renewal.error
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _report_active_sso_session(opts, renewal, use_stderr=False):
|
|
74
|
+
"""Report the usable SSO session without exposing its access token."""
|
|
75
|
+
expires_at = get_access_token_expiry(renewal.access_token)
|
|
76
|
+
data = {
|
|
77
|
+
"authenticated": True,
|
|
78
|
+
"method": "sso",
|
|
79
|
+
"status": renewal.status.value,
|
|
80
|
+
"expires_at": expires_at,
|
|
81
|
+
"renewal_error": str(renewal.error) if renewal.error else None,
|
|
82
|
+
}
|
|
83
|
+
if utils.maybe_print_as_json(opts, data):
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if renewal.status == SsoRenewalStatus.RENEWED:
|
|
87
|
+
click.secho("SSO session renewed.", fg="green", err=use_stderr)
|
|
88
|
+
elif renewal.error:
|
|
89
|
+
click.secho(
|
|
90
|
+
"The SSO session could not be renewed; the existing access token "
|
|
91
|
+
"remains active.",
|
|
92
|
+
fg="yellow",
|
|
93
|
+
err=use_stderr,
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
click.secho("SSO session is active.", fg="green", err=use_stderr)
|
|
97
|
+
if expires_at:
|
|
98
|
+
click.echo(
|
|
99
|
+
f"Access token expires at {utils.fmt_datetime(expires_at)}.",
|
|
100
|
+
err=use_stderr,
|
|
101
|
+
)
|
|
17
102
|
|
|
18
103
|
|
|
19
104
|
def _perform_saml_authentication(
|
|
@@ -28,50 +113,52 @@ def _perform_saml_authentication(
|
|
|
28
113
|
session = create_configured_session(opts)
|
|
29
114
|
api_host = opts.api_config.host
|
|
30
115
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
click.echo(
|
|
34
|
-
f"Your Workspace's SAML IDP URL is: {click.style(idp_url, bold=True)}",
|
|
35
|
-
err=use_stderr,
|
|
116
|
+
auth_server, port = _create_auth_server(
|
|
117
|
+
opts, owner, session, enable_token_creation, profile
|
|
36
118
|
)
|
|
37
|
-
|
|
119
|
+
redirect_url = f"http://{AUTH_REDIRECT_HOST}:{port}"
|
|
38
120
|
|
|
39
|
-
|
|
121
|
+
try:
|
|
40
122
|
click.echo(
|
|
41
|
-
"
|
|
42
|
-
"Please open the URL above manually to continue.",
|
|
123
|
+
f"Waiting for the authentication callback on port {port} ... ",
|
|
43
124
|
err=use_stderr,
|
|
44
125
|
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
126
|
+
|
|
127
|
+
idp_url = get_idp_url(
|
|
128
|
+
api_host, owner, redirect_url=redirect_url, session=session
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
click.echo(
|
|
132
|
+
f"Your Workspace's SAML IDP URL is: {click.style(idp_url, bold=True)}",
|
|
133
|
+
err=use_stderr,
|
|
134
|
+
)
|
|
135
|
+
click.echo(err=use_stderr)
|
|
136
|
+
|
|
137
|
+
if no_browser:
|
|
55
138
|
click.echo(
|
|
56
|
-
"
|
|
139
|
+
"Skipping automatic browser launch (--no-browser). "
|
|
57
140
|
"Please open the URL above manually to continue.",
|
|
58
141
|
err=use_stderr,
|
|
59
142
|
)
|
|
143
|
+
else:
|
|
144
|
+
try:
|
|
145
|
+
browser_opened = webbrowser.open(idp_url)
|
|
146
|
+
except Exception:
|
|
147
|
+
# Browser launch failures vary by platform (webbrowser.Error on
|
|
148
|
+
# Cygwin/headless, anything else elsewhere), so catch broadly and
|
|
149
|
+
# fall back to the manual URL rather than crashing.
|
|
150
|
+
browser_opened = False
|
|
60
151
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
session=session,
|
|
68
|
-
debug=opts.debug,
|
|
69
|
-
refresh_api_on_success=enable_token_creation,
|
|
70
|
-
api_opts=opts.api_config,
|
|
71
|
-
profile=profile,
|
|
72
|
-
)
|
|
152
|
+
if not browser_opened:
|
|
153
|
+
click.echo(
|
|
154
|
+
"Couldn't open a browser automatically. "
|
|
155
|
+
"Please open the URL above manually to continue.",
|
|
156
|
+
err=use_stderr,
|
|
157
|
+
)
|
|
73
158
|
|
|
74
|
-
|
|
159
|
+
auth_server.handle_request()
|
|
160
|
+
finally:
|
|
161
|
+
auth_server.server_close()
|
|
75
162
|
|
|
76
163
|
|
|
77
164
|
@main.command(aliases=["auth"])
|
|
@@ -165,6 +252,14 @@ def authenticate(
|
|
|
165
252
|
err=True,
|
|
166
253
|
)
|
|
167
254
|
|
|
255
|
+
if not force and not token and not request_api_key_flag:
|
|
256
|
+
session_result = _renew_existing_sso_session(
|
|
257
|
+
opts, profile=ctx.meta.get("profile")
|
|
258
|
+
)
|
|
259
|
+
if session_result:
|
|
260
|
+
_report_active_sso_session(opts, session_result, use_stderr)
|
|
261
|
+
return
|
|
262
|
+
|
|
168
263
|
workspace = opts.org or click.prompt("Workspace", err=use_stderr)
|
|
169
264
|
workspace = validators.validate_owner(ctx, None, workspace)[0]
|
|
170
265
|
opts.org = workspace
|
|
@@ -14,6 +14,7 @@ from .docker import docker as docker_cmd
|
|
|
14
14
|
from .generic import generic as generic_cmd
|
|
15
15
|
from .manage import install_cmd, list_cmd, uninstall_cmd
|
|
16
16
|
from .pnpm import pnpm as pnpm_cmd
|
|
17
|
+
from .terraform import terraform as terraform_cmd
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
@click.group()
|
|
@@ -26,18 +27,20 @@ def credential_helper():
|
|
|
26
27
|
manager automatically, or run the runtime command directly for debugging.
|
|
27
28
|
|
|
28
29
|
Examples:
|
|
30
|
+
|
|
31
|
+
\b
|
|
29
32
|
# Install Docker credential helper
|
|
30
33
|
$ cloudsmith credential-helper install docker
|
|
31
|
-
|
|
34
|
+
\b
|
|
32
35
|
# Install pnpm credential helper
|
|
33
36
|
$ cloudsmith credential-helper install pnpm
|
|
34
|
-
|
|
37
|
+
\b
|
|
35
38
|
# Install cargo credential helper
|
|
36
39
|
$ cloudsmith credential-helper install cargo
|
|
37
|
-
|
|
40
|
+
\b
|
|
38
41
|
# Test Docker credential helper directly
|
|
39
42
|
$ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
|
|
40
|
-
|
|
43
|
+
\b
|
|
41
44
|
# Test pnpm credential helper directly
|
|
42
45
|
$ cloudsmith credential-helper pnpm npm.cloudsmith.io
|
|
43
46
|
"""
|
|
@@ -50,5 +53,6 @@ credential_helper.add_command(install_cmd, name="install")
|
|
|
50
53
|
credential_helper.add_command(uninstall_cmd, name="uninstall")
|
|
51
54
|
credential_helper.add_command(list_cmd, name="list")
|
|
52
55
|
credential_helper.add_command(cargo_cmd, name="cargo")
|
|
56
|
+
credential_helper.add_command(terraform_cmd, name="terraform")
|
|
53
57
|
|
|
54
58
|
main.add_command(credential_helper, name="credential-helper")
|
|
@@ -33,23 +33,30 @@ def docker(opts, operation):
|
|
|
33
33
|
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
|
|
34
34
|
valid API key/token).
|
|
35
35
|
|
|
36
|
+
\b
|
|
36
37
|
Input (stdin):
|
|
37
38
|
Server URL as plain text (e.g. "docker.cloudsmith.io")
|
|
38
39
|
|
|
40
|
+
\b
|
|
39
41
|
Output (stdout):
|
|
40
42
|
JSON: {"Username": "token", "Secret": "<cloudsmith-token>"}
|
|
41
43
|
|
|
44
|
+
\b
|
|
42
45
|
Exit codes:
|
|
43
46
|
0: Success
|
|
44
47
|
1: Error (no credentials available, not a Cloudsmith registry, etc.)
|
|
45
48
|
|
|
49
|
+
\b
|
|
46
50
|
Examples:
|
|
51
|
+
|
|
52
|
+
\b
|
|
47
53
|
# Manual testing
|
|
48
54
|
$ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
|
|
49
|
-
|
|
55
|
+
\b
|
|
50
56
|
# Called by Docker via launcher
|
|
51
57
|
$ echo "docker.cloudsmith.io" | docker-credential-cloudsmith get
|
|
52
58
|
|
|
59
|
+
\b
|
|
53
60
|
Environment variables:
|
|
54
61
|
CLOUDSMITH_API_KEY: API key for authentication (optional)
|
|
55
62
|
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
|
|
@@ -15,6 +15,15 @@ import click
|
|
|
15
15
|
from cloudsmith_cli.credential_helpers.cargo.installer import CargoInstaller
|
|
16
16
|
from cloudsmith_cli.credential_helpers.generic import PartialInstallError
|
|
17
17
|
from cloudsmith_cli.credential_helpers.pnpm.installer import PNPMInstaller
|
|
18
|
+
from cloudsmith_cli.credential_helpers.terraform.installer import (
|
|
19
|
+
TerraformHelperExeNotFound,
|
|
20
|
+
TerraformInstaller,
|
|
21
|
+
TerraformPluginDirError,
|
|
22
|
+
_terraformrc_path,
|
|
23
|
+
)
|
|
24
|
+
from cloudsmith_cli.credential_helpers.terraform.terraformrc import (
|
|
25
|
+
TerraformrcConflictError,
|
|
26
|
+
)
|
|
18
27
|
|
|
19
28
|
from ....credential_helpers.docker.installer import DockerInstaller
|
|
20
29
|
from ... import utils
|
|
@@ -33,6 +42,7 @@ _INSTALLERS: dict[str, type] = {
|
|
|
33
42
|
"docker": DockerInstaller,
|
|
34
43
|
"pnpm": PNPMInstaller,
|
|
35
44
|
"cargo": CargoInstaller,
|
|
45
|
+
"terraform": TerraformInstaller,
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
|
|
@@ -65,6 +75,72 @@ def _get_installer(name: str):
|
|
|
65
75
|
return cls()
|
|
66
76
|
|
|
67
77
|
|
|
78
|
+
def _terraform_helper_args(ctx, opts, repo: str | None = None) -> tuple[str, ...]:
|
|
79
|
+
"""Build the terraformrc ``args`` list from the resolved config.
|
|
80
|
+
|
|
81
|
+
Baking ``--org``/``-r``/``-P`` into the block means ``terraform init`` works
|
|
82
|
+
with no environment variables and no hand-edited config. Only values
|
|
83
|
+
actually supplied are written, so an install without ``--org``/``--repo``/
|
|
84
|
+
``-P`` leaves ``args = []``.
|
|
85
|
+
|
|
86
|
+
A non-default ``--config-file``/``--credentials-file`` is also baked in: the
|
|
87
|
+
helper is later invoked by ``terraform init`` in a fresh process with none
|
|
88
|
+
of this invocation's flags, so without pinning them Terraform would resolve
|
|
89
|
+
credentials from the default search path instead of the files the operator
|
|
90
|
+
installed with.
|
|
91
|
+
"""
|
|
92
|
+
args: list[str] = []
|
|
93
|
+
if opts.org:
|
|
94
|
+
args.extend(["--org", opts.org])
|
|
95
|
+
if repo:
|
|
96
|
+
args.extend(["-r", repo])
|
|
97
|
+
profile = ctx.meta.get("profile")
|
|
98
|
+
if profile:
|
|
99
|
+
args.extend(["-P", profile])
|
|
100
|
+
config_file = ctx.meta.get("config_file")
|
|
101
|
+
if config_file:
|
|
102
|
+
args.extend(["--config-file", config_file])
|
|
103
|
+
creds_file = ctx.meta.get("creds_file")
|
|
104
|
+
if creds_file:
|
|
105
|
+
args.extend(["--credentials-file", creds_file])
|
|
106
|
+
return tuple(args)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
_REPO_FLAGS = ("-r", "--repo", "--repository")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _terraform_next_steps(helper_args: tuple[str, ...]) -> list[str]:
|
|
113
|
+
"""Return the post-install guidance for the required repository.
|
|
114
|
+
|
|
115
|
+
Terraform never tells the credentials helper which repository is being
|
|
116
|
+
requested, so the helper needs the repository supplied out-of-band. If it
|
|
117
|
+
was not baked into the generated ``args`` at install time, tell the user how
|
|
118
|
+
to provide it: set ``CLOUDSMITH_REPO`` or add ``--repo`` to the terraformrc
|
|
119
|
+
``args``. Returns an empty list when a repository is already configured.
|
|
120
|
+
"""
|
|
121
|
+
has_repo = any(
|
|
122
|
+
a in _REPO_FLAGS or a.split("=", 1)[0] in _REPO_FLAGS for a in helper_args
|
|
123
|
+
)
|
|
124
|
+
if has_repo:
|
|
125
|
+
return []
|
|
126
|
+
rc_path = _terraformrc_path()
|
|
127
|
+
return [
|
|
128
|
+
(
|
|
129
|
+
"Next steps: the Terraform helper requires a repository, which"
|
|
130
|
+
" Terraform does not pass to credentials helpers. Provide it in one"
|
|
131
|
+
" of these ways:"
|
|
132
|
+
),
|
|
133
|
+
(
|
|
134
|
+
" - export CLOUDSMITH_REPO=<your-repo> in the environment that runs"
|
|
135
|
+
" `terraform init`, or"
|
|
136
|
+
),
|
|
137
|
+
(
|
|
138
|
+
f" - add it to the args in {rc_path}, e.g."
|
|
139
|
+
' args = ["--repo", "<your-repo>", ...].'
|
|
140
|
+
),
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
|
|
68
144
|
# ---------------------------------------------------------------------------
|
|
69
145
|
# install
|
|
70
146
|
# ---------------------------------------------------------------------------
|
|
@@ -99,6 +175,16 @@ def _get_installer(name: str):
|
|
|
99
175
|
default=False,
|
|
100
176
|
help="Bypass the custom-domain cache and fetch fresh data from the API.",
|
|
101
177
|
)
|
|
178
|
+
@click.option(
|
|
179
|
+
"-r",
|
|
180
|
+
"--repo",
|
|
181
|
+
"--repository",
|
|
182
|
+
"repo",
|
|
183
|
+
default=None,
|
|
184
|
+
help="Terraform only: bake this repository into the terraformrc `args` "
|
|
185
|
+
"list (as `-r <repo>`) so `terraform init` needs no CLOUDSMITH_REPO env "
|
|
186
|
+
"var. Terraform does not pass the repository to a credentials helper.",
|
|
187
|
+
)
|
|
102
188
|
@common_cli_config_options
|
|
103
189
|
@common_cli_output_options
|
|
104
190
|
@common_api_auth_options
|
|
@@ -113,6 +199,7 @@ def install_cmd(
|
|
|
113
199
|
dry_run: bool,
|
|
114
200
|
no_discover: bool,
|
|
115
201
|
refresh: bool,
|
|
202
|
+
repo: str | None,
|
|
116
203
|
) -> None:
|
|
117
204
|
"""Install a credential helper launcher and configure the package manager.
|
|
118
205
|
|
|
@@ -140,19 +227,41 @@ def install_cmd(
|
|
|
140
227
|
\b
|
|
141
228
|
# Disable automatic custom-domain discovery
|
|
142
229
|
$ cloudsmith credential-helper install HELPER --no-discover
|
|
230
|
+
|
|
231
|
+
\b
|
|
232
|
+
# Terraform: bake the repository into the terraformrc args so
|
|
233
|
+
# `terraform init` needs no CLOUDSMITH_REPO env var
|
|
234
|
+
$ cloudsmith credential-helper install terraform --org acme --repo my-repo
|
|
143
235
|
"""
|
|
144
236
|
installer = _get_installer(helper)
|
|
237
|
+
|
|
238
|
+
install_kwargs = {
|
|
239
|
+
"bin_dir": bin_dir,
|
|
240
|
+
"domains": domains,
|
|
241
|
+
"dry_run": dry_run,
|
|
242
|
+
"discover": not no_discover,
|
|
243
|
+
"refresh": refresh,
|
|
244
|
+
"org": opts.org,
|
|
245
|
+
"credential": opts.credential,
|
|
246
|
+
"api_host": opts.api_host,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# Terraform bakes the resolved org/profile into the terraformrc `args` list
|
|
250
|
+
# so `terraform init` needs neither env vars nor a hand-edited config. The
|
|
251
|
+
# wrapper forwards these to the CLI ahead of the hostname at call time.
|
|
252
|
+
helper_args: tuple[str, ...] = ()
|
|
253
|
+
if helper == "terraform":
|
|
254
|
+
helper_args = _terraform_helper_args(ctx, opts, repo)
|
|
255
|
+
install_kwargs["helper_args"] = helper_args
|
|
256
|
+
|
|
145
257
|
try:
|
|
146
|
-
actions = installer.install(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
credential=opts.credential,
|
|
154
|
-
api_host=opts.api_host,
|
|
155
|
-
)
|
|
258
|
+
actions = installer.install(**install_kwargs)
|
|
259
|
+
except (
|
|
260
|
+
TerraformrcConflictError,
|
|
261
|
+
TerraformHelperExeNotFound,
|
|
262
|
+
TerraformPluginDirError,
|
|
263
|
+
) as exc:
|
|
264
|
+
raise click.ClickException(str(exc))
|
|
156
265
|
except OSError as exc:
|
|
157
266
|
raise click.ClickException(
|
|
158
267
|
f"Failed to install {helper!r} credential helper: {exc}"
|
|
@@ -166,11 +275,17 @@ def install_cmd(
|
|
|
166
275
|
use_stderr = utils.should_use_stderr(opts)
|
|
167
276
|
warnings = [a for a in actions if a.startswith("WARNING")]
|
|
168
277
|
normal = [a for a in actions if not a.startswith("WARNING")]
|
|
278
|
+
|
|
279
|
+
next_steps: list[str] = []
|
|
280
|
+
if helper == "terraform":
|
|
281
|
+
next_steps = _terraform_next_steps(helper_args)
|
|
282
|
+
|
|
169
283
|
data = {
|
|
170
284
|
"helper": helper,
|
|
171
285
|
"dry_run": dry_run,
|
|
172
286
|
"actions": normal,
|
|
173
287
|
"warnings": warnings,
|
|
288
|
+
"next_steps": next_steps,
|
|
174
289
|
}
|
|
175
290
|
if utils.maybe_print_as_json(opts, data):
|
|
176
291
|
sys.exit(ec)
|
|
@@ -181,6 +296,8 @@ def install_cmd(
|
|
|
181
296
|
click.echo(f" {action}" if dry_run else action, err=use_stderr)
|
|
182
297
|
for warning in warnings:
|
|
183
298
|
click.secho(f" {warning}" if dry_run else warning, err=True, fg="yellow")
|
|
299
|
+
for line in next_steps:
|
|
300
|
+
click.secho(line, err=use_stderr, fg="cyan")
|
|
184
301
|
sys.exit(ec)
|
|
185
302
|
|
|
186
303
|
|