cloudsmith-cli 1.23.0__tar.gz → 1.25.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.23.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.25.0}/PKG-INFO +18 -4
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/README.md +15 -1
- cloudsmith_cli-1.25.0/cloudsmith_cli/__main__.py +15 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/auth.py +41 -4
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +8 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +24 -9
- cloudsmith_cli-1.25.0/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +51 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/entitlements.py +17 -5
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/logout.py +6 -3
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/mcp.py +7 -2
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/push.py +107 -1
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/upstream.py +1 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/config.py +1 -1
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/decorators.py +29 -3
- cloudsmith_cli-1.25.0/cloudsmith_cli/cli/dsc_parser.py +268 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/exceptions.py +3 -2
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/saml.py +25 -24
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/validators.py +23 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/webserver.py +62 -8
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +33 -1
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/keyring.py +68 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/generic.py +16 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/launchers.py +19 -9
- cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/__init__.py +4 -0
- cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/installer.py +264 -0
- cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/rc.py +185 -0
- cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/runtime.py +68 -0
- cloudsmith_cli-1.25.0/cloudsmith_cli/data/VERSION +1 -0
- cloudsmith_cli-1.25.0/cloudsmith_cli/templates/auth_two_factor.html +38 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0/cloudsmith_cli.egg-info}/PKG-INFO +18 -4
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/SOURCES.txt +8 -1
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/requires.txt +2 -2
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/pyproject.toml +5 -3
- cloudsmith_cli-1.23.0/cloudsmith_cli/__main__.py +0 -8
- cloudsmith_cli-1.23.0/cloudsmith_cli/data/VERSION +0 -1
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/LICENSE +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/MANIFEST.in +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/command.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/check.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/generic.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/domains.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/download.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/list_.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/login.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/main.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metadata.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/move.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/status.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/whoami.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/table.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/types.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/utils.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/conftest.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/distros.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/files.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/init.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/metadata.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/metrics.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/orgs.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/packages.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/quota.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/rates.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/repos.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/status.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/user.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/version.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/vulnerabilities.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/cache_utils.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/config.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/models.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/download.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/data.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/server.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/pagination.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/ratelimits.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/rest.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/utils.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/version.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/common.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/custom_domains.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/default_domains.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/installer.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/data/config.ini +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/data/credentials.ini +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/__init__.py +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/auth_error.html +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/auth_success.html +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
- {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.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.25.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,12 +23,12 @@ 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: click!=8.3.0,>=8.
|
|
26
|
+
Requires-Dist: click!=8.3.0,>=8.2
|
|
27
27
|
Requires-Dist: click-configfile>=0.2.3
|
|
28
28
|
Requires-Dist: click-didyoumean>=0.0.3
|
|
29
29
|
Requires-Dist: click-spinner>=0.1.7
|
|
30
30
|
Requires-Dist: json5>=0.9.0
|
|
31
|
-
Requires-Dist: cloudsmith-api<3.0,>=2.0.
|
|
31
|
+
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
|
|
@@ -367,13 +367,21 @@ You can authenticate using your organization's SAML provider, if configured, wit
|
|
|
367
367
|
```
|
|
368
368
|
cloudsmith auth --owner example
|
|
369
369
|
Beginning authentication for the example org ...
|
|
370
|
-
|
|
370
|
+
Your organization's SAML IDP URL is: https://example.com/some-saml-idp
|
|
371
371
|
|
|
372
372
|
Starting webserver to begin authentication ...
|
|
373
373
|
|
|
374
374
|
Authentication complete
|
|
375
375
|
```
|
|
376
376
|
|
|
377
|
+
The CLI opens the IDP URL in your browser automatically. If it can't (for example in Cygwin or a headless shell), it tells you to open the URL above manually and carries on waiting for the callback. Use `--no-browser` to skip the automatic launch entirely:
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
cloudsmith auth --owner example --no-browser
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
*Note:* The authentication callback is served on `127.0.0.1:12400`, so the browser you open the URL in must be running on the same machine as the CLI.
|
|
384
|
+
|
|
377
385
|
#### Getting Your API Key
|
|
378
386
|
|
|
379
387
|
You can retrieve your API key using the `cloudsmith login` command:
|
|
@@ -411,6 +419,12 @@ For example, if you wanted to upload a Debian package, you can do it in one-step
|
|
|
411
419
|
cloudsmith push deb your-org/your-repo/ubuntu/xenial libxml2-2.9.4-2.x86_64.deb
|
|
412
420
|
```
|
|
413
421
|
|
|
422
|
+
To upload a Debian *source* package, give it the `.dsc`. The source and Debian packaging archives listed in the `.dsc` are found and uploaded with it:
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
cloudsmith push deb your-org/your-repo/ubuntu/xenial hello_2.10-3.dsc
|
|
426
|
+
```
|
|
427
|
+
|
|
414
428
|
Want to know how to do it with another packaging format? Easy, just ask for help:
|
|
415
429
|
|
|
416
430
|
```
|
|
@@ -319,13 +319,21 @@ You can authenticate using your organization's SAML provider, if configured, wit
|
|
|
319
319
|
```
|
|
320
320
|
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
|
Starting webserver to begin authentication ...
|
|
325
325
|
|
|
326
326
|
Authentication complete
|
|
327
327
|
```
|
|
328
328
|
|
|
329
|
+
The CLI opens the IDP URL in your browser automatically. If it can't (for example in Cygwin or a headless shell), it tells you to open the URL above manually and carries on waiting for the callback. Use `--no-browser` to skip the automatic launch entirely:
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
cloudsmith auth --owner example --no-browser
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
*Note:* The authentication callback is served on `127.0.0.1:12400`, so the browser you open the URL in must be running on the same machine as the CLI.
|
|
336
|
+
|
|
329
337
|
#### Getting Your API Key
|
|
330
338
|
|
|
331
339
|
You can retrieve your API key using the `cloudsmith login` command:
|
|
@@ -363,6 +371,12 @@ For example, if you wanted to upload a Debian package, you can do it in one-step
|
|
|
363
371
|
cloudsmith push deb your-org/your-repo/ubuntu/xenial libxml2-2.9.4-2.x86_64.deb
|
|
364
372
|
```
|
|
365
373
|
|
|
374
|
+
To upload a Debian *source* package, give it the `.dsc`. The source and Debian packaging archives listed in the `.dsc` are found and uploaded with it:
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
cloudsmith push deb your-org/your-repo/ubuntu/xenial hello_2.10-3.dsc
|
|
378
|
+
```
|
|
379
|
+
|
|
366
380
|
Want to know how to do it with another packaging format? Easy, just ask for help:
|
|
367
381
|
|
|
368
382
|
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Cloudsmith CLI - Main script."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from .cli.commands.main import main
|
|
6
|
+
|
|
7
|
+
if __name__ == "__main__":
|
|
8
|
+
# sys.exit() is required: AliasGroup.main runs click with
|
|
9
|
+
# standalone_mode=False, so click returns the exit code (e.g. from
|
|
10
|
+
# ctx.exit()) instead of raising SystemExit. The console script and the
|
|
11
|
+
# PyInstaller entry point wrap main() in sys.exit() too; a bare main()
|
|
12
|
+
# call would discard the code and always exit 0.
|
|
13
|
+
# Disable false positive for parameters handled by click.
|
|
14
|
+
# pylint: disable=no-value-for-parameter
|
|
15
|
+
sys.exit(main())
|
|
@@ -17,7 +17,7 @@ AUTH_SERVER_PORT = 12400
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _perform_saml_authentication(
|
|
20
|
-
opts, owner, enable_token_creation=False, use_stderr=False
|
|
20
|
+
opts, owner, enable_token_creation=False, use_stderr=False, no_browser=False
|
|
21
21
|
):
|
|
22
22
|
"""Perform SAML authentication via web browser and local web server."""
|
|
23
23
|
session = create_configured_session(opts)
|
|
@@ -26,11 +26,32 @@ def _perform_saml_authentication(
|
|
|
26
26
|
idp_url = get_idp_url(api_host, owner, session=session)
|
|
27
27
|
|
|
28
28
|
click.echo(
|
|
29
|
-
f"
|
|
29
|
+
f"Your organization's SAML IDP URL is: {click.style(idp_url, bold=True)}",
|
|
30
30
|
err=use_stderr,
|
|
31
31
|
)
|
|
32
32
|
click.echo(err=use_stderr)
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
if no_browser:
|
|
35
|
+
click.echo(
|
|
36
|
+
"Skipping automatic browser launch (--no-browser). "
|
|
37
|
+
"Please open the URL above manually to continue.",
|
|
38
|
+
err=use_stderr,
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
try:
|
|
42
|
+
browser_opened = webbrowser.open(idp_url)
|
|
43
|
+
except Exception:
|
|
44
|
+
# Browser launch failures vary by platform (webbrowser.Error on
|
|
45
|
+
# Cygwin/headless, anything else elsewhere), so catch broadly and
|
|
46
|
+
# fall back to the manual URL rather than crashing.
|
|
47
|
+
browser_opened = False
|
|
48
|
+
|
|
49
|
+
if not browser_opened:
|
|
50
|
+
click.echo(
|
|
51
|
+
"Couldn't open a browser automatically. "
|
|
52
|
+
"Please open the URL above manually to continue.",
|
|
53
|
+
err=use_stderr,
|
|
54
|
+
)
|
|
34
55
|
|
|
35
56
|
click.echo("Starting webserver to begin authentication ... ", err=use_stderr)
|
|
36
57
|
|
|
@@ -91,12 +112,27 @@ def _perform_saml_authentication(
|
|
|
91
112
|
help="Retrieve API token (auto-creates or auto-rotates, no prompts). "
|
|
92
113
|
"Warning: If token exists, this will rotate it and invalidate the old key.",
|
|
93
114
|
)
|
|
115
|
+
@click.option(
|
|
116
|
+
"--no-browser",
|
|
117
|
+
default=False,
|
|
118
|
+
is_flag=True,
|
|
119
|
+
help="Don't try to open a browser automatically; print the SAML IDP URL to "
|
|
120
|
+
"open manually instead.",
|
|
121
|
+
)
|
|
94
122
|
@decorators.common_cli_config_options
|
|
95
123
|
@decorators.common_cli_output_options
|
|
96
124
|
@decorators.initialise_api
|
|
97
125
|
@click.pass_context
|
|
98
126
|
def authenticate(
|
|
99
|
-
ctx,
|
|
127
|
+
ctx,
|
|
128
|
+
opts,
|
|
129
|
+
owner,
|
|
130
|
+
token,
|
|
131
|
+
force,
|
|
132
|
+
save_config,
|
|
133
|
+
json,
|
|
134
|
+
request_api_key_flag,
|
|
135
|
+
no_browser,
|
|
100
136
|
):
|
|
101
137
|
"""Authenticate to Cloudsmith using the org's SAML setup."""
|
|
102
138
|
# Validate mutual exclusivity
|
|
@@ -150,6 +186,7 @@ def authenticate(
|
|
|
150
186
|
owner,
|
|
151
187
|
enable_token_creation=enable_token_creation,
|
|
152
188
|
use_stderr=use_stderr,
|
|
189
|
+
no_browser=no_browser,
|
|
153
190
|
)
|
|
154
191
|
|
|
155
192
|
if request_api_key_flag:
|
|
@@ -12,6 +12,7 @@ from ..main import main
|
|
|
12
12
|
from .docker import docker as docker_cmd
|
|
13
13
|
from .generic import generic as generic_cmd
|
|
14
14
|
from .manage import install_cmd, list_cmd, uninstall_cmd
|
|
15
|
+
from .pnpm import pnpm as pnpm_cmd
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@click.group()
|
|
@@ -27,11 +28,18 @@ def credential_helper():
|
|
|
27
28
|
# Install Docker credential helper
|
|
28
29
|
$ cloudsmith credential-helper install docker
|
|
29
30
|
|
|
31
|
+
# Install pnpm credential helper
|
|
32
|
+
$ cloudsmith credential-helper install pnpm
|
|
33
|
+
|
|
30
34
|
# Test Docker credential helper directly
|
|
31
35
|
$ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
|
|
36
|
+
|
|
37
|
+
# Test pnpm credential helper directly
|
|
38
|
+
$ cloudsmith credential-helper pnpm npm.cloudsmith.io
|
|
32
39
|
"""
|
|
33
40
|
|
|
34
41
|
|
|
42
|
+
credential_helper.add_command(pnpm_cmd, name="pnpm")
|
|
35
43
|
credential_helper.add_command(docker_cmd, name="docker")
|
|
36
44
|
credential_helper.add_command(generic_cmd, name="generic")
|
|
37
45
|
credential_helper.add_command(install_cmd, name="install")
|
|
@@ -12,6 +12,9 @@ import sys
|
|
|
12
12
|
|
|
13
13
|
import click
|
|
14
14
|
|
|
15
|
+
from cloudsmith_cli.credential_helpers.generic import PartialInstallError
|
|
16
|
+
from cloudsmith_cli.credential_helpers.pnpm.installer import PNPMInstaller
|
|
17
|
+
|
|
15
18
|
from ....credential_helpers.docker.installer import DockerInstaller
|
|
16
19
|
from ... import utils
|
|
17
20
|
from ...decorators import (
|
|
@@ -27,6 +30,7 @@ from ...decorators import (
|
|
|
27
30
|
|
|
28
31
|
_INSTALLERS: dict[str, type] = {
|
|
29
32
|
"docker": DockerInstaller,
|
|
33
|
+
"pnpm": PNPMInstaller,
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
|
|
@@ -40,7 +44,7 @@ def _get_installer(name: str):
|
|
|
40
44
|
|
|
41
45
|
Returns
|
|
42
46
|
-------
|
|
43
|
-
|
|
47
|
+
BaseInstaller
|
|
44
48
|
An instance of the appropriate installer class.
|
|
45
49
|
|
|
46
50
|
Raises
|
|
@@ -85,7 +89,7 @@ def _get_installer(name: str):
|
|
|
85
89
|
"--no-discover",
|
|
86
90
|
is_flag=True,
|
|
87
91
|
default=False,
|
|
88
|
-
help="Disable automatic discovery of custom
|
|
92
|
+
help="Disable automatic discovery of custom domains.",
|
|
89
93
|
)
|
|
90
94
|
@click.option(
|
|
91
95
|
"--refresh",
|
|
@@ -110,25 +114,30 @@ def install_cmd(
|
|
|
110
114
|
) -> None:
|
|
111
115
|
"""Install a credential helper launcher and configure the package manager.
|
|
112
116
|
|
|
113
|
-
HELPER is the name of the credential helper to install (e.g. ``docker``).
|
|
117
|
+
HELPER is the name of the credential helper to install (e.g. ``docker``, ``pnpm``).
|
|
118
|
+
|
|
119
|
+
Important for pnpm: The tokenHelper directive is only honored in the
|
|
120
|
+
user-level ~/.npmrc file, not in a project-level .npmrc. This is a pnpm
|
|
121
|
+
security restriction. The absolute path to the launcher is automatically
|
|
122
|
+
calculated and configured.
|
|
114
123
|
|
|
115
124
|
Examples:
|
|
116
125
|
|
|
117
126
|
\b
|
|
118
|
-
# Install
|
|
119
|
-
$ cloudsmith credential-helper install
|
|
127
|
+
# Install credential helper
|
|
128
|
+
$ cloudsmith credential-helper install HELPER
|
|
120
129
|
|
|
121
130
|
\b
|
|
122
131
|
# Install with a custom domain
|
|
123
|
-
$ cloudsmith credential-helper install
|
|
132
|
+
$ cloudsmith credential-helper install HELPER --domain my.registry.example.com
|
|
124
133
|
|
|
125
134
|
\b
|
|
126
135
|
# Preview without making changes
|
|
127
|
-
$ cloudsmith credential-helper install
|
|
136
|
+
$ cloudsmith credential-helper install HELPER --dry-run
|
|
128
137
|
|
|
129
138
|
\b
|
|
130
139
|
# Disable automatic custom-domain discovery
|
|
131
|
-
$ cloudsmith credential-helper install
|
|
140
|
+
$ cloudsmith credential-helper install HELPER --no-discover
|
|
132
141
|
"""
|
|
133
142
|
installer = _get_installer(helper)
|
|
134
143
|
try:
|
|
@@ -146,6 +155,11 @@ def install_cmd(
|
|
|
146
155
|
raise click.ClickException(
|
|
147
156
|
f"Failed to install {helper!r} credential helper: {exc}"
|
|
148
157
|
)
|
|
158
|
+
except PartialInstallError as exc:
|
|
159
|
+
actions = exc.actions
|
|
160
|
+
ec = exc.exit_code
|
|
161
|
+
else:
|
|
162
|
+
ec = 0
|
|
149
163
|
|
|
150
164
|
use_stderr = utils.should_use_stderr(opts)
|
|
151
165
|
warnings = [a for a in actions if a.startswith("WARNING")]
|
|
@@ -157,7 +171,7 @@ def install_cmd(
|
|
|
157
171
|
"warnings": warnings,
|
|
158
172
|
}
|
|
159
173
|
if utils.maybe_print_as_json(opts, data):
|
|
160
|
-
|
|
174
|
+
sys.exit(ec)
|
|
161
175
|
|
|
162
176
|
if dry_run:
|
|
163
177
|
click.echo("Dry run — no changes will be made:", err=use_stderr)
|
|
@@ -165,6 +179,7 @@ def install_cmd(
|
|
|
165
179
|
click.echo(f" {action}" if dry_run else action, err=use_stderr)
|
|
166
180
|
for warning in warnings:
|
|
167
181
|
click.secho(f" {warning}" if dry_run else warning, err=True, fg="yellow")
|
|
182
|
+
sys.exit(ec)
|
|
168
183
|
|
|
169
184
|
|
|
170
185
|
# ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Copyright 2026 Cloudsmith Ltd
|
|
2
|
+
"""
|
|
3
|
+
pnpm credential helper command.
|
|
4
|
+
|
|
5
|
+
Implements the pnpm credential helper protocol for Cloudsmith registries.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ....credential_helpers.pnpm import execute
|
|
13
|
+
from ...decorators import common_api_auth_options, resolve_credentials
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command()
|
|
17
|
+
@click.argument("repo", required=False, default="npm.cloudsmith.io")
|
|
18
|
+
@common_api_auth_options
|
|
19
|
+
@resolve_credentials
|
|
20
|
+
def pnpm(opts, repo):
|
|
21
|
+
"""
|
|
22
|
+
Input (arg, optional):
|
|
23
|
+
Server URL as plain text (e.g. "npm.cloudsmith.io")
|
|
24
|
+
|
|
25
|
+
Output (stdout):
|
|
26
|
+
Text: <cloudsmith-token>
|
|
27
|
+
|
|
28
|
+
\b
|
|
29
|
+
Exit codes:
|
|
30
|
+
0: Success
|
|
31
|
+
1: Error (no credentials available, not a Cloudsmith registry, etc.)
|
|
32
|
+
|
|
33
|
+
\b
|
|
34
|
+
Environment variables:
|
|
35
|
+
CLOUDSMITH_API_KEY: API key for authentication (optional)
|
|
36
|
+
CLOUDSMITH_ORG: Organisation slug (required for custom domain support)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
exit_code, stdout, stderr = execute(
|
|
40
|
+
repo,
|
|
41
|
+
credential=opts.credential,
|
|
42
|
+
api_host=opts.api_host,
|
|
43
|
+
org=opts.org,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if stdout is not None:
|
|
47
|
+
click.echo(stdout, nl=False)
|
|
48
|
+
if stderr is not None:
|
|
49
|
+
click.echo(stderr, err=True)
|
|
50
|
+
|
|
51
|
+
sys.exit(exit_code)
|
|
@@ -115,7 +115,9 @@ def list_entitlements(ctx, opts, owner_repo, page, page_size, show_tokens, page_
|
|
|
115
115
|
|
|
116
116
|
click.secho("OK", fg="green", err=use_stderr)
|
|
117
117
|
|
|
118
|
-
print_entitlements(
|
|
118
|
+
print_entitlements(
|
|
119
|
+
opts=opts, data=entitlements_, page_info=page_info, page_all=page_all
|
|
120
|
+
)
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
@entitlements.command(name="list", aliases=["ls"])
|
|
@@ -126,7 +128,7 @@ def list_(*args, **kwargs): # pylint: disable=missing-docstring
|
|
|
126
128
|
return list_entitlements(*args, **kwargs)
|
|
127
129
|
|
|
128
130
|
|
|
129
|
-
def print_entitlements(opts, data, page_info=None, show_list_info=True):
|
|
131
|
+
def print_entitlements(opts, data, page_info=None, show_list_info=True, page_all=False):
|
|
130
132
|
"""Print entitlements as a table or output in another format."""
|
|
131
133
|
if utils.maybe_print_as_json(opts, data, page_info):
|
|
132
134
|
return
|
|
@@ -163,11 +165,16 @@ def print_entitlements(opts, data, page_info=None, show_list_info=True):
|
|
|
163
165
|
|
|
164
166
|
num_results = len(data)
|
|
165
167
|
list_suffix = "entitlement%s" % ("s" if num_results != 1 else "")
|
|
166
|
-
utils.pretty_print_list_info(
|
|
168
|
+
utils.pretty_print_list_info(
|
|
169
|
+
num_results=num_results,
|
|
170
|
+
page_info=None if page_all else page_info,
|
|
171
|
+
suffix=f"{list_suffix} retrieved" if page_all else f"{list_suffix} visible",
|
|
172
|
+
page_all=page_all,
|
|
173
|
+
)
|
|
167
174
|
|
|
168
175
|
|
|
169
176
|
def print_entitlements_with_restrictions(
|
|
170
|
-
opts, data, page_info=None, show_list_info=True
|
|
177
|
+
opts, data, page_info=None, show_list_info=True, page_all=False
|
|
171
178
|
):
|
|
172
179
|
# pylint: disable=too-many-locals
|
|
173
180
|
"""Print entitlements (with restrictions) as a table or output in another format."""
|
|
@@ -276,7 +283,12 @@ def print_entitlements_with_restrictions(
|
|
|
276
283
|
|
|
277
284
|
num_results = len(data)
|
|
278
285
|
list_suffix = "entitlement%s" % ("s" if num_results != 1 else "")
|
|
279
|
-
utils.pretty_print_list_info(
|
|
286
|
+
utils.pretty_print_list_info(
|
|
287
|
+
num_results=num_results,
|
|
288
|
+
page_info=None if page_all else page_info,
|
|
289
|
+
suffix=f"{list_suffix} retrieved" if page_all else f"{list_suffix} visible",
|
|
290
|
+
page_all=page_all,
|
|
291
|
+
)
|
|
280
292
|
|
|
281
293
|
|
|
282
294
|
@entitlements.command(aliases=["new"])
|
|
@@ -7,7 +7,7 @@ import click
|
|
|
7
7
|
import cloudsmith_api
|
|
8
8
|
|
|
9
9
|
from ...core import keyring
|
|
10
|
-
from .. import decorators, utils
|
|
10
|
+
from .. import decorators, utils, validators
|
|
11
11
|
from ..config import CredentialsReader
|
|
12
12
|
from .main import main
|
|
13
13
|
|
|
@@ -117,8 +117,11 @@ def logout(ctx, opts, api_host, keyring_only, config_only, dry_run):
|
|
|
117
117
|
"--keyring-only and --config-only are mutually exclusive."
|
|
118
118
|
)
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
api_host = (
|
|
121
|
+
validators.normalize_api_host(api_host)
|
|
122
|
+
or opts.api_host
|
|
123
|
+
or cloudsmith_api.Configuration().host
|
|
124
|
+
)
|
|
122
125
|
|
|
123
126
|
use_stderr = utils.should_use_stderr(opts)
|
|
124
127
|
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
"""Main command/entrypoint."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import json
|
|
4
6
|
import os
|
|
5
7
|
import shutil
|
|
6
8
|
import sys
|
|
7
9
|
import tempfile
|
|
8
10
|
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
9
12
|
|
|
10
13
|
import click
|
|
11
14
|
import json5
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
from ...core.mcp
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ...core.mcp import server
|
|
18
|
+
from ...core.mcp.data import OpenAPITool
|
|
19
|
+
|
|
15
20
|
from .. import command, decorators, utils
|
|
16
21
|
from .main import main
|
|
17
22
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# pylint: disable=too-many-lines
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import math
|
|
6
7
|
import os
|
|
7
8
|
import shlex
|
|
@@ -38,6 +39,7 @@ from ...core.api.packages import (
|
|
|
38
39
|
validate_create_package as api_validate_create_package,
|
|
39
40
|
)
|
|
40
41
|
from .. import command, decorators, utils, validators
|
|
42
|
+
from ..dsc_parser import resolve_dsc_files
|
|
41
43
|
from ..exceptions import handle_api_exceptions
|
|
42
44
|
from ..metadata_common import (
|
|
43
45
|
MetadataContentError,
|
|
@@ -75,6 +77,10 @@ METADATA_KWARG_NAMES = (
|
|
|
75
77
|
#: separately from the metadata payload kwargs so it does not leak into the
|
|
76
78
|
#: package-create API call.
|
|
77
79
|
METADATA_FAILURE_MODE_KWARG = "cli_metadata_failure_mode"
|
|
80
|
+
#: Filename suffix of a Debian source control file. A ``deb`` push whose
|
|
81
|
+
#: PACKAGE_FILE carries it is a source-package upload, so its members can be
|
|
82
|
+
#: derived without ``--dsc-file`` (GitHub issue #56).
|
|
83
|
+
DSC_SUFFIX = ".dsc"
|
|
78
84
|
|
|
79
85
|
|
|
80
86
|
def _metadata_failure_is_warn(opts=None):
|
|
@@ -689,7 +695,7 @@ def wait_for_package_sync(
|
|
|
689
695
|
# When using stderr for logs, avoid an interactive progress bar and just poll for status.
|
|
690
696
|
while True:
|
|
691
697
|
res = get_package_status(owner, repo, slug)
|
|
692
|
-
ok, failed, _,
|
|
698
|
+
ok, failed, _, status_str, stage_str, reason = res
|
|
693
699
|
if ok or failed:
|
|
694
700
|
break
|
|
695
701
|
|
|
@@ -806,6 +812,34 @@ def wait_for_package_sync(
|
|
|
806
812
|
attempts=attempts,
|
|
807
813
|
)
|
|
808
814
|
else:
|
|
815
|
+
if use_stderr:
|
|
816
|
+
# In JSON output mode the human-readable text above went to
|
|
817
|
+
# stderr, so stdout still needs a machine-readable envelope.
|
|
818
|
+
# Mirror the shape produced by ``handle_api_exceptions`` so a
|
|
819
|
+
# sync failure is parseable the same way as an API failure.
|
|
820
|
+
error_data = {
|
|
821
|
+
"detail": reason or "Package failed to synchronise.",
|
|
822
|
+
"help": {
|
|
823
|
+
"context": context_msg,
|
|
824
|
+
"hint": None,
|
|
825
|
+
},
|
|
826
|
+
"meta": {
|
|
827
|
+
"status": status_str,
|
|
828
|
+
"stage": stage_str or "Unknown",
|
|
829
|
+
"seconds": seconds,
|
|
830
|
+
},
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
metadata_context = getattr(opts, "push_metadata_info", None)
|
|
834
|
+
if metadata_context is not None:
|
|
835
|
+
error_data["metadata_attachment"] = metadata_context
|
|
836
|
+
|
|
837
|
+
click.echo(
|
|
838
|
+
json.dumps(
|
|
839
|
+
error_data, indent=4 if opts.output == "pretty_json" else None
|
|
840
|
+
)
|
|
841
|
+
)
|
|
842
|
+
|
|
809
843
|
ctx.exit(1)
|
|
810
844
|
|
|
811
845
|
|
|
@@ -1019,6 +1053,13 @@ def upload_files_and_create_package(
|
|
|
1019
1053
|
return slug_perm, slug
|
|
1020
1054
|
|
|
1021
1055
|
|
|
1056
|
+
def _implied_dsc_file(package_file):
|
|
1057
|
+
"""Return ``package_file`` if it is itself a Debian ``.dsc``, else None."""
|
|
1058
|
+
if isinstance(package_file, str) and package_file.endswith(DSC_SUFFIX):
|
|
1059
|
+
return package_file
|
|
1060
|
+
return None
|
|
1061
|
+
|
|
1062
|
+
|
|
1022
1063
|
def create_push_handlers():
|
|
1023
1064
|
"""Create a handler for upload per package format."""
|
|
1024
1065
|
# pylint: disable=fixme
|
|
@@ -1181,6 +1222,38 @@ def create_push_handlers():
|
|
|
1181
1222
|
parameters = context.get(ctx.info_name)
|
|
1182
1223
|
kwargs["package_type"] = ctx.info_name
|
|
1183
1224
|
|
|
1225
|
+
# deb-only: derive the upstream/native source archive and Debian
|
|
1226
|
+
# packaging archive from a .dsc (see GitHub issue #56). Only the
|
|
1227
|
+
# `deb` subcommand ever registers --dsc-file (below), so this is
|
|
1228
|
+
# a no-op kwargs.pop() for every other format.
|
|
1229
|
+
dsc_file = kwargs.pop("dsc_file", None)
|
|
1230
|
+
if dsc_file is None and not kwargs.get("sources_file"):
|
|
1231
|
+
# Pushing a Debian source package means passing the .dsc as
|
|
1232
|
+
# PACKAGE_FILE, and the API rejects it without a sources
|
|
1233
|
+
# archive, so parse it by default rather than making the user
|
|
1234
|
+
# name the same file twice. An explicit --sources-file means
|
|
1235
|
+
# the caller is driving the members manually; leave them to it.
|
|
1236
|
+
dsc_file = _implied_dsc_file(kwargs.get("package_file"))
|
|
1237
|
+
if dsc_file:
|
|
1238
|
+
resolved_dsc = resolve_dsc_files(dsc_file)
|
|
1239
|
+
if resolved_dsc.ignored_files:
|
|
1240
|
+
click.secho(
|
|
1241
|
+
"Not uploading {files}: the deb package format has no "
|
|
1242
|
+
"field for detached upstream signatures.".format(
|
|
1243
|
+
files=", ".join(resolved_dsc.ignored_files)
|
|
1244
|
+
),
|
|
1245
|
+
fg="yellow",
|
|
1246
|
+
err=utils.should_use_stderr(opts),
|
|
1247
|
+
)
|
|
1248
|
+
# Precedence: explicit --sources-file/--changes-file always
|
|
1249
|
+
# win over values derived from --dsc-file. A user who passes
|
|
1250
|
+
# both wants a manual override, not to have their explicit
|
|
1251
|
+
# flag silently clobbered.
|
|
1252
|
+
if not kwargs.get("sources_file"):
|
|
1253
|
+
kwargs["sources_file"] = resolved_dsc.sources_file
|
|
1254
|
+
if not kwargs.get("changes_file"):
|
|
1255
|
+
kwargs["changes_file"] = resolved_dsc.changes_file
|
|
1256
|
+
|
|
1184
1257
|
owner_repo = kwargs.pop("owner_repo")
|
|
1185
1258
|
if "distribution" in parameters:
|
|
1186
1259
|
kwargs["distribution"] = "/".join(owner_repo[2:])
|
|
@@ -1307,6 +1380,39 @@ def create_push_handlers():
|
|
|
1307
1380
|
)
|
|
1308
1381
|
push_handler = decorator(push_handler)
|
|
1309
1382
|
|
|
1383
|
+
if key == "deb":
|
|
1384
|
+
# deb-only shim (GitHub issue #56): the generic per-format loop
|
|
1385
|
+
# above is driven entirely by the API's package-format model, so
|
|
1386
|
+
# this stays layered on top rather than becoming a new branch in
|
|
1387
|
+
# that generic system.
|
|
1388
|
+
push_handler = click.option(
|
|
1389
|
+
"--dsc-file",
|
|
1390
|
+
"dsc_file",
|
|
1391
|
+
type=ExpandPath(
|
|
1392
|
+
dir_okay=False, exists=True, writable=False, resolve_path=True
|
|
1393
|
+
),
|
|
1394
|
+
default=None,
|
|
1395
|
+
help=(
|
|
1396
|
+
"Path to a Debian .dsc control file, plain or "
|
|
1397
|
+
"OpenPGP-clearsigned. Only needed to point at a .dsc "
|
|
1398
|
+
"other than PACKAGE_FILE: pushing a source package means "
|
|
1399
|
+
"passing the .dsc as PACKAGE_FILE, and that is parsed "
|
|
1400
|
+
"automatically unless --sources-file is given. Its "
|
|
1401
|
+
"'Checksums-Sha256:' (or 'Files:') field supplies "
|
|
1402
|
+
"--sources-file with the upstream/native source archive "
|
|
1403
|
+
"and --changes-file with the Debian packaging archive "
|
|
1404
|
+
"(.debian.tar.* or .diff.gz). Supports the 1.0, 2.0, "
|
|
1405
|
+
"3.0 (native) and 3.0 (quilt) source formats, and the "
|
|
1406
|
+
"referenced files must sit next to the .dsc. An explicit "
|
|
1407
|
+
"--sources-file or --changes-file always takes precedence "
|
|
1408
|
+
"over the derived value for that field. Detached upstream "
|
|
1409
|
+
"signatures (*.asc) are skipped with a warning; a "
|
|
1410
|
+
"multi-component source package (*.orig-*.tar.*) is "
|
|
1411
|
+
"rejected, because the deb package format has no field "
|
|
1412
|
+
"for the extra components."
|
|
1413
|
+
),
|
|
1414
|
+
)(push_handler)
|
|
1415
|
+
|
|
1310
1416
|
handlers[key] = push_handler
|
|
1311
1417
|
|
|
1312
1418
|
|
|
@@ -341,7 +341,7 @@ class Options: # pylint: disable=too-many-public-methods
|
|
|
341
341
|
@api_host.setter
|
|
342
342
|
def api_host(self, value):
|
|
343
343
|
"""Set value for API host."""
|
|
344
|
-
self._set_option("api_host", value)
|
|
344
|
+
self._set_option("api_host", validators.normalize_api_host(value))
|
|
345
345
|
|
|
346
346
|
@property
|
|
347
347
|
def api_key(self):
|