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.
Files changed (150) hide show
  1. {cloudsmith_cli-1.23.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.25.0}/PKG-INFO +18 -4
  2. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/README.md +15 -1
  3. cloudsmith_cli-1.25.0/cloudsmith_cli/__main__.py +15 -0
  4. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/auth.py +41 -4
  5. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +8 -0
  6. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +24 -9
  7. cloudsmith_cli-1.25.0/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +51 -0
  8. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/entitlements.py +17 -5
  9. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/logout.py +6 -3
  10. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/mcp.py +7 -2
  11. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/push.py +107 -1
  12. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/upstream.py +1 -0
  13. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/config.py +1 -1
  14. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/decorators.py +29 -3
  15. cloudsmith_cli-1.25.0/cloudsmith_cli/cli/dsc_parser.py +268 -0
  16. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/exceptions.py +3 -2
  17. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/saml.py +25 -24
  18. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/validators.py +23 -0
  19. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/webserver.py +62 -8
  20. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +33 -1
  21. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/keyring.py +68 -0
  22. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/generic.py +16 -0
  23. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/launchers.py +19 -9
  24. cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/__init__.py +4 -0
  25. cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/installer.py +264 -0
  26. cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/rc.py +185 -0
  27. cloudsmith_cli-1.25.0/cloudsmith_cli/credential_helpers/pnpm/runtime.py +68 -0
  28. cloudsmith_cli-1.25.0/cloudsmith_cli/data/VERSION +1 -0
  29. cloudsmith_cli-1.25.0/cloudsmith_cli/templates/auth_two_factor.html +38 -0
  30. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0/cloudsmith_cli.egg-info}/PKG-INFO +18 -4
  31. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/SOURCES.txt +8 -1
  32. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/requires.txt +2 -2
  33. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/pyproject.toml +5 -3
  34. cloudsmith_cli-1.23.0/cloudsmith_cli/__main__.py +0 -8
  35. cloudsmith_cli-1.23.0/cloudsmith_cli/data/VERSION +0 -1
  36. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/LICENSE +0 -0
  37. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/MANIFEST.in +0 -0
  38. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/__init__.py +0 -0
  39. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/__init__.py +0 -0
  40. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/command.py +0 -0
  41. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/__init__.py +0 -0
  42. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/check.py +0 -0
  43. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
  44. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +0 -0
  45. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/credential_helper/generic.py +0 -0
  46. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
  47. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
  48. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
  49. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/domains.py +0 -0
  50. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/download.py +0 -0
  51. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
  52. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/list_.py +0 -0
  53. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/login.py +0 -0
  54. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/main.py +0 -0
  55. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metadata.py +0 -0
  56. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
  57. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
  58. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
  59. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
  60. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/move.py +0 -0
  61. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
  62. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
  63. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
  64. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
  65. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
  66. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
  67. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
  68. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
  69. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
  70. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
  71. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
  72. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
  73. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/status.py +0 -0
  74. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
  75. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
  76. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
  77. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/commands/whoami.py +0 -0
  78. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
  79. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/table.py +0 -0
  80. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/types.py +0 -0
  81. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/cli/utils.py +0 -0
  82. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/conftest.py +0 -0
  83. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/__init__.py +0 -0
  84. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/__init__.py +0 -0
  85. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/distros.py +0 -0
  86. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
  87. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
  88. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/files.py +0 -0
  89. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/init.py +0 -0
  90. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/metadata.py +0 -0
  91. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/metrics.py +0 -0
  92. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/orgs.py +0 -0
  93. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/packages.py +0 -0
  94. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/quota.py +0 -0
  95. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/rates.py +0 -0
  96. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/repos.py +0 -0
  97. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/status.py +0 -0
  98. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
  99. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/user.py +0 -0
  100. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/version.py +0 -0
  101. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/api/vulnerabilities.py +0 -0
  102. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/cache_utils.py +0 -0
  103. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/config.py +0 -0
  104. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
  105. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
  106. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/models.py +0 -0
  107. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
  108. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
  109. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
  110. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
  111. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
  112. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
  113. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
  114. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
  115. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
  116. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
  117. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
  118. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
  119. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
  120. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
  121. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
  122. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
  123. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -0
  124. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +0 -0
  125. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/download.py +0 -0
  126. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
  127. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/data.py +0 -0
  128. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/mcp/server.py +0 -0
  129. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/pagination.py +0 -0
  130. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/ratelimits.py +0 -0
  131. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/rest.py +0 -0
  132. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/utils.py +0 -0
  133. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/core/version.py +0 -0
  134. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
  135. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
  136. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/common.py +0 -0
  137. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/custom_domains.py +0 -0
  138. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/default_domains.py +0 -0
  139. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
  140. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/installer.py +0 -0
  141. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
  142. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/data/config.ini +0 -0
  143. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/data/credentials.ini +0 -0
  144. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/__init__.py +0 -0
  145. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/auth_error.html +0 -0
  146. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli/templates/auth_success.html +0 -0
  147. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
  148. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
  149. {cloudsmith_cli-1.23.0 → cloudsmith_cli-1.25.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
  150. {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.23.0
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.1.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.29
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
- Opening your organization's SAML IDP URL in your browser: https://example.com/some-saml-idp
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
- Opening your organization's SAML IDP URL in your browser: https://example.com/some-saml-idp
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"Opening your organization's SAML IDP URL in your browser: {click.style(idp_url, bold=True)}",
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
- webbrowser.open(idp_url)
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, opts, owner, token, force, save_config, json, request_api_key_flag
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
- DockerInstaller
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 Docker domains.",
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 Docker credential helper
119
- $ cloudsmith credential-helper install docker
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 docker --domain my.registry.example.com
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 docker --dry-run
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 docker --no-discover
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
- return
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(opts=opts, data=entitlements_, page_info=page_info)
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(num_results=num_results, suffix=list_suffix)
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(num_results=num_results, suffix=list_suffix)
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
- if api_host is None:
121
- api_host = opts.api_host or cloudsmith_api.Configuration().host
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
- from ...core.mcp import server
14
- from ...core.mcp.data import OpenAPITool
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, _, _, _, _ = res
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
 
@@ -31,6 +31,7 @@ UPSTREAM_FORMATS = [
31
31
  "hex",
32
32
  "huggingface",
33
33
  "maven",
34
+ "nix",
34
35
  "npm",
35
36
  "nuget",
36
37
  "python",
@@ -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):