cloudsmith-cli 1.25.0__tar.gz → 1.27.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.25.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.27.0}/PKG-INFO +8 -4
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/README.md +2 -2
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/__init__.py +0 -2
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/command.py +21 -3
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/__init__.py +6 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/auth.py +305 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/copy.py +17 -3
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +7 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/credential_helper/cargo.py +94 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +5 -4
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/credential_helper/generic.py +1 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +129 -10
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +1 -1
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/credential_helper/terraform.py +151 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/dependencies.py +9 -10
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/domains.py +17 -15
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/download.py +14 -16
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/list_.py +13 -14
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/logout.py +12 -5
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/main.py +3 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/registry.py +59 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/cli/commands/repos.py +1474 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/whoami.py +12 -6
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/config.py +27 -5
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/decorators.py +80 -17
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/exceptions.py +75 -41
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/saml.py +11 -47
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/table.py +1 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/utils.py +13 -6
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/webserver.py +27 -31
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/files.py +1 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/packages.py +2 -7
- cloudsmith_cli-1.27.0/cloudsmith_cli/core/api/repos.py +176 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/version.py +2 -2
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/vulnerabilities.py +5 -5
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/cache_utils.py +32 -10
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/chain.py +12 -9
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/models.py +9 -2
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +1 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +2 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +2 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +1 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/provider.py +3 -1
- cloudsmith_cli-1.27.0/cloudsmith_cli/core/credentials/providers/keyring_provider.py +95 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +88 -9
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/download.py +6 -7
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/keyring.py +150 -38
- cloudsmith_cli-1.27.0/cloudsmith_cli/core/macos_keychain.py +168 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/rest.py +2 -119
- cloudsmith_cli-1.27.0/cloudsmith_cli/core/session.py +142 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/core/sso.py +224 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/utils.py +50 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/version.py +2 -2
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/cargo/__init__.py +4 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/cargo/installer.py +491 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/cargo/runtime.py +265 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/common.py +26 -5
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/custom_domains.py +4 -3
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/docker/installer.py +13 -10
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/pnpm/runtime.py +2 -1
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/terraform/__init__.py +4 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/terraform/installer.py +496 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/terraform/runtime.py +204 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/credential_helpers/terraform/terraformrc.py +165 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/data/VERSION +1 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/data/config.ini +3 -0
- cloudsmith_cli-1.27.0/cloudsmith_cli/wrapper.py +48 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0/cloudsmith_cli.egg-info}/PKG-INFO +8 -4
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli.egg-info/SOURCES.txt +14 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli.egg-info/entry_points.txt +1 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli.egg-info/requires.txt +5 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/pyproject.toml +46 -6
- cloudsmith_cli-1.25.0/cloudsmith_cli/cli/commands/__init__.py +0 -34
- cloudsmith_cli-1.25.0/cloudsmith_cli/cli/commands/auth.py +0 -210
- cloudsmith_cli-1.25.0/cloudsmith_cli/cli/commands/repos.py +0 -333
- cloudsmith_cli-1.25.0/cloudsmith_cli/core/api/repos.py +0 -81
- cloudsmith_cli-1.25.0/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -59
- cloudsmith_cli-1.25.0/cloudsmith_cli/data/VERSION +0 -1
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/LICENSE +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/MANIFEST.in +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/__main__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/check.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/entitlements.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/login.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/mcp.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/metadata.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/move.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/push.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/status.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/dsc_parser.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/types.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/cli/validators.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/conftest.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/distros.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/init.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/metadata.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/metrics.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/orgs.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/quota.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/rates.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/status.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/api/user.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/config.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/mcp/data.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/mcp/server.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/pagination.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/core/ratelimits.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/default_domains.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/generic.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/launchers.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/pnpm/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/pnpm/installer.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/credential_helpers/pnpm/rc.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/data/credentials.ini +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/templates/__init__.py +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/templates/auth_error.html +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/templates/auth_success.html +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli/templates/auth_two_factor.html +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
- {cloudsmith_cli-1.25.0 → cloudsmith_cli-1.27.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.27.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
|
|
@@ -32,14 +32,18 @@ Requires-Dist: cloudsmith-api<3.0,>=2.0.31
|
|
|
32
32
|
Requires-Dist: keyring>=25.4.1
|
|
33
33
|
Requires-Dist: keyrings-alt>=5.0.2
|
|
34
34
|
Requires-Dist: keyrings-cryptfile>=1.3.9
|
|
35
|
+
Requires-Dist: cryptography>=50.0.0
|
|
35
36
|
Requires-Dist: mcp==1.28.1
|
|
36
|
-
Requires-Dist:
|
|
37
|
+
Requires-Dist: pydantic-settings>=2.14.2
|
|
38
|
+
Requires-Dist: PyJWT[crypto]>=2.13.0
|
|
37
39
|
Requires-Dist: python-toon==0.1.2
|
|
38
40
|
Requires-Dist: requests>=2.18.4
|
|
39
41
|
Requires-Dist: requests_toolbelt>=1.0.0
|
|
40
42
|
Requires-Dist: rich>=13.0.0
|
|
41
43
|
Requires-Dist: semver>=2.7.9
|
|
44
|
+
Requires-Dist: starlette>=1.3.1
|
|
42
45
|
Requires-Dist: urllib3>=2.5
|
|
46
|
+
Requires-Dist: tomlkit>=0.15.0
|
|
43
47
|
Provides-Extra: aws
|
|
44
48
|
Requires-Dist: boto3[crt]>=1.26.0; extra == "aws"
|
|
45
49
|
Provides-Extra: all
|
|
@@ -369,7 +373,7 @@ cloudsmith auth --owner example
|
|
|
369
373
|
Beginning authentication for the example org ...
|
|
370
374
|
Your organization's SAML IDP URL is: https://example.com/some-saml-idp
|
|
371
375
|
|
|
372
|
-
|
|
376
|
+
Waiting for the authentication callback on port 12400 ...
|
|
373
377
|
|
|
374
378
|
Authentication complete
|
|
375
379
|
```
|
|
@@ -380,7 +384,7 @@ The CLI opens the IDP URL in your browser automatically. If it can't (for exampl
|
|
|
380
384
|
cloudsmith auth --owner example --no-browser
|
|
381
385
|
```
|
|
382
386
|
|
|
383
|
-
*Note:* The authentication callback is served on `127.0.0.1
|
|
387
|
+
*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.
|
|
384
388
|
|
|
385
389
|
#### Getting Your API Key
|
|
386
390
|
|
|
@@ -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
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""CLI - Group/Command classes."""
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
from collections import OrderedDict
|
|
4
5
|
|
|
5
6
|
import click.exceptions
|
|
@@ -50,12 +51,25 @@ def _format_click_exception_as_json(exception):
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
class AliasGroup(DYMGroup):
|
|
53
|
-
"""A command group with DYM and
|
|
54
|
+
"""A command group with DYM, alias and lazy-subcommand support.
|
|
55
|
+
|
|
56
|
+
``lazy_commands`` maps a command name to the module that registers it.
|
|
57
|
+
The module is imported on first use, which keeps startup fast because
|
|
58
|
+
an invocation imports only the module of the invoked command.
|
|
59
|
+
``lazy_aliases`` supplies the alias map for those commands up front.
|
|
60
|
+
"""
|
|
54
61
|
|
|
55
62
|
def __init__(self, *args, **kwargs):
|
|
63
|
+
lazy_commands = kwargs.pop("lazy_commands", None)
|
|
64
|
+
lazy_aliases = kwargs.pop("lazy_aliases", None)
|
|
56
65
|
super().__init__(*args, **kwargs)
|
|
66
|
+
self.lazy_commands = dict(lazy_commands or {})
|
|
57
67
|
self.aliases = OrderedDict()
|
|
58
68
|
self.inverse = {}
|
|
69
|
+
for name, aliases in (lazy_aliases or {}).items():
|
|
70
|
+
self.aliases[name] = aliases
|
|
71
|
+
for alias in aliases:
|
|
72
|
+
self.inverse[alias] = name
|
|
59
73
|
|
|
60
74
|
def resolve_command(self, ctx, args):
|
|
61
75
|
try:
|
|
@@ -74,7 +88,7 @@ class AliasGroup(DYMGroup):
|
|
|
74
88
|
raise
|
|
75
89
|
|
|
76
90
|
def list_commands(self, ctx):
|
|
77
|
-
commands = super().list_commands(ctx)
|
|
91
|
+
commands = sorted(set(super().list_commands(ctx)) | set(self.lazy_commands))
|
|
78
92
|
|
|
79
93
|
if getattr(ctx, "showing_help", False):
|
|
80
94
|
for k, v in enumerate(commands):
|
|
@@ -99,7 +113,11 @@ class AliasGroup(DYMGroup):
|
|
|
99
113
|
except KeyError:
|
|
100
114
|
pass
|
|
101
115
|
|
|
102
|
-
|
|
116
|
+
cmd = super().get_command(ctx, cmd_name)
|
|
117
|
+
if cmd is None and cmd_name in self.lazy_commands:
|
|
118
|
+
importlib.import_module(self.lazy_commands[cmd_name])
|
|
119
|
+
cmd = super().get_command(ctx, cmd_name)
|
|
120
|
+
return cmd
|
|
103
121
|
|
|
104
122
|
def command(self, *args, **kwargs):
|
|
105
123
|
def decorator(f):
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""CLI/Commands - Authenticate the user."""
|
|
2
|
+
|
|
3
|
+
import webbrowser
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from ...core.sso import (
|
|
9
|
+
SsoRenewalStatus,
|
|
10
|
+
get_access_token_expiry,
|
|
11
|
+
renew_sso_session,
|
|
12
|
+
)
|
|
13
|
+
from .. import decorators, utils, validators
|
|
14
|
+
from ..exceptions import handle_api_exceptions
|
|
15
|
+
from ..saml import create_configured_session, get_idp_url
|
|
16
|
+
from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
|
|
17
|
+
from .main import main
|
|
18
|
+
from .tokens import create, request_api_key
|
|
19
|
+
|
|
20
|
+
# Authentication server configuration
|
|
21
|
+
AUTH_SERVER_HOST = "127.0.0.1"
|
|
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
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _perform_saml_authentication(
|
|
105
|
+
opts,
|
|
106
|
+
owner,
|
|
107
|
+
enable_token_creation=False,
|
|
108
|
+
use_stderr=False,
|
|
109
|
+
no_browser=False,
|
|
110
|
+
profile=None,
|
|
111
|
+
):
|
|
112
|
+
"""Perform SAML authentication via web browser and local web server."""
|
|
113
|
+
session = create_configured_session(opts)
|
|
114
|
+
api_host = opts.api_config.host
|
|
115
|
+
|
|
116
|
+
auth_server, port = _create_auth_server(
|
|
117
|
+
opts, owner, session, enable_token_creation, profile
|
|
118
|
+
)
|
|
119
|
+
redirect_url = f"http://{AUTH_REDIRECT_HOST}:{port}"
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
click.echo(
|
|
123
|
+
f"Waiting for the authentication callback on port {port} ... ",
|
|
124
|
+
err=use_stderr,
|
|
125
|
+
)
|
|
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:
|
|
138
|
+
click.echo(
|
|
139
|
+
"Skipping automatic browser launch (--no-browser). "
|
|
140
|
+
"Please open the URL above manually to continue.",
|
|
141
|
+
err=use_stderr,
|
|
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
|
|
151
|
+
|
|
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
|
+
)
|
|
158
|
+
|
|
159
|
+
auth_server.handle_request()
|
|
160
|
+
finally:
|
|
161
|
+
auth_server.server_close()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@main.command(aliases=["auth"])
|
|
165
|
+
@click.option(
|
|
166
|
+
"-t",
|
|
167
|
+
"--token",
|
|
168
|
+
default=False,
|
|
169
|
+
is_flag=True,
|
|
170
|
+
help="[DEPRECATED: Use --request-api-key] Retrieve a user API token after successful authentication.",
|
|
171
|
+
)
|
|
172
|
+
@click.option(
|
|
173
|
+
"-f",
|
|
174
|
+
"--force",
|
|
175
|
+
default=False,
|
|
176
|
+
is_flag=True,
|
|
177
|
+
help="[DEPRECATED: Use --request-api-key] Force refresh of user API token without prompts.",
|
|
178
|
+
)
|
|
179
|
+
@click.option(
|
|
180
|
+
"--save-config",
|
|
181
|
+
default=False,
|
|
182
|
+
is_flag=True,
|
|
183
|
+
help="Save the new API key to your configuration files.",
|
|
184
|
+
)
|
|
185
|
+
@click.option(
|
|
186
|
+
"--json",
|
|
187
|
+
default=False,
|
|
188
|
+
is_flag=True,
|
|
189
|
+
help="[DEPRECATED: Use --output-format json] Output token details in JSON format.",
|
|
190
|
+
)
|
|
191
|
+
@click.option(
|
|
192
|
+
"--request-api-key",
|
|
193
|
+
"request_api_key_flag",
|
|
194
|
+
default=False,
|
|
195
|
+
is_flag=True,
|
|
196
|
+
help="Retrieve API token (auto-creates or auto-rotates, no prompts). "
|
|
197
|
+
"Warning: If token exists, this will rotate it and invalidate the old key.",
|
|
198
|
+
)
|
|
199
|
+
@click.option(
|
|
200
|
+
"--no-browser",
|
|
201
|
+
default=False,
|
|
202
|
+
is_flag=True,
|
|
203
|
+
help="Don't try to open a browser automatically; print the SAML IDP URL to "
|
|
204
|
+
"open manually instead.",
|
|
205
|
+
)
|
|
206
|
+
@decorators.common_cli_config_options
|
|
207
|
+
@decorators.common_cli_output_options
|
|
208
|
+
@decorators.initialise_api
|
|
209
|
+
@click.pass_context
|
|
210
|
+
def authenticate(
|
|
211
|
+
ctx,
|
|
212
|
+
opts,
|
|
213
|
+
token,
|
|
214
|
+
force,
|
|
215
|
+
save_config,
|
|
216
|
+
json,
|
|
217
|
+
request_api_key_flag,
|
|
218
|
+
no_browser,
|
|
219
|
+
):
|
|
220
|
+
"""Authenticate to Cloudsmith using the Workspace's SAML setup."""
|
|
221
|
+
# Validate mutual exclusivity
|
|
222
|
+
if request_api_key_flag and (token or force):
|
|
223
|
+
raise click.UsageError(
|
|
224
|
+
"--request-api-key cannot be used with --token or --force. "
|
|
225
|
+
"Use --request-api-key alone for fully automated token retrieval."
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Determine if we should redirect info messages to stderr
|
|
229
|
+
use_stderr = request_api_key_flag or json or utils.should_use_stderr(opts)
|
|
230
|
+
|
|
231
|
+
if token:
|
|
232
|
+
click.secho(
|
|
233
|
+
"DEPRECATION WARNING: The `--token` flag is deprecated and will be removed in a future release. "
|
|
234
|
+
"Please use `--request-api-key` instead.",
|
|
235
|
+
fg="yellow",
|
|
236
|
+
err=True,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if force:
|
|
240
|
+
click.secho(
|
|
241
|
+
"DEPRECATION WARNING: The `--force` flag is deprecated and will be removed in a future release. "
|
|
242
|
+
"Please use `--request-api-key` instead (force is implied).",
|
|
243
|
+
fg="yellow",
|
|
244
|
+
err=True,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if json and not utils.should_use_stderr(opts):
|
|
248
|
+
click.secho(
|
|
249
|
+
"DEPRECATION WARNING: The `--json` flag is deprecated and will be removed in a future release. "
|
|
250
|
+
"Please use `--output-format json` instead.",
|
|
251
|
+
fg="yellow",
|
|
252
|
+
err=True,
|
|
253
|
+
)
|
|
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
|
+
|
|
263
|
+
workspace = opts.org or click.prompt("Workspace", err=use_stderr)
|
|
264
|
+
workspace = validators.validate_owner(ctx, None, workspace)[0]
|
|
265
|
+
opts.org = workspace
|
|
266
|
+
|
|
267
|
+
click.echo(
|
|
268
|
+
"Beginning authentication for the "
|
|
269
|
+
f"{click.style(workspace, bold=True)} Workspace ... ",
|
|
270
|
+
err=use_stderr,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Determine if we need to refresh API after SSO (required for token operations)
|
|
274
|
+
enable_token_creation = token or request_api_key_flag
|
|
275
|
+
|
|
276
|
+
context_message = "Failed to authenticate via SSO!"
|
|
277
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_message):
|
|
278
|
+
_perform_saml_authentication(
|
|
279
|
+
opts,
|
|
280
|
+
workspace,
|
|
281
|
+
enable_token_creation=enable_token_creation,
|
|
282
|
+
use_stderr=use_stderr,
|
|
283
|
+
no_browser=no_browser,
|
|
284
|
+
profile=ctx.meta.get("profile"),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
if request_api_key_flag:
|
|
288
|
+
# Non-interactive token retrieval
|
|
289
|
+
new_token = request_api_key(ctx, opts, save_config=save_config)
|
|
290
|
+
|
|
291
|
+
if not new_token:
|
|
292
|
+
raise click.ClickException(
|
|
293
|
+
"Failed to retrieve API token. No token was returned."
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# Check if JSON output is requested
|
|
297
|
+
if utils.maybe_print_as_json(opts, new_token):
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
# Default: output only the raw token value to stdout
|
|
301
|
+
click.echo(new_token.key)
|
|
302
|
+
return
|
|
303
|
+
|
|
304
|
+
if token:
|
|
305
|
+
ctx.invoke(create, opts=opts, save_config=save_config, force=force, json=json)
|
|
@@ -71,14 +71,26 @@ def copy(
|
|
|
71
71
|
),
|
|
72
72
|
maybe_spinner(opts),
|
|
73
73
|
):
|
|
74
|
-
|
|
74
|
+
slug_perm, new_slug = copy_package(
|
|
75
75
|
owner=owner, repo=source, identifier=slug, destination=destination
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
click.secho("OK", fg="green", err=use_stderr)
|
|
79
79
|
|
|
80
|
+
click.echo(
|
|
81
|
+
"Copied: {owner}/{repo}/{slug} ({slug_perm})".format(
|
|
82
|
+
owner=click.style(owner, fg="magenta"),
|
|
83
|
+
repo=click.style(destination, fg="magenta"),
|
|
84
|
+
slug=click.style(new_slug, fg="green"),
|
|
85
|
+
slug_perm=click.style(slug_perm, bold=True),
|
|
86
|
+
),
|
|
87
|
+
err=use_stderr,
|
|
88
|
+
)
|
|
89
|
+
|
|
80
90
|
if no_wait_for_sync:
|
|
81
|
-
utils.maybe_print_status_json(
|
|
91
|
+
utils.maybe_print_status_json(
|
|
92
|
+
opts, {"slug": new_slug, "slug_perm": slug_perm, "status": "OK"}
|
|
93
|
+
)
|
|
82
94
|
return
|
|
83
95
|
|
|
84
96
|
wait_for_package_sync(
|
|
@@ -92,4 +104,6 @@ def copy(
|
|
|
92
104
|
attempts=sync_attempts,
|
|
93
105
|
)
|
|
94
106
|
|
|
95
|
-
utils.maybe_print_status_json(
|
|
107
|
+
utils.maybe_print_status_json(
|
|
108
|
+
opts, {"slug": new_slug, "slug_perm": slug_perm, "status": "OK"}
|
|
109
|
+
)
|
|
@@ -9,10 +9,12 @@ that follow their respective credential helper protocols.
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
11
|
from ..main import main
|
|
12
|
+
from .cargo import cargo as cargo_cmd
|
|
12
13
|
from .docker import docker as docker_cmd
|
|
13
14
|
from .generic import generic as generic_cmd
|
|
14
15
|
from .manage import install_cmd, list_cmd, uninstall_cmd
|
|
15
16
|
from .pnpm import pnpm as pnpm_cmd
|
|
17
|
+
from .terraform import terraform as terraform_cmd
|
|
16
18
|
|
|
17
19
|
|
|
18
20
|
@click.group()
|
|
@@ -31,6 +33,9 @@ def credential_helper():
|
|
|
31
33
|
# Install pnpm credential helper
|
|
32
34
|
$ cloudsmith credential-helper install pnpm
|
|
33
35
|
|
|
36
|
+
# Install cargo credential helper
|
|
37
|
+
$ cloudsmith credential-helper install cargo
|
|
38
|
+
|
|
34
39
|
# Test Docker credential helper directly
|
|
35
40
|
$ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
|
|
36
41
|
|
|
@@ -45,5 +50,7 @@ credential_helper.add_command(generic_cmd, name="generic")
|
|
|
45
50
|
credential_helper.add_command(install_cmd, name="install")
|
|
46
51
|
credential_helper.add_command(uninstall_cmd, name="uninstall")
|
|
47
52
|
credential_helper.add_command(list_cmd, name="list")
|
|
53
|
+
credential_helper.add_command(cargo_cmd, name="cargo")
|
|
54
|
+
credential_helper.add_command(terraform_cmd, name="terraform")
|
|
48
55
|
|
|
49
56
|
main.add_command(credential_helper, name="credential-helper")
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Copyright 2026 Cloudsmith Ltd
|
|
2
|
+
"""
|
|
3
|
+
Cargo credential provider command.
|
|
4
|
+
|
|
5
|
+
Implements the Cargo credential provider protocol for Cloudsmith registries.
|
|
6
|
+
|
|
7
|
+
See: https://doc.rust-lang.org/cargo/reference/credential-provider-protocol.html
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from ....credential_helpers.cargo import execute
|
|
15
|
+
from ...decorators import common_api_auth_options, resolve_credentials
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command(context_settings={"ignore_unknown_options": True})
|
|
19
|
+
@click.option(
|
|
20
|
+
"--cargo-plugin",
|
|
21
|
+
is_flag=True,
|
|
22
|
+
default=False,
|
|
23
|
+
hidden=True,
|
|
24
|
+
help="Passed by Cargo when invoking this command as a credential provider.",
|
|
25
|
+
)
|
|
26
|
+
@click.argument("provider_args", nargs=-1, type=click.UNPROCESSED)
|
|
27
|
+
@common_api_auth_options
|
|
28
|
+
@resolve_credentials
|
|
29
|
+
def cargo(opts, cargo_plugin, provider_args): # pylint: disable=unused-argument
|
|
30
|
+
"""
|
|
31
|
+
Cargo credential provider for Cloudsmith registries.
|
|
32
|
+
|
|
33
|
+
Speaks the Cargo credential provider protocol: a newline-delimited JSON
|
|
34
|
+
conversation on stdin/stdout, starting with a hello message that announces
|
|
35
|
+
the supported protocol versions, then one response per request.
|
|
36
|
+
|
|
37
|
+
Provides credentials for all Cloudsmith Cargo registries: ``*.cloudsmith.io``,
|
|
38
|
+
``*.cloudsmith.com``, and any custom domains configured for the Workspace
|
|
39
|
+
(requires a Workspace - ``--workspace``, CLOUDSMITH_WORKSPACE, or
|
|
40
|
+
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
|
|
41
|
+
valid API key/token).
|
|
42
|
+
|
|
43
|
+
A registry that is not a Cloudsmith one is answered with
|
|
44
|
+
``url-not-supported`` so Cargo falls through to the next configured
|
|
45
|
+
credential provider. ``cargo login``/``cargo logout`` are answered with
|
|
46
|
+
``operation-not-supported``: credentials come from the Cloudsmith CLI's own
|
|
47
|
+
provider chain, so there is nothing to store or clear.
|
|
48
|
+
|
|
49
|
+
\b
|
|
50
|
+
Input (stdin):
|
|
51
|
+
One JSON request per line, e.g.
|
|
52
|
+
{"v":1,"kind":"get","operation":"read",
|
|
53
|
+
"registry":{"index-url":"sparse+https://cargo.cloudsmith.io/org/repo/"}}
|
|
54
|
+
|
|
55
|
+
\b
|
|
56
|
+
Output (stdout):
|
|
57
|
+
{"v":[1]}
|
|
58
|
+
{"Ok":{"kind":"get","token":"<cloudsmith-token>","cache":"session",
|
|
59
|
+
"operation_independent":true}}
|
|
60
|
+
|
|
61
|
+
\b
|
|
62
|
+
Exit codes:
|
|
63
|
+
0: Session completed
|
|
64
|
+
1: No credentials available, or the session broke down
|
|
65
|
+
|
|
66
|
+
\b
|
|
67
|
+
Examples:
|
|
68
|
+
# Manual testing
|
|
69
|
+
$ echo '{"v":1,"kind":"get","operation":"read","registry":{"index-url":"sparse+https://cargo.cloudsmith.io/org/repo/"}}' \\
|
|
70
|
+
| cloudsmith credential-helper cargo
|
|
71
|
+
|
|
72
|
+
# Called by Cargo via the launcher
|
|
73
|
+
$ cargo-credential-cloudsmith --cargo-plugin
|
|
74
|
+
|
|
75
|
+
\b
|
|
76
|
+
Environment variables:
|
|
77
|
+
CLOUDSMITH_API_KEY: API key for authentication (optional)
|
|
78
|
+
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
|
|
79
|
+
"""
|
|
80
|
+
# `provider_args` collects the extra arguments Cargo appends from the
|
|
81
|
+
# credential-provider config entry. This provider takes no configuration
|
|
82
|
+
# of its own, so they are accepted and ignored rather than rejected — an
|
|
83
|
+
# unknown-option error would surface as an authentication failure.
|
|
84
|
+
exit_code, stderr = execute(
|
|
85
|
+
sys.stdin,
|
|
86
|
+
sys.stdout,
|
|
87
|
+
credential=opts.credential,
|
|
88
|
+
api_host=opts.api_host,
|
|
89
|
+
org=opts.org,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if stderr is not None:
|
|
93
|
+
click.echo(stderr, err=True)
|
|
94
|
+
sys.exit(exit_code)
|
|
@@ -28,9 +28,10 @@ def docker(opts, operation):
|
|
|
28
28
|
(get/store/erase/list).
|
|
29
29
|
|
|
30
30
|
Provides credentials for all Cloudsmith Docker registries: ``*.cloudsmith.io``,
|
|
31
|
-
``*.cloudsmith.com``, and any custom domains configured for the
|
|
32
|
-
(requires
|
|
33
|
-
``config.ini
|
|
31
|
+
``*.cloudsmith.com``, and any custom domains configured for the Workspace
|
|
32
|
+
(requires a Workspace - ``--workspace``, CLOUDSMITH_WORKSPACE, or
|
|
33
|
+
``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
|
|
34
|
+
valid API key/token).
|
|
34
35
|
|
|
35
36
|
Input (stdin):
|
|
36
37
|
Server URL as plain text (e.g. "docker.cloudsmith.io")
|
|
@@ -51,7 +52,7 @@ def docker(opts, operation):
|
|
|
51
52
|
|
|
52
53
|
Environment variables:
|
|
53
54
|
CLOUDSMITH_API_KEY: API key for authentication (optional)
|
|
54
|
-
|
|
55
|
+
CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
|
|
55
56
|
"""
|
|
56
57
|
exit_code, stdout, stderr = execute(
|
|
57
58
|
operation,
|
|
@@ -27,7 +27,7 @@ def generic(opts):
|
|
|
27
27
|
|
|
28
28
|
Resolves a credential through the full provider chain and writes a
|
|
29
29
|
versioned JSON document to stdout. Takes no arguments: a Cloudsmith token
|
|
30
|
-
is
|
|
30
|
+
is Workspace-wide, so the host it will be used against does not change
|
|
31
31
|
which credential resolves.
|
|
32
32
|
|
|
33
33
|
\b
|