cloudsmith-cli 1.27.0__tar.gz → 1.28.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. {cloudsmith_cli-1.27.0/cloudsmith_cli.egg-info → cloudsmith_cli-1.28.0}/PKG-INFO +4 -3
  2. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/__init__.py +6 -4
  3. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/docker.py +8 -1
  4. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/terraform.py +4 -2
  5. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/domains.py +1 -0
  6. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/entitlements.py +9 -8
  7. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/list_.py +1 -0
  8. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/main.py +11 -1
  9. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/mcp.py +7 -5
  10. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metadata.py +1 -0
  11. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/registry.py +2 -0
  12. cloudsmith_cli-1.28.0/cloudsmith_cli/cli/commands/update.py +170 -0
  13. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/config.py +13 -0
  14. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/utils.py +8 -3
  15. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/webserver.py +12 -7
  16. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/download.py +2 -1
  17. cloudsmith_cli-1.28.0/cloudsmith_cli/core/installation.py +156 -0
  18. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/keyring.py +45 -6
  19. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/server.py +108 -15
  20. cloudsmith_cli-1.28.0/cloudsmith_cli/core/self_update.py +429 -0
  21. cloudsmith_cli-1.28.0/cloudsmith_cli/core/update_check.py +474 -0
  22. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/utils.py +15 -0
  23. cloudsmith_cli-1.28.0/cloudsmith_cli/data/VERSION +1 -0
  24. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/data/config.ini +3 -0
  25. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0/cloudsmith_cli.egg-info}/PKG-INFO +4 -3
  26. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/SOURCES.txt +4 -0
  27. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/requires.txt +3 -2
  28. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/pyproject.toml +15 -12
  29. cloudsmith_cli-1.27.0/cloudsmith_cli/data/VERSION +0 -1
  30. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/LICENSE +0 -0
  31. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/MANIFEST.in +0 -0
  32. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/README.md +0 -0
  33. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/__init__.py +0 -0
  34. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/__main__.py +0 -0
  35. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/__init__.py +0 -0
  36. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/command.py +0 -0
  37. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/__init__.py +0 -0
  38. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/auth.py +0 -0
  39. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/check.py +0 -0
  40. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/copy.py +0 -0
  41. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/cargo.py +0 -0
  42. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/generic.py +0 -0
  43. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/manage.py +0 -0
  44. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/credential_helper/pnpm.py +0 -0
  45. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/delete.py +0 -0
  46. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/dependencies.py +0 -0
  47. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/docs.py +0 -0
  48. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/download.py +0 -0
  49. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/help_.py +0 -0
  50. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/login.py +0 -0
  51. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/logout.py +0 -0
  52. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/__init__.py +0 -0
  53. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/command.py +0 -0
  54. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/entitlements.py +0 -0
  55. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/metrics/packages.py +0 -0
  56. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/move.py +0 -0
  57. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/__init__.py +0 -0
  58. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/command.py +0 -0
  59. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/deny.py +0 -0
  60. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/license.py +0 -0
  61. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/policy/vulnerability.py +0 -0
  62. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/push.py +0 -0
  63. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quarantine.py +0 -0
  64. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/__init__.py +0 -0
  65. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/command.py +0 -0
  66. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/history.py +0 -0
  67. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/quota/quota.py +0 -0
  68. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/repos.py +0 -0
  69. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/resync.py +0 -0
  70. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/status.py +0 -0
  71. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/tags.py +0 -0
  72. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/tokens.py +0 -0
  73. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/upstream.py +0 -0
  74. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/vulnerabilities.py +0 -0
  75. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/commands/whoami.py +0 -0
  76. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/decorators.py +0 -0
  77. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/dsc_parser.py +0 -0
  78. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/exceptions.py +0 -0
  79. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/metadata_common.py +0 -0
  80. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/saml.py +0 -0
  81. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/table.py +0 -0
  82. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/types.py +0 -0
  83. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/cli/validators.py +0 -0
  84. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/conftest.py +0 -0
  85. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/__init__.py +0 -0
  86. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/__init__.py +0 -0
  87. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/distros.py +0 -0
  88. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/entitlements.py +0 -0
  89. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/exceptions.py +0 -0
  90. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/files.py +0 -0
  91. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/init.py +0 -0
  92. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/metadata.py +0 -0
  93. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/metrics.py +0 -0
  94. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/orgs.py +0 -0
  95. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/packages.py +0 -0
  96. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/quota.py +0 -0
  97. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/rates.py +0 -0
  98. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/repos.py +0 -0
  99. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/status.py +0 -0
  100. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/upstreams.py +0 -0
  101. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/user.py +0 -0
  102. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/version.py +0 -0
  103. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/api/vulnerabilities.py +0 -0
  104. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/cache_utils.py +0 -0
  105. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/config.py +0 -0
  106. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/__init__.py +0 -0
  107. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/chain.py +0 -0
  108. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/models.py +0 -0
  109. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/__init__.py +0 -0
  110. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/cache.py +0 -0
  111. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +0 -0
  112. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/aws.py +0 -0
  113. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +0 -0
  114. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/base.py +0 -0
  115. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +0 -0
  116. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +0 -0
  117. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/generic.py +0 -0
  118. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +0 -0
  119. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +0 -0
  120. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/oidc/exchange.py +0 -0
  121. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/provider.py +0 -0
  122. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/__init__.py +0 -0
  123. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/cli_flag.py +0 -0
  124. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/credentials_file.py +0 -0
  125. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/env_var.py +0 -0
  126. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/keyring_provider.py +0 -0
  127. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/credentials/providers/oidc_provider.py +0 -0
  128. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/macos_keychain.py +0 -0
  129. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/__init__.py +0 -0
  130. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/mcp/data.py +0 -0
  131. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/pagination.py +0 -0
  132. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/ratelimits.py +0 -0
  133. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/rest.py +0 -0
  134. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/session.py +0 -0
  135. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/sso.py +0 -0
  136. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/core/version.py +0 -0
  137. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/__init__.py +0 -0
  138. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/backends.py +0 -0
  139. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/__init__.py +0 -0
  140. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/installer.py +0 -0
  141. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/cargo/runtime.py +0 -0
  142. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/common.py +0 -0
  143. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/custom_domains.py +0 -0
  144. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/default_domains.py +0 -0
  145. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/__init__.py +0 -0
  146. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/installer.py +0 -0
  147. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/docker/runtime.py +0 -0
  148. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/generic.py +0 -0
  149. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/launchers.py +0 -0
  150. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/__init__.py +0 -0
  151. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/installer.py +0 -0
  152. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/rc.py +0 -0
  153. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/pnpm/runtime.py +0 -0
  154. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/terraform/__init__.py +0 -0
  155. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/terraform/installer.py +0 -0
  156. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/terraform/runtime.py +0 -0
  157. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/credential_helpers/terraform/terraformrc.py +0 -0
  158. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/data/credentials.ini +0 -0
  159. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/__init__.py +0 -0
  160. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_error.html +0 -0
  161. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_success.html +0 -0
  162. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/templates/auth_two_factor.html +0 -0
  163. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli/wrapper.py +0 -0
  164. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/dependency_links.txt +0 -0
  165. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/entry_points.txt +0 -0
  166. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/cloudsmith_cli.egg-info/top_level.txt +0 -0
  167. {cloudsmith_cli-1.27.0 → cloudsmith_cli-1.28.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsmith-cli
3
- Version: 1.27.0
3
+ Version: 1.28.0
4
4
  Summary: Cloudsmith Command-Line Interface (CLI)
5
5
  Author-email: Cloudsmith Ltd <support@cloudsmith.io>
6
6
  License-Expression: Apache-2.0
@@ -23,6 +23,7 @@ Classifier: Topic :: Utilities
23
23
  Requires-Python: >=3.10.0
24
24
  Description-Content-Type: text/markdown
25
25
  License-File: LICENSE
26
+ Requires-Dist: certifi>=2024.7.4
26
27
  Requires-Dist: click!=8.3.0,>=8.2
27
28
  Requires-Dist: click-configfile>=0.2.3
28
29
  Requires-Dist: click-didyoumean>=0.0.3
@@ -33,10 +34,10 @@ Requires-Dist: keyring>=25.4.1
33
34
  Requires-Dist: keyrings-alt>=5.0.2
34
35
  Requires-Dist: keyrings-cryptfile>=1.3.9
35
36
  Requires-Dist: cryptography>=50.0.0
36
- Requires-Dist: mcp==1.28.1
37
+ Requires-Dist: mcp==2.2.0
37
38
  Requires-Dist: pydantic-settings>=2.14.2
38
39
  Requires-Dist: PyJWT[crypto]>=2.13.0
39
- Requires-Dist: python-toon==0.1.2
40
+ Requires-Dist: python-toon==0.1.3
40
41
  Requires-Dist: requests>=2.18.4
41
42
  Requires-Dist: requests_toolbelt>=1.0.0
42
43
  Requires-Dist: rich>=13.0.0
@@ -27,18 +27,20 @@ def credential_helper():
27
27
  manager automatically, or run the runtime command directly for debugging.
28
28
 
29
29
  Examples:
30
+
31
+ \b
30
32
  # Install Docker credential helper
31
33
  $ cloudsmith credential-helper install docker
32
-
34
+ \b
33
35
  # Install pnpm credential helper
34
36
  $ cloudsmith credential-helper install pnpm
35
-
37
+ \b
36
38
  # Install cargo credential helper
37
39
  $ cloudsmith credential-helper install cargo
38
-
40
+ \b
39
41
  # Test Docker credential helper directly
40
42
  $ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
41
-
43
+ \b
42
44
  # Test pnpm credential helper directly
43
45
  $ cloudsmith credential-helper pnpm npm.cloudsmith.io
44
46
  """
@@ -33,23 +33,30 @@ def docker(opts, operation):
33
33
  ``workspace`` in ``config.ini``; legacy aliases are also accepted - and a
34
34
  valid API key/token).
35
35
 
36
+ \b
36
37
  Input (stdin):
37
38
  Server URL as plain text (e.g. "docker.cloudsmith.io")
38
39
 
40
+ \b
39
41
  Output (stdout):
40
42
  JSON: {"Username": "token", "Secret": "<cloudsmith-token>"}
41
43
 
44
+ \b
42
45
  Exit codes:
43
46
  0: Success
44
47
  1: Error (no credentials available, not a Cloudsmith registry, etc.)
45
48
 
49
+ \b
46
50
  Examples:
51
+
52
+ \b
47
53
  # Manual testing
48
54
  $ echo "docker.cloudsmith.io" | cloudsmith credential-helper docker
49
-
55
+ \b
50
56
  # Called by Docker via launcher
51
57
  $ echo "docker.cloudsmith.io" | docker-credential-cloudsmith get
52
58
 
59
+ \b
53
60
  Environment variables:
54
61
  CLOUDSMITH_API_KEY: API key for authentication (optional)
55
62
  CLOUDSMITH_WORKSPACE: Workspace slug (CLOUDSMITH_ORG is also accepted)
@@ -92,13 +92,15 @@ def terraform(opts, repo, params):
92
92
 
93
93
  \b
94
94
  Examples:
95
+
96
+ \b
95
97
  # Direct usage
96
98
  $ cloudsmith credential-helper terraform --repo my-repo terraform.cloudsmith.io
97
99
  {"token": "..."}
98
-
100
+ \b
99
101
  # Terraform's calling convention (verb + hostname)
100
102
  $ cloudsmith credential-helper terraform --repo my-repo get terraform.cloudsmith.io
101
-
103
+ \b
102
104
  # Select an org and profile explicitly (no env vars needed)
103
105
  $ cloudsmith credential-helper terraform --org=acme -P ci get terraform.cloudsmith.io
104
106
 
@@ -162,6 +162,7 @@ def list_domains( # pylint: disable=too-many-arguments
162
162
  paginated endpoint, so there is no benefit to hiding entries by default.
163
163
  Passing --page or --page-size switches to paged output.
164
164
 
165
+ \b
165
166
  Output (stdout):
166
167
  JSON: {"version": 1, "domains": [{"host": ..., "format": ...,
167
168
  "type": ..., "domain_type": ..., "workspace": ..., "repository": ...,
@@ -775,14 +775,15 @@ def restrict(
775
775
 
776
776
  Full CLI example:
777
777
 
778
- $ cloudsmith entitlements restrict cloudsmith/testing-private/9xGSdAxlIqIV \
779
- --limit-bandwidth=1 \
780
- --limit-bandwidth-unit=gigabyte \
781
- --limit-num-clients=10 \
782
- --limit-num-downloads=1000 \
783
- --limit-package-query="package-darwin-amd64 OR package-windows-amd64 OR package-linux-amd64" \
784
- --limit-path-query=tag:latest \
785
- --limit-date-range-from=2020-01-01T00:00:00Z \
778
+ \b
779
+ $ cloudsmith entitlements restrict cloudsmith/testing-private/9xGSdAxlIqIV \\
780
+ --limit-bandwidth=1 \\
781
+ --limit-bandwidth-unit=gigabyte \\
782
+ --limit-num-clients=10 \\
783
+ --limit-num-downloads=1000 \\
784
+ --limit-package-query="package-darwin-amd64 OR package-windows-amd64 OR package-linux-amd64" \\
785
+ --limit-path-query=tag:latest \\
786
+ --limit-date-range-from=2020-01-01T00:00:00Z \\
786
787
  --limit-date-range-to=2077-01-01T00:00:00Z
787
788
  """
788
789
  owner, repo, identifier = owner_repo_identifier
@@ -195,6 +195,7 @@ def packages(ctx, opts, owner_repo, page, page_size, query, sort, page_all):
195
195
 
196
196
  --query 'name:^foo$ filename:.zip$ architecture:~x86'
197
197
 
198
+ \b
198
199
  You can sort the results using --sort with these fields:
199
200
  - date/-date: Sort by creation date
200
201
  - downloads/-downloads: Sort by download count
@@ -2,6 +2,7 @@
2
2
 
3
3
  import click
4
4
 
5
+ from ...core import update_check
5
6
  from ...core.api.version import get_version as get_api_version
6
7
  from ...core.utils import get_github_website, get_help_website
7
8
  from ...core.version import get_version as get_cli_version
@@ -56,13 +57,22 @@ For issues/contributing: {get_github_website()}
56
57
  is_flag=True,
57
58
  is_eager=True,
58
59
  )
60
+ @click.option(
61
+ "--no-check-update",
62
+ is_flag=True,
63
+ default=False,
64
+ envvar="CLOUDSMITH_NO_UPDATE_CHECK",
65
+ help="Disable the once-a-day check for a newer version of the CLI.",
66
+ )
59
67
  @decorators.common_cli_config_options
60
68
  @decorators.common_cli_output_options
61
69
  @click.pass_context
62
- def main(ctx, opts, version):
70
+ def main(ctx, opts, version, no_check_update):
63
71
  """Handle entrypoint to CLI."""
64
72
  # pylint: disable=unused-argument
65
73
 
74
+ update_check.arm(ctx, opts, no_check_update)
75
+
66
76
  if version:
67
77
  print_version(opts)
68
78
  elif ctx.invoked_subcommand is None:
@@ -200,6 +200,7 @@ def configure(ctx, opts, client, is_global): # pylint: disable=unused-argument
200
200
  """
201
201
  Configure the Cloudsmith MCP server for supported clients.
202
202
 
203
+ \b
203
204
  This command automatically adds the Cloudsmith MCP server configuration
204
205
  to the specified client's configuration file. Supported clients are:
205
206
  - Claude Desktop
@@ -211,11 +212,12 @@ def configure(ctx, opts, client, is_global): # pylint: disable=unused-argument
211
212
  For Claude Code, --global edits ~/.claude.json (user scope) and
212
213
  --local writes ./.mcp.json (project scope, intended to be committed).
213
214
 
214
- Examples:\n
215
- cloudsmith mcp configure --client claude\n
216
- cloudsmith mcp configure --client claude-code\n
217
- cloudsmith mcp configure --client cursor --local\n
218
- cloudsmith mcp configure --client gemini-cli\n
215
+ \b
216
+ Examples:
217
+ cloudsmith mcp configure --client claude
218
+ cloudsmith mcp configure --client claude-code
219
+ cloudsmith mcp configure --client cursor --local
220
+ cloudsmith mcp configure --client gemini-cli
219
221
  cloudsmith mcp configure # Auto-detect and configure all
220
222
  """
221
223
 
@@ -463,6 +463,7 @@ def remove_metadata(ctx, opts, owner_repo_package, metadata_slug_perm, yes):
463
463
  """
464
464
  Remove package metadata.
465
465
 
466
+ \b
466
467
  OWNER/REPO/PACKAGE: target package.
467
468
  METADATA_SLUG_PERM: permanent slug for the metadata entry.
468
469
 
@@ -37,6 +37,7 @@ LAZY_COMMANDS = {
37
37
  "status": f"{_PACKAGE}.status",
38
38
  "tags": f"{_PACKAGE}.tags",
39
39
  "tokens": f"{_PACKAGE}.tokens",
40
+ "update": f"{_PACKAGE}.update",
40
41
  "upstream": f"{_PACKAGE}.upstream",
41
42
  "vulnerabilities": f"{_PACKAGE}.vulnerabilities",
42
43
  "whoami": f"{_PACKAGE}.whoami",
@@ -56,4 +57,5 @@ LAZY_ALIASES = {
56
57
  "quarantine": ["block"],
57
58
  "repositories": ["repos"],
58
59
  "tags": ["tag"],
60
+ "update": ["upgrade"],
59
61
  }
@@ -0,0 +1,170 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """CLI/Commands - Update the CLI to the latest released version."""
3
+
4
+ import click
5
+
6
+ from ...core import installation, self_update, update_check
7
+ from ...core.session import create_requests_session
8
+ from ...core.version import get_version, parse_version
9
+ from .. import decorators, utils
10
+ from ..utils import maybe_spinner
11
+ from .main import main
12
+
13
+
14
+ def _fetch_manifest(opts, session, target):
15
+ import requests
16
+
17
+ # A non-standalone channel on a platform without a recognised standalone
18
+ # target has ``target is None``; the manifest is still fetched only to read
19
+ # the latest *version* (the channel prints its own upgrade command), so fall
20
+ # back to a known target rather than building a ``manifest-None`` URL. The
21
+ # standalone path guards ``target is None`` before reaching here.
22
+ target = target or update_check.VERSION_PROBE_TARGET
23
+ try:
24
+ with maybe_spinner(opts):
25
+ return update_check.fetch_latest_manifest(session, target=target)
26
+ except (requests.RequestException, ValueError) as exc:
27
+ raise click.ClickException(f"Failed to fetch the latest version: {exc}")
28
+
29
+
30
+ def _is_up_to_date(latest, current):
31
+ try:
32
+ return parse_version(latest) <= parse_version(current)
33
+ except ValueError as exc:
34
+ raise click.ClickException(f"Cannot compare versions: {exc}")
35
+
36
+
37
+ def _confirm_self_update(opts, current, latest, assume_yes):
38
+ """Return True to proceed with the self-update.
39
+
40
+ Auto-confirms when ``--yes`` is given, when output is machine-readable, or
41
+ when stderr is not a terminal (so scripted/piped invocations do not hang on
42
+ a prompt).
43
+ """
44
+ if assume_yes or utils.should_use_stderr(opts) or not update_check.stderr_is_tty():
45
+ return True
46
+ return click.confirm(
47
+ f"Update the Cloudsmith CLI from {current} to {latest}?", default=True
48
+ )
49
+
50
+
51
+ def _run_self_update(opts, session, manifest, data):
52
+ import requests
53
+
54
+ use_stderr = utils.should_use_stderr(opts)
55
+ click.echo(
56
+ f"Downloading and installing version {manifest['version']} ... ",
57
+ nl=False,
58
+ err=use_stderr,
59
+ )
60
+ try:
61
+ with maybe_spinner(opts):
62
+ self_update.perform_self_update(manifest, session=session)
63
+ except (self_update.SelfUpdateError, requests.RequestException, OSError) as exc:
64
+ click.secho("ERROR", fg="red", err=use_stderr)
65
+ raise click.ClickException(str(exc))
66
+ click.secho("OK", fg="green", err=use_stderr)
67
+ data["upgraded"] = True
68
+ if utils.maybe_print_as_json(opts, data):
69
+ return
70
+ click.echo(f"The Cloudsmith CLI is now at version {manifest['version']}.")
71
+
72
+
73
+ def _print_manual_update(opts, current, latest, manifest, data):
74
+ """Report a manual-update path when self-update cannot run (Windows).
75
+
76
+ The command already holds the host manifest, so it shows the exact archive
77
+ URL and checksum rather than a vague pointer; it exits 0 so scripts do not
78
+ treat "update available, self-update unsupported" as a failure.
79
+ """
80
+ changelog = installation.changelog_url(latest)
81
+ data["outcome"] = "manual"
82
+ data["download_url"] = manifest.get("url")
83
+ data["sha256"] = manifest.get("sha256")
84
+ data["archive"] = manifest.get("archive")
85
+ data["changelog_url"] = changelog
86
+ if utils.maybe_print_as_json(opts, data):
87
+ return
88
+ click.echo(
89
+ f"A new version of the Cloudsmith CLI is available: {current} \u2192 {latest}"
90
+ )
91
+ click.echo(f"See what's changed: {changelog}")
92
+ click.echo("Self-update is not supported for the standalone binary on Windows.")
93
+ click.echo(
94
+ "Download and extract this archive, then replace your install directory:"
95
+ )
96
+ click.echo(f" URL: {manifest['url']}")
97
+ click.echo(f" SHA256: {manifest['sha256']}")
98
+
99
+
100
+ @main.command(aliases=["upgrade"])
101
+ @click.option(
102
+ "-y",
103
+ "--yes",
104
+ "assume_yes",
105
+ is_flag=True,
106
+ default=False,
107
+ help="Do not prompt for confirmation before installing an update.",
108
+ )
109
+ @decorators.common_cli_config_options
110
+ @decorators.common_cli_output_options
111
+ @click.pass_context
112
+ def update(ctx, opts, assume_yes):
113
+ """Update the Cloudsmith CLI to the latest released version.
114
+
115
+ The check runs on every invocation, independent of the once-a-day
116
+ background version check. A standalone binary downloads and installs the
117
+ latest release in place; every other install channel is told the correct
118
+ upgrade command for that channel.
119
+ """
120
+ # pylint: disable=unused-argument
121
+ current = get_version()
122
+ channel = installation.detect_channel()
123
+ target = installation.detect_target()
124
+ if channel == installation.CHANNEL_STANDALONE and target is None:
125
+ raise click.ClickException(
126
+ "Cannot detect a supported platform for the standalone binary."
127
+ )
128
+
129
+ session = create_requests_session(user_agent=opts.api_user_agent)
130
+ manifest = _fetch_manifest(opts, session, target)
131
+ latest = manifest["version"]
132
+
133
+ # Stamp both timestamps regardless of the outcome: the user has just run an
134
+ # explicit check, so the background notice's daily re-nag is disarmed.
135
+ update_check.record_checked_and_notified(latest)
136
+
137
+ data = {"current_version": current, "latest_version": latest, "channel": channel}
138
+
139
+ if _is_up_to_date(latest, current):
140
+ data["up_to_date"] = True
141
+ if not utils.maybe_print_as_json(opts, data):
142
+ click.echo(f"The Cloudsmith CLI is up to date (version {current}).")
143
+ return
144
+
145
+ instruction = installation.upgrade_instruction(channel)
146
+ if instruction is not None:
147
+ changelog = installation.changelog_url(latest)
148
+ data["upgrade_command"] = instruction
149
+ data["changelog_url"] = changelog
150
+ if utils.maybe_print_as_json(opts, data):
151
+ return
152
+ click.echo(
153
+ f"A new version of the Cloudsmith CLI is available: {current} \u2192 {latest}"
154
+ )
155
+ click.echo(f"See what's changed: {changelog}")
156
+ click.echo(f"The CLI was installed via {channel}. To update, run:")
157
+ click.echo(f" {instruction}")
158
+ return
159
+
160
+ if not installation.self_update_supported():
161
+ _print_manual_update(opts, current, latest, manifest, data)
162
+ return
163
+
164
+ if not _confirm_self_update(opts, current, latest, assume_yes):
165
+ data["upgraded"] = False
166
+ if not utils.maybe_print_as_json(opts, data):
167
+ click.echo("Update cancelled.")
168
+ return
169
+
170
+ _run_self_update(opts, session, manifest, data)
@@ -75,6 +75,7 @@ class ConfigSchema:
75
75
  oidc_detector_order = ConfigParam(name="oidc_detector_order", type=str)
76
76
  oidc_disabled_detectors = ConfigParam(name="oidc_disabled_detectors", type=str)
77
77
  metadata_failure_mode = ConfigParam(name="metadata_failure_mode", type=str)
78
+ check_for_update = ConfigParam(name="check_for_update", type=bool, default=True)
78
79
 
79
80
  @matches_section("profile:*")
80
81
  class Profile(Default):
@@ -602,6 +603,18 @@ class Options: # pylint: disable=too-many-public-methods
602
603
  )
603
604
  self._set_option("metadata_failure_mode", normalised)
604
605
 
606
+ @property
607
+ def check_for_update(self):
608
+ """Get value for the once-a-day update check toggle."""
609
+ return self._get_option("check_for_update", default=True)
610
+
611
+ @check_for_update.setter
612
+ def check_for_update(self, value):
613
+ """Set value for the once-a-day update check toggle."""
614
+ if value is None:
615
+ return
616
+ self._set_option("check_for_update", bool(value))
617
+
605
618
  @property
606
619
  def output(self):
607
620
  """Get value for output format."""
@@ -2,6 +2,7 @@
2
2
 
3
3
  import json
4
4
  import platform
5
+ import sys
5
6
  from contextlib import contextmanager
6
7
  from datetime import date, datetime
7
8
 
@@ -75,7 +76,7 @@ def rich_print_table(headers, rows, title=None, show_lines=False):
75
76
  from rich.console import Console
76
77
  from rich.table import Table
77
78
 
78
- console = Console()
79
+ console = Console(width=(None if sys.stdout.isatty() else 1000))
79
80
  table = Table(title=title, show_lines=show_lines)
80
81
 
81
82
  for header in headers:
@@ -231,8 +232,12 @@ def confirm_operation(prompt, prefix=None, assume_yes=False, err=False):
231
232
  @contextmanager
232
233
  def maybe_spinner(opts):
233
234
  """Only activate the spinner if not in debug mode or using json output."""
234
- if should_use_stderr(opts) or get_output_format(opts) in ("json", "pretty_json"):
235
- # No spinner
235
+ if (
236
+ getattr(opts, "debug", False)
237
+ or should_use_stderr(opts)
238
+ or get_output_format(opts) in ("json", "pretty_json")
239
+ ):
240
+ # No spinner (debug logs / machine output must not be clobbered)
236
241
  yield
237
242
  else:
238
243
  with spinner() as spin:
@@ -240,13 +240,18 @@ class AuthenticationWebRequestHandler(BaseHTTPRequestHandler):
240
240
  if self.server_instance:
241
241
  self.server_instance.sso_access_token = access_token
242
242
 
243
- if not store_sso_tokens(
244
- self.api_host, access_token, refresh_token, profile=self.profile
245
- ):
246
- click.echo(
247
- "SSO tokens not stored (CLOUDSMITH_NO_KEYRING is set)",
248
- err=True,
249
- )
243
+ from keyring.errors import KeyringError
244
+
245
+ try:
246
+ if not store_sso_tokens(
247
+ self.api_host, access_token, refresh_token, profile=self.profile
248
+ ):
249
+ click.echo(
250
+ "SSO tokens not stored (CLOUDSMITH_NO_KEYRING is set)",
251
+ err=True,
252
+ )
253
+ except KeyringError as exc:
254
+ click.echo(f"SSO tokens not stored: {exc}", err=True)
250
255
 
251
256
  if self.refresh_api_on_success and self.server_instance:
252
257
  self.server_instance.refresh_api_config_after_auth()
@@ -3,6 +3,7 @@
3
3
  import fnmatch
4
4
  import hashlib
5
5
  import os
6
+ import sys
6
7
 
7
8
  import click
8
9
  import requests
@@ -309,7 +310,7 @@ def _display_multiple_packages(packages: list[dict]) -> None:
309
310
  _format_date(pkg.get("uploaded_at", "")),
310
311
  )
311
312
 
312
- Console().print(table)
313
+ Console(width=(None if sys.stdout.isatty() else 1000)).print(table)
313
314
  click.echo()
314
315
 
315
316
 
@@ -0,0 +1,156 @@
1
+ # Copyright 2026 Cloudsmith Ltd
2
+ """Detect how the CLI was installed and how to upgrade it."""
3
+
4
+ import os
5
+ import platform
6
+ import sys
7
+ from importlib import metadata
8
+
9
+ CHANNEL_STANDALONE = "standalone"
10
+ CHANNEL_HOMEBREW = "homebrew"
11
+ CHANNEL_DOCKER = "docker"
12
+ CHANNEL_AQUA = "aqua"
13
+ CHANNEL_PIP = "pip"
14
+ CHANNEL_PIPX = "pipx"
15
+ CHANNEL_UV_TOOL = "uv-tool"
16
+ CHANNEL_UV_PIP = "uv-pip"
17
+ CHANNEL_UNKNOWN = "unknown"
18
+
19
+ _DISTRIBUTION_NAME = "cloudsmith-cli"
20
+
21
+ #: Where a user downloads a fresh standalone build when self-update cannot run.
22
+ RELEASES_LATEST_URL = "https://github.com/cloudsmith-io/cloudsmith-cli/releases/latest"
23
+
24
+ #: Base for a version's GitHub release page. Each release is tagged ``v<version>``
25
+ #: and its page renders that version's changelog, so it answers "what do I gain
26
+ #: by upgrading?" for the exact version being offered.
27
+ _RELEASE_TAG_URL_TEMPLATE = (
28
+ "https://github.com/cloudsmith-io/cloudsmith-cli/releases/tag/v{version}"
29
+ )
30
+
31
+
32
+ def changelog_url(version=None):
33
+ """Return the changelog URL for ``version`` (its release page), or the list.
34
+
35
+ With a version, points at that version's release page (tagged
36
+ ``v<version>``), which shows exactly what changed. Without one, points at
37
+ the releases listing.
38
+ """
39
+ if not version:
40
+ return RELEASES_LATEST_URL
41
+ return _RELEASE_TAG_URL_TEMPLATE.format(version=str(version).lstrip("v"))
42
+
43
+
44
+ _UPGRADE_INSTRUCTIONS = {
45
+ CHANNEL_PIP: "pip install --upgrade cloudsmith-cli",
46
+ CHANNEL_PIPX: "pipx upgrade cloudsmith-cli",
47
+ CHANNEL_UV_TOOL: "uv tool upgrade cloudsmith-cli",
48
+ CHANNEL_UV_PIP: "uv pip install --upgrade cloudsmith-cli",
49
+ CHANNEL_HOMEBREW: "brew update && brew upgrade cloudsmith-cli",
50
+ CHANNEL_DOCKER: "docker pull cloudsmith/cloudsmith-cli:latest",
51
+ CHANNEL_AQUA: (
52
+ "update the cloudsmith-io/cloudsmith-cli version in your aqua "
53
+ "configuration, then run `aqua install`"
54
+ ),
55
+ CHANNEL_UNKNOWN: "pip install --upgrade cloudsmith-cli",
56
+ }
57
+
58
+
59
+ def _running_in_container():
60
+ if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
61
+ return True
62
+ return bool(
63
+ os.environ.get("container") or os.environ.get("KUBERNETES_SERVICE_HOST")
64
+ )
65
+
66
+
67
+ def _distribution_location():
68
+ """Return the installed distribution's directory, or None."""
69
+ try:
70
+ distribution = metadata.distribution(_DISTRIBUTION_NAME)
71
+ except metadata.PackageNotFoundError:
72
+ return None
73
+ return str(distribution.locate_file(""))
74
+
75
+
76
+ def _distribution_installer():
77
+ """Return the INSTALLER record of the installed distribution, or None."""
78
+ try:
79
+ distribution = metadata.distribution(_DISTRIBUTION_NAME)
80
+ except metadata.PackageNotFoundError:
81
+ return None
82
+ installer = distribution.read_text("INSTALLER")
83
+ return installer.strip() if installer else None
84
+
85
+
86
+ def _detect_frozen_channel(executable_path):
87
+ normalized = os.path.realpath(executable_path).replace("\\", "/")
88
+ if "aquaproj-aqua" in normalized:
89
+ return CHANNEL_AQUA
90
+ if "/Cellar/" in normalized or "/linuxbrew/" in normalized:
91
+ return CHANNEL_HOMEBREW
92
+ if normalized.startswith("/opt/cloudsmith") and _running_in_container():
93
+ return CHANNEL_DOCKER
94
+ return CHANNEL_STANDALONE
95
+
96
+
97
+ def _detect_package_channel():
98
+ location = _distribution_location()
99
+ if location is None:
100
+ return CHANNEL_UNKNOWN
101
+ normalized = location.replace("\\", "/")
102
+ if "/pipx/" in normalized:
103
+ return CHANNEL_PIPX
104
+ if "/uv/tools/" in normalized:
105
+ return CHANNEL_UV_TOOL
106
+ installer = _distribution_installer()
107
+ if installer == "uv":
108
+ return CHANNEL_UV_PIP
109
+ if installer == "pip":
110
+ return CHANNEL_PIP
111
+ return CHANNEL_UNKNOWN
112
+
113
+
114
+ def detect_channel(frozen=None, executable_path=None):
115
+ """Detect the install channel of the running CLI."""
116
+ frozen = getattr(sys, "frozen", False) if frozen is None else frozen
117
+ if frozen:
118
+ return _detect_frozen_channel(executable_path or sys.executable)
119
+ return _detect_package_channel()
120
+
121
+
122
+ def detect_target():
123
+ """Detect the standalone build target for this platform, or None."""
124
+ system = platform.system()
125
+ machine = platform.machine().lower()
126
+ arch = {"amd64": "x86_64", "x86_64": "x86_64"}.get(machine)
127
+ arm = machine in ("arm64", "aarch64")
128
+ if system == "Darwin":
129
+ return "macos-arm64" if arm else ("macos-x86_64" if arch else None)
130
+ if system == "Windows":
131
+ return "windows-x86_64" if arch else None
132
+ if system == "Linux":
133
+ if arm:
134
+ arch = "aarch64"
135
+ if arch is None:
136
+ return None
137
+ libc = "gnu" if platform.libc_ver()[0] == "glibc" else "musl"
138
+ return f"linux-{arch}-{libc}"
139
+ return None
140
+
141
+
142
+ def upgrade_instruction(channel):
143
+ """Return the upgrade command for a channel, or None for self-update."""
144
+ if channel == CHANNEL_STANDALONE:
145
+ return None
146
+ return _UPGRADE_INSTRUCTIONS[channel]
147
+
148
+
149
+ def self_update_supported():
150
+ """Whether the standalone binary can replace itself in place.
151
+
152
+ False on Windows: the running ``.exe`` is locked, so an in-place swap is
153
+ not possible (a deferred-swap helper is a future follow-up). Callers fall
154
+ back to manual-download instructions.
155
+ """
156
+ return os.name != "nt"