cloudsmith-cli 1.12.1__tar.gz → 1.14.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.12.1/cloudsmith_cli.egg-info → cloudsmith_cli-1.14.0}/PKG-INFO +45 -2
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/README.md +42 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/__init__.py +2 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/auth.py +70 -13
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/download.py +12 -1
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/logout.py +151 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/tokens.py +71 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/vulnerabilities.py +141 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/commands/whoami.py +193 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/config.py +38 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/conftest.py +27 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_auth.py +283 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_download.py +53 -1
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_logout.py +147 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_tokens.py +75 -8
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/commands/test_vulnerabilities.py +112 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/cli/tests/test_webserver.py +155 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/utils.py +24 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/webserver.py +23 -10
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/init.py +34 -16
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/user.py +10 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/core/api/vulnerabilities.py +230 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/config.py +22 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/download.py +29 -1
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/keyring.py +51 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/server.py +4 -10
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_download.py +90 -0
- cloudsmith_cli-1.14.0/cloudsmith_cli/core/tests/test_init.py +371 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_keyring.py +99 -5
- cloudsmith_cli-1.14.0/cloudsmith_cli/data/VERSION +1 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0/cloudsmith_cli.egg-info}/PKG-INFO +45 -2
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/SOURCES.txt +8 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/requires.txt +2 -1
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/setup.py +2 -1
- cloudsmith_cli-1.12.1/cloudsmith_cli/cli/commands/whoami.py +0 -62
- cloudsmith_cli-1.12.1/cloudsmith_cli/core/tests/test_init.py +0 -186
- cloudsmith_cli-1.12.1/cloudsmith_cli/data/VERSION +0 -1
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/LICENSE +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/MANIFEST.in +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/__main__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/command.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/check.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/entitlements.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/list_.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/login.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/main.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/mcp.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/move.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/push.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/status.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/decorators.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/exceptions.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/saml.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/table.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_deny.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_licence.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/policy/test_vulnerability.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_check.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_entitlements.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_login.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_main.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_mcp.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_package_commands.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_repos.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/commands/test_upstream.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/conftest.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_push.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_saml.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/test_utils.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/tests/utils.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/types.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/cli/validators.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/distros.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/files.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/metrics.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/orgs.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/packages.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/quota.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/rates.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/repos.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/status.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/api/version.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/mcp/data.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/pagination.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/ratelimits.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/rest.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_rest.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/tests/test_version.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/utils.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/core/version.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/data/config.ini +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/data/credentials.ini +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/__init__.py +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/auth_error.html +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli/templates/auth_success.html +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/not-zip-safe +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
- {cloudsmith_cli-1.12.1 → cloudsmith_cli-1.14.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.14.0
|
|
4
4
|
Summary: Cloudsmith Command-Line Interface (CLI)
|
|
5
5
|
Home-page: https://github.com/cloudsmith-io/cloudsmith-cli
|
|
6
6
|
Author: Cloudsmith Ltd
|
|
@@ -34,9 +34,10 @@ Requires-Dist: json5>=0.9.0
|
|
|
34
34
|
Requires-Dist: cloudsmith-api<3.0,>=2.0.24
|
|
35
35
|
Requires-Dist: keyring>=25.4.1
|
|
36
36
|
Requires-Dist: mcp==1.9.1
|
|
37
|
-
Requires-Dist: toon
|
|
37
|
+
Requires-Dist: python-toon==0.1.2
|
|
38
38
|
Requires-Dist: requests>=2.18.4
|
|
39
39
|
Requires-Dist: requests_toolbelt>=1.0.0
|
|
40
|
+
Requires-Dist: rich>=13.0.0
|
|
40
41
|
Requires-Dist: semver>=2.7.9
|
|
41
42
|
Requires-Dist: urllib3>=2.5
|
|
42
43
|
Dynamic: author
|
|
@@ -87,6 +88,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
87
88
|
- `delete`|`rm`: Delete a package from a repository.
|
|
88
89
|
- `dependencies`|`deps`: List direct (non-transitive) dependencies for a package.
|
|
89
90
|
- `docs`: Launch the help website in your browser.
|
|
91
|
+
- `download`: Download a package from a repository.
|
|
90
92
|
- `entitlements`|`ents`: Manage the entitlements for a repository.
|
|
91
93
|
- `create`|`new`: Create a new entitlement in a repository.
|
|
92
94
|
- `delete`|`rm`: Delete an entitlement from a repository.
|
|
@@ -102,6 +104,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
102
104
|
- `packages`: List packages for a repository. (Aliases `repos list`)
|
|
103
105
|
- `repos`: List repositories for a namespace (owner).
|
|
104
106
|
- `login`|`token`: Retrieve your API authentication token/key via login.
|
|
107
|
+
- `logout`: Clear stored authentication credentials and SSO tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
|
|
105
108
|
- `metrics`: Metrics and statistics for a repository.
|
|
106
109
|
- `tokens`: Retrieve bandwidth usage for entitlement tokens.
|
|
107
110
|
- `packages`: Retrieve package usage for repository.
|
|
@@ -167,6 +170,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
167
170
|
- `rpm`: Manage rpm upstreams for a repository.
|
|
168
171
|
- `ruby`: Manage ruby upstreams for a repository.
|
|
169
172
|
- `swift`: Manage swift upstreams for a repository.
|
|
173
|
+
- `vulnerabilities`: Retrieve vulnerability results for a package.
|
|
170
174
|
- `whoami`: Retrieve your current authentication status.
|
|
171
175
|
|
|
172
176
|
## Installation
|
|
@@ -304,6 +308,45 @@ cloudsmith push rpm --help
|
|
|
304
308
|
```
|
|
305
309
|
|
|
306
310
|
|
|
311
|
+
## Downloading Packages
|
|
312
|
+
|
|
313
|
+
You can download packages from repositories using the `cloudsmith download` command. The CLI supports various filtering options to help you find and download the exact package you need.
|
|
314
|
+
|
|
315
|
+
For example, to download a specific package:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
cloudsmith download your-account/your-repo package-name
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
You can filter by various attributes like version, format, architecture, operating system, and tags:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
# Download a specific version
|
|
325
|
+
cloudsmith download your-account/your-repo package-name --version 1.2.3
|
|
326
|
+
|
|
327
|
+
# Filter by format and architecture
|
|
328
|
+
cloudsmith download your-account/your-repo package-name --format deb --arch amd64
|
|
329
|
+
|
|
330
|
+
# Filter by package tag (e.g., latest, stable, beta)
|
|
331
|
+
cloudsmith download your-account/your-repo package-name --tag latest
|
|
332
|
+
|
|
333
|
+
# Combine tag with metadata filters
|
|
334
|
+
cloudsmith download your-account/your-repo package-name --tag stable --format deb --arch arm64
|
|
335
|
+
|
|
336
|
+
# Download all associated files (POM, sources, javadoc, etc.)
|
|
337
|
+
cloudsmith download your-account/your-repo package-name --all-files
|
|
338
|
+
|
|
339
|
+
# Preview what would be downloaded without actually downloading
|
|
340
|
+
cloudsmith download your-account/your-repo package-name --dry-run
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
For more advanced usage and all available options:
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
cloudsmith download --help
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
|
|
307
350
|
## Contributing
|
|
308
351
|
|
|
309
352
|
Yes! Please do contribute, this is why we love open source. Please see [CONTRIBUTING](https://github.com/cloudsmith-io/cloudsmith-cli/blob/master/CONTRIBUTING.md) for contribution guidelines when making code changes or raising issues for bug reports, ideas, discussions and/or questions (i.e. help required).
|
|
@@ -32,6 +32,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
32
32
|
- `delete`|`rm`: Delete a package from a repository.
|
|
33
33
|
- `dependencies`|`deps`: List direct (non-transitive) dependencies for a package.
|
|
34
34
|
- `docs`: Launch the help website in your browser.
|
|
35
|
+
- `download`: Download a package from a repository.
|
|
35
36
|
- `entitlements`|`ents`: Manage the entitlements for a repository.
|
|
36
37
|
- `create`|`new`: Create a new entitlement in a repository.
|
|
37
38
|
- `delete`|`rm`: Delete an entitlement from a repository.
|
|
@@ -47,6 +48,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
47
48
|
- `packages`: List packages for a repository. (Aliases `repos list`)
|
|
48
49
|
- `repos`: List repositories for a namespace (owner).
|
|
49
50
|
- `login`|`token`: Retrieve your API authentication token/key via login.
|
|
51
|
+
- `logout`: Clear stored authentication credentials and SSO tokens (Keyring, API key from credential file and emit warning when `$CLOUDSMITH_API_KEY` is still set).
|
|
50
52
|
- `metrics`: Metrics and statistics for a repository.
|
|
51
53
|
- `tokens`: Retrieve bandwidth usage for entitlement tokens.
|
|
52
54
|
- `packages`: Retrieve package usage for repository.
|
|
@@ -112,6 +114,7 @@ The CLI currently supports the following commands (and sub-commands):
|
|
|
112
114
|
- `rpm`: Manage rpm upstreams for a repository.
|
|
113
115
|
- `ruby`: Manage ruby upstreams for a repository.
|
|
114
116
|
- `swift`: Manage swift upstreams for a repository.
|
|
117
|
+
- `vulnerabilities`: Retrieve vulnerability results for a package.
|
|
115
118
|
- `whoami`: Retrieve your current authentication status.
|
|
116
119
|
|
|
117
120
|
## Installation
|
|
@@ -249,6 +252,45 @@ cloudsmith push rpm --help
|
|
|
249
252
|
```
|
|
250
253
|
|
|
251
254
|
|
|
255
|
+
## Downloading Packages
|
|
256
|
+
|
|
257
|
+
You can download packages from repositories using the `cloudsmith download` command. The CLI supports various filtering options to help you find and download the exact package you need.
|
|
258
|
+
|
|
259
|
+
For example, to download a specific package:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
cloudsmith download your-account/your-repo package-name
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
You can filter by various attributes like version, format, architecture, operating system, and tags:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
# Download a specific version
|
|
269
|
+
cloudsmith download your-account/your-repo package-name --version 1.2.3
|
|
270
|
+
|
|
271
|
+
# Filter by format and architecture
|
|
272
|
+
cloudsmith download your-account/your-repo package-name --format deb --arch amd64
|
|
273
|
+
|
|
274
|
+
# Filter by package tag (e.g., latest, stable, beta)
|
|
275
|
+
cloudsmith download your-account/your-repo package-name --tag latest
|
|
276
|
+
|
|
277
|
+
# Combine tag with metadata filters
|
|
278
|
+
cloudsmith download your-account/your-repo package-name --tag stable --format deb --arch arm64
|
|
279
|
+
|
|
280
|
+
# Download all associated files (POM, sources, javadoc, etc.)
|
|
281
|
+
cloudsmith download your-account/your-repo package-name --all-files
|
|
282
|
+
|
|
283
|
+
# Preview what would be downloaded without actually downloading
|
|
284
|
+
cloudsmith download your-account/your-repo package-name --dry-run
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
For more advanced usage and all available options:
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
cloudsmith download --help
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
|
|
252
294
|
## Contributing
|
|
253
295
|
|
|
254
296
|
Yes! Please do contribute, this is why we love open source. Please see [CONTRIBUTING](https://github.com/cloudsmith-io/cloudsmith-cli/blob/master/CONTRIBUTING.md) for contribution guidelines when making code changes or raising issues for bug reports, ideas, discussions and/or questions (i.e. help required).
|
|
@@ -9,14 +9,16 @@ from ..exceptions import handle_api_exceptions
|
|
|
9
9
|
from ..saml import create_configured_session, get_idp_url
|
|
10
10
|
from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
|
|
11
11
|
from .main import main
|
|
12
|
-
from .tokens import create
|
|
12
|
+
from .tokens import create, request_api_key
|
|
13
13
|
|
|
14
14
|
# Authentication server configuration
|
|
15
15
|
AUTH_SERVER_HOST = "127.0.0.1"
|
|
16
16
|
AUTH_SERVER_PORT = 12400
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def _perform_saml_authentication(
|
|
19
|
+
def _perform_saml_authentication(
|
|
20
|
+
opts, owner, enable_token_creation=False, use_stderr=False
|
|
21
|
+
):
|
|
20
22
|
"""Perform SAML authentication via web browser and local web server."""
|
|
21
23
|
session = create_configured_session(opts)
|
|
22
24
|
api_host = opts.api_config.host
|
|
@@ -25,12 +27,12 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
|
|
|
25
27
|
|
|
26
28
|
click.echo(
|
|
27
29
|
f"Opening your organization's SAML IDP URL in your browser: {click.style(idp_url, bold=True)}",
|
|
28
|
-
err=
|
|
30
|
+
err=use_stderr,
|
|
29
31
|
)
|
|
30
|
-
click.echo(err=
|
|
32
|
+
click.echo(err=use_stderr)
|
|
31
33
|
webbrowser.open(idp_url)
|
|
32
34
|
|
|
33
|
-
click.echo("Starting webserver to begin authentication ... ", err=
|
|
35
|
+
click.echo("Starting webserver to begin authentication ... ", err=use_stderr)
|
|
34
36
|
|
|
35
37
|
auth_server = AuthenticationWebServer(
|
|
36
38
|
(AUTH_SERVER_HOST, AUTH_SERVER_PORT),
|
|
@@ -60,14 +62,14 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
|
|
|
60
62
|
"--token",
|
|
61
63
|
default=False,
|
|
62
64
|
is_flag=True,
|
|
63
|
-
help="Retrieve a user API token after successful authentication.",
|
|
65
|
+
help="[DEPRECATED: Use --request-api-key] Retrieve a user API token after successful authentication.",
|
|
64
66
|
)
|
|
65
67
|
@click.option(
|
|
66
68
|
"-f",
|
|
67
69
|
"--force",
|
|
68
70
|
default=False,
|
|
69
71
|
is_flag=True,
|
|
70
|
-
help="Force refresh of user API token without prompts.",
|
|
72
|
+
help="[DEPRECATED: Use --request-api-key] Force refresh of user API token without prompts.",
|
|
71
73
|
)
|
|
72
74
|
@click.option(
|
|
73
75
|
"--save-config",
|
|
@@ -79,17 +81,49 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
|
|
|
79
81
|
"--json",
|
|
80
82
|
default=False,
|
|
81
83
|
is_flag=True,
|
|
82
|
-
help="Output token details in
|
|
84
|
+
help="[DEPRECATED: Use --output-format json] Output token details in JSON format.",
|
|
85
|
+
)
|
|
86
|
+
@click.option(
|
|
87
|
+
"--request-api-key",
|
|
88
|
+
"request_api_key_flag",
|
|
89
|
+
default=False,
|
|
90
|
+
is_flag=True,
|
|
91
|
+
help="Retrieve API token (auto-creates or auto-rotates, no prompts). "
|
|
92
|
+
"Warning: If token exists, this will rotate it and invalidate the old key.",
|
|
83
93
|
)
|
|
84
94
|
@decorators.common_cli_config_options
|
|
85
95
|
@decorators.common_cli_output_options
|
|
86
96
|
@decorators.initialise_api
|
|
87
97
|
@click.pass_context
|
|
88
|
-
def authenticate(
|
|
98
|
+
def authenticate(
|
|
99
|
+
ctx, opts, owner, token, force, save_config, json, request_api_key_flag
|
|
100
|
+
):
|
|
89
101
|
"""Authenticate to Cloudsmith using the org's SAML setup."""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
# Validate mutual exclusivity
|
|
103
|
+
if request_api_key_flag and (token or force):
|
|
104
|
+
raise click.UsageError(
|
|
105
|
+
"--request-api-key cannot be used with --token or --force. "
|
|
106
|
+
"Use --request-api-key alone for fully automated token retrieval."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Determine if we should redirect info messages to stderr
|
|
110
|
+
use_stderr = request_api_key_flag or json or utils.should_use_stderr(opts)
|
|
111
|
+
|
|
112
|
+
if token:
|
|
113
|
+
click.secho(
|
|
114
|
+
"DEPRECATION WARNING: The `--token` flag is deprecated and will be removed in a future release. "
|
|
115
|
+
"Please use `--request-api-key` instead.",
|
|
116
|
+
fg="yellow",
|
|
117
|
+
err=True,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if force:
|
|
121
|
+
click.secho(
|
|
122
|
+
"DEPRECATION WARNING: The `--force` flag is deprecated and will be removed in a future release. "
|
|
123
|
+
"Please use `--request-api-key` instead (force is implied).",
|
|
124
|
+
fg="yellow",
|
|
125
|
+
err=True,
|
|
126
|
+
)
|
|
93
127
|
|
|
94
128
|
if json and not utils.should_use_stderr(opts):
|
|
95
129
|
click.secho(
|
|
@@ -106,11 +140,34 @@ def authenticate(ctx, opts, owner, token, force, save_config, json):
|
|
|
106
140
|
err=use_stderr,
|
|
107
141
|
)
|
|
108
142
|
|
|
143
|
+
# Determine if we need to refresh API after SSO (required for token operations)
|
|
144
|
+
enable_token_creation = token or request_api_key_flag
|
|
145
|
+
|
|
109
146
|
context_message = "Failed to authenticate via SSO!"
|
|
110
147
|
with handle_api_exceptions(ctx, opts=opts, context_msg=context_message):
|
|
111
148
|
_perform_saml_authentication(
|
|
112
|
-
opts,
|
|
149
|
+
opts,
|
|
150
|
+
owner,
|
|
151
|
+
enable_token_creation=enable_token_creation,
|
|
152
|
+
use_stderr=use_stderr,
|
|
113
153
|
)
|
|
114
154
|
|
|
155
|
+
if request_api_key_flag:
|
|
156
|
+
# Non-interactive token retrieval
|
|
157
|
+
new_token = request_api_key(ctx, opts, save_config=save_config)
|
|
158
|
+
|
|
159
|
+
if not new_token:
|
|
160
|
+
raise click.ClickException(
|
|
161
|
+
"Failed to retrieve API token. No token was returned."
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Check if JSON output is requested
|
|
165
|
+
if utils.maybe_print_as_json(opts, new_token):
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
# Default: output only the raw token value to stdout
|
|
169
|
+
click.echo(new_token.key)
|
|
170
|
+
return
|
|
171
|
+
|
|
115
172
|
if token:
|
|
116
173
|
ctx.invoke(create, opts=opts, save_config=save_config, force=force, json=json)
|
|
@@ -42,6 +42,11 @@ from .main import main
|
|
|
42
42
|
@click.option(
|
|
43
43
|
"--arch", "arch_filter", help="Architecture filter (e.g., 'amd64', 'arm64')."
|
|
44
44
|
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--tag",
|
|
47
|
+
"tag_filter",
|
|
48
|
+
help="Filter by package tag (e.g., 'latest', 'stable'). Use --format, --arch, --os for metadata filters.",
|
|
49
|
+
)
|
|
45
50
|
@click.option(
|
|
46
51
|
"--outfile",
|
|
47
52
|
type=click.Path(),
|
|
@@ -78,6 +83,7 @@ def download( # noqa: C901
|
|
|
78
83
|
format_filter,
|
|
79
84
|
os_filter,
|
|
80
85
|
arch_filter,
|
|
86
|
+
tag_filter,
|
|
81
87
|
outfile,
|
|
82
88
|
overwrite,
|
|
83
89
|
all_files,
|
|
@@ -88,7 +94,7 @@ def download( # noqa: C901
|
|
|
88
94
|
Download a package from a Cloudsmith repository.
|
|
89
95
|
|
|
90
96
|
This command downloads a package binary from a Cloudsmith repository. You can
|
|
91
|
-
filter packages by version, format, operating system, and
|
|
97
|
+
filter packages by version, format, operating system, architecture, and tags.
|
|
92
98
|
|
|
93
99
|
Examples:
|
|
94
100
|
|
|
@@ -104,6 +110,10 @@ def download( # noqa: C901
|
|
|
104
110
|
# Download with filters and custom output name
|
|
105
111
|
cloudsmith download myorg/myrepo mypackage --format deb --arch amd64 --outfile my-package.deb
|
|
106
112
|
|
|
113
|
+
\b
|
|
114
|
+
# Download a package with a specific tag
|
|
115
|
+
cloudsmith download myorg/myrepo mypackage --tag latest
|
|
116
|
+
|
|
107
117
|
\b
|
|
108
118
|
# Download all associated files (POM, sources, javadoc, etc.) for a Maven/NuGet package
|
|
109
119
|
cloudsmith download myorg/myrepo mypackage --all-files
|
|
@@ -150,6 +160,7 @@ def download( # noqa: C901
|
|
|
150
160
|
format_filter=format_filter,
|
|
151
161
|
os_filter=os_filter,
|
|
152
162
|
arch_filter=arch_filter,
|
|
163
|
+
tag_filter=tag_filter,
|
|
153
164
|
yes=yes,
|
|
154
165
|
)
|
|
155
166
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Copyright 2026 Cloudsmith Ltd
|
|
2
|
+
"""CLI/Commands - Log out and clear authentication state."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import cloudsmith_api
|
|
8
|
+
|
|
9
|
+
from ...core import keyring
|
|
10
|
+
from .. import decorators, utils
|
|
11
|
+
from ..config import CredentialsReader
|
|
12
|
+
from .main import main
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _clear_credentials(dry_run, use_stderr):
|
|
16
|
+
"""Clear credential files. Returns result dict."""
|
|
17
|
+
creds_files = CredentialsReader.find_existing_files()
|
|
18
|
+
if not creds_files:
|
|
19
|
+
click.echo("No credentials file found.", err=use_stderr)
|
|
20
|
+
return {"action": "not_found", "files": []}
|
|
21
|
+
|
|
22
|
+
if not dry_run:
|
|
23
|
+
for path in creds_files:
|
|
24
|
+
CredentialsReader.clear_api_key(path)
|
|
25
|
+
|
|
26
|
+
verb = "Would remove" if dry_run else "Removed"
|
|
27
|
+
for path in creds_files:
|
|
28
|
+
click.echo(
|
|
29
|
+
f"{verb} credentials from: " + click.style(path, bold=True),
|
|
30
|
+
err=use_stderr,
|
|
31
|
+
)
|
|
32
|
+
action = "would_remove" if dry_run else "removed"
|
|
33
|
+
return {"action": action, "files": list(creds_files)}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _clear_keyring(api_host, dry_run, use_stderr):
|
|
37
|
+
"""Clear SSO tokens from keyring. Returns result dict."""
|
|
38
|
+
if not keyring.should_use_keyring():
|
|
39
|
+
click.secho(
|
|
40
|
+
"Keyring is disabled (CLOUDSMITH_NO_KEYRING is set).",
|
|
41
|
+
fg="yellow",
|
|
42
|
+
err=use_stderr,
|
|
43
|
+
)
|
|
44
|
+
return {"action": "disabled"}
|
|
45
|
+
|
|
46
|
+
if not keyring.has_sso_tokens(api_host):
|
|
47
|
+
click.echo("No SSO tokens found in system keyring.", err=use_stderr)
|
|
48
|
+
return {"action": "not_found"}
|
|
49
|
+
|
|
50
|
+
if dry_run:
|
|
51
|
+
click.echo("Would remove SSO tokens from system keyring.", err=use_stderr)
|
|
52
|
+
return {"action": "would_remove"}
|
|
53
|
+
|
|
54
|
+
deleted = keyring.delete_sso_tokens(api_host)
|
|
55
|
+
action = "removed" if deleted else "failed"
|
|
56
|
+
msg = f"{'Removed' if deleted else 'Failed to remove'} SSO tokens from system keyring."
|
|
57
|
+
click.secho(msg, fg=None if deleted else "red", err=use_stderr)
|
|
58
|
+
return {"action": action} if deleted else {"action": action, "message": msg}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _env_api_key_status():
|
|
62
|
+
"""Return structured status for the CLOUDSMITH_API_KEY env var."""
|
|
63
|
+
is_set = bool(os.environ.get("CLOUDSMITH_API_KEY"))
|
|
64
|
+
return {
|
|
65
|
+
"is_set": is_set,
|
|
66
|
+
"action": "unset CLOUDSMITH_API_KEY" if is_set else "none",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _collect_warnings(keyring_only, config_only):
|
|
71
|
+
"""Collect advisory warnings based on flags and environment."""
|
|
72
|
+
warnings = []
|
|
73
|
+
if config_only:
|
|
74
|
+
warnings.append("SSO tokens were not modified (--config-only).")
|
|
75
|
+
if keyring_only:
|
|
76
|
+
warnings.append("credentials.ini was not modified (--keyring-only).")
|
|
77
|
+
if os.environ.get("CLOUDSMITH_API_KEY"):
|
|
78
|
+
warnings.append(
|
|
79
|
+
"CLOUDSMITH_API_KEY is set in your environment. "
|
|
80
|
+
"Run: unset CLOUDSMITH_API_KEY"
|
|
81
|
+
)
|
|
82
|
+
return warnings
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@main.command()
|
|
86
|
+
@click.option(
|
|
87
|
+
"--api-host",
|
|
88
|
+
envvar="CLOUDSMITH_API_HOST",
|
|
89
|
+
default=None,
|
|
90
|
+
help="The API host to clear keyring tokens for.",
|
|
91
|
+
)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--keyring-only",
|
|
94
|
+
is_flag=True,
|
|
95
|
+
default=False,
|
|
96
|
+
help="Only clear SSO tokens from the system keyring.",
|
|
97
|
+
)
|
|
98
|
+
@click.option(
|
|
99
|
+
"--config-only",
|
|
100
|
+
is_flag=True,
|
|
101
|
+
default=False,
|
|
102
|
+
help="Only clear credentials from credentials.ini.",
|
|
103
|
+
)
|
|
104
|
+
@click.option(
|
|
105
|
+
"--dry-run",
|
|
106
|
+
is_flag=True,
|
|
107
|
+
default=False,
|
|
108
|
+
help="Show what would be removed without removing anything.",
|
|
109
|
+
)
|
|
110
|
+
@decorators.common_cli_config_options
|
|
111
|
+
@decorators.common_cli_output_options
|
|
112
|
+
@click.pass_context
|
|
113
|
+
def logout(ctx, opts, api_host, keyring_only, config_only, dry_run):
|
|
114
|
+
"""Clear stored authentication credentials and SSO tokens."""
|
|
115
|
+
if keyring_only and config_only:
|
|
116
|
+
raise click.UsageError(
|
|
117
|
+
"--keyring-only and --config-only are mutually exclusive."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if api_host is None:
|
|
121
|
+
api_host = opts.api_host or cloudsmith_api.Configuration().host
|
|
122
|
+
|
|
123
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
124
|
+
|
|
125
|
+
credential_file = (
|
|
126
|
+
_clear_credentials(dry_run, use_stderr)
|
|
127
|
+
if not keyring_only
|
|
128
|
+
else {"action": "skipped", "files": []}
|
|
129
|
+
)
|
|
130
|
+
keyring_result = (
|
|
131
|
+
_clear_keyring(api_host, dry_run, use_stderr)
|
|
132
|
+
if not config_only
|
|
133
|
+
else {"action": "skipped"}
|
|
134
|
+
)
|
|
135
|
+
warnings = _collect_warnings(keyring_only, config_only)
|
|
136
|
+
|
|
137
|
+
for warning in warnings:
|
|
138
|
+
click.secho(f"Note: {warning}", fg="yellow", err=use_stderr)
|
|
139
|
+
|
|
140
|
+
utils.maybe_print_as_json(
|
|
141
|
+
opts,
|
|
142
|
+
{
|
|
143
|
+
"api_host": api_host,
|
|
144
|
+
"dry_run": dry_run,
|
|
145
|
+
"sources": {
|
|
146
|
+
"credential_file": credential_file,
|
|
147
|
+
"keyring": keyring_result,
|
|
148
|
+
"environment_api_key": _env_api_key_status(),
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
)
|
|
@@ -31,6 +31,77 @@ def handle_duplicate_token_error(exc, ctx, opts, save_config, force, json):
|
|
|
31
31
|
raise exc
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def request_api_key(ctx, opts, save_config=False):
|
|
35
|
+
"""
|
|
36
|
+
Request an API key non-interactively.
|
|
37
|
+
|
|
38
|
+
This function creates a new token or rotates an existing one without any prompts.
|
|
39
|
+
Used by the --request-api-key flag in the auth command.
|
|
40
|
+
|
|
41
|
+
Returns the token object on success.
|
|
42
|
+
Raises ApiException on failure.
|
|
43
|
+
"""
|
|
44
|
+
context_msg = "Failed to retrieve API token!"
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
# Don't use handle_api_exceptions here so we can catch and handle
|
|
48
|
+
# the "already has token" error ourselves
|
|
49
|
+
with utils.maybe_spinner(opts):
|
|
50
|
+
new_token = api.create_user_token_saml()
|
|
51
|
+
|
|
52
|
+
if save_config:
|
|
53
|
+
create, has_errors = create_config_files(
|
|
54
|
+
ctx, opts, api_key=new_token.key, force=True
|
|
55
|
+
)
|
|
56
|
+
new_config_messaging(has_errors, opts, create, api_key=new_token.key)
|
|
57
|
+
|
|
58
|
+
return new_token
|
|
59
|
+
|
|
60
|
+
except exceptions.ApiException as exc:
|
|
61
|
+
if exc.status == 401:
|
|
62
|
+
# Unauthorized - re-raise with handler
|
|
63
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
64
|
+
raise
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
exc.status == 400
|
|
68
|
+
and exc.detail
|
|
69
|
+
and "User has already created an API key" in exc.detail
|
|
70
|
+
):
|
|
71
|
+
# Token exists - rotate it automatically
|
|
72
|
+
click.echo(
|
|
73
|
+
"Warning: Rotating existing API token. Your old key will be invalidated.",
|
|
74
|
+
err=True,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# List tokens and select the first one
|
|
78
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
79
|
+
with utils.maybe_spinner(opts):
|
|
80
|
+
api_tokens = api.list_user_tokens()
|
|
81
|
+
|
|
82
|
+
if not api_tokens:
|
|
83
|
+
raise click.ClickException("No existing tokens found to rotate.")
|
|
84
|
+
|
|
85
|
+
token_slug = api_tokens[0].slug_perm
|
|
86
|
+
|
|
87
|
+
# Refresh the token
|
|
88
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
89
|
+
with utils.maybe_spinner(opts):
|
|
90
|
+
new_token = api.refresh_user_token(token_slug)
|
|
91
|
+
|
|
92
|
+
if save_config:
|
|
93
|
+
create, has_errors = create_config_files(
|
|
94
|
+
ctx, opts, api_key=new_token.key, force=True
|
|
95
|
+
)
|
|
96
|
+
new_config_messaging(has_errors, opts, create, api_key=new_token.key)
|
|
97
|
+
|
|
98
|
+
return new_token
|
|
99
|
+
|
|
100
|
+
# Other errors - use the handler
|
|
101
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
102
|
+
raise exc
|
|
103
|
+
|
|
104
|
+
|
|
34
105
|
@main.group(cls=command.AliasGroup, name="tokens")
|
|
35
106
|
@decorators.common_cli_config_options
|
|
36
107
|
@decorators.common_cli_output_options
|