cloudsmith-cli 1.20.0__py3-none-any.whl

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 (130) hide show
  1. cloudsmith_cli/__init__.py +10 -0
  2. cloudsmith_cli/__main__.py +8 -0
  3. cloudsmith_cli/cli/__init__.py +1 -0
  4. cloudsmith_cli/cli/command.py +160 -0
  5. cloudsmith_cli/cli/commands/__init__.py +33 -0
  6. cloudsmith_cli/cli/commands/auth.py +173 -0
  7. cloudsmith_cli/cli/commands/check.py +129 -0
  8. cloudsmith_cli/cli/commands/copy.py +98 -0
  9. cloudsmith_cli/cli/commands/credential_helper/__init__.py +39 -0
  10. cloudsmith_cli/cli/commands/credential_helper/docker.py +66 -0
  11. cloudsmith_cli/cli/commands/credential_helper/manage.py +299 -0
  12. cloudsmith_cli/cli/commands/delete.py +67 -0
  13. cloudsmith_cli/cli/commands/dependencies.py +108 -0
  14. cloudsmith_cli/cli/commands/docs.py +16 -0
  15. cloudsmith_cli/cli/commands/download.py +620 -0
  16. cloudsmith_cli/cli/commands/entitlements.py +819 -0
  17. cloudsmith_cli/cli/commands/help_.py +12 -0
  18. cloudsmith_cli/cli/commands/list_.py +317 -0
  19. cloudsmith_cli/cli/commands/login.py +99 -0
  20. cloudsmith_cli/cli/commands/logout.py +151 -0
  21. cloudsmith_cli/cli/commands/main.py +73 -0
  22. cloudsmith_cli/cli/commands/mcp.py +523 -0
  23. cloudsmith_cli/cli/commands/metadata.py +503 -0
  24. cloudsmith_cli/cli/commands/metrics/__init__.py +2 -0
  25. cloudsmith_cli/cli/commands/metrics/command.py +20 -0
  26. cloudsmith_cli/cli/commands/metrics/entitlements.py +148 -0
  27. cloudsmith_cli/cli/commands/metrics/packages.py +134 -0
  28. cloudsmith_cli/cli/commands/move.py +111 -0
  29. cloudsmith_cli/cli/commands/policy/__init__.py +3 -0
  30. cloudsmith_cli/cli/commands/policy/command.py +20 -0
  31. cloudsmith_cli/cli/commands/policy/deny.py +248 -0
  32. cloudsmith_cli/cli/commands/policy/license.py +335 -0
  33. cloudsmith_cli/cli/commands/policy/vulnerability.py +322 -0
  34. cloudsmith_cli/cli/commands/push.py +1323 -0
  35. cloudsmith_cli/cli/commands/quarantine.py +148 -0
  36. cloudsmith_cli/cli/commands/quota/__init__.py +2 -0
  37. cloudsmith_cli/cli/commands/quota/command.py +20 -0
  38. cloudsmith_cli/cli/commands/quota/history.py +122 -0
  39. cloudsmith_cli/cli/commands/quota/quota.py +107 -0
  40. cloudsmith_cli/cli/commands/repos.py +330 -0
  41. cloudsmith_cli/cli/commands/resync.py +90 -0
  42. cloudsmith_cli/cli/commands/status.py +92 -0
  43. cloudsmith_cli/cli/commands/tags.py +375 -0
  44. cloudsmith_cli/cli/commands/tokens.py +318 -0
  45. cloudsmith_cli/cli/commands/upstream.py +479 -0
  46. cloudsmith_cli/cli/commands/vulnerabilities.py +141 -0
  47. cloudsmith_cli/cli/commands/whoami.py +188 -0
  48. cloudsmith_cli/cli/config.py +635 -0
  49. cloudsmith_cli/cli/decorators.py +624 -0
  50. cloudsmith_cli/cli/exceptions.py +215 -0
  51. cloudsmith_cli/cli/metadata_common.py +146 -0
  52. cloudsmith_cli/cli/saml.py +109 -0
  53. cloudsmith_cli/cli/table.py +59 -0
  54. cloudsmith_cli/cli/types.py +14 -0
  55. cloudsmith_cli/cli/utils.py +267 -0
  56. cloudsmith_cli/cli/validators.py +378 -0
  57. cloudsmith_cli/cli/webserver.py +263 -0
  58. cloudsmith_cli/core/__init__.py +1 -0
  59. cloudsmith_cli/core/api/__init__.py +1 -0
  60. cloudsmith_cli/core/api/distros.py +31 -0
  61. cloudsmith_cli/core/api/entitlements.py +130 -0
  62. cloudsmith_cli/core/api/exceptions.py +57 -0
  63. cloudsmith_cli/core/api/files.py +131 -0
  64. cloudsmith_cli/core/api/init.py +109 -0
  65. cloudsmith_cli/core/api/metadata.py +217 -0
  66. cloudsmith_cli/core/api/metrics.py +78 -0
  67. cloudsmith_cli/core/api/orgs.py +201 -0
  68. cloudsmith_cli/core/api/packages.py +309 -0
  69. cloudsmith_cli/core/api/quota.py +64 -0
  70. cloudsmith_cli/core/api/rates.py +28 -0
  71. cloudsmith_cli/core/api/repos.py +81 -0
  72. cloudsmith_cli/core/api/status.py +27 -0
  73. cloudsmith_cli/core/api/upstreams.py +72 -0
  74. cloudsmith_cli/core/api/user.py +109 -0
  75. cloudsmith_cli/core/api/version.py +15 -0
  76. cloudsmith_cli/core/api/vulnerabilities.py +230 -0
  77. cloudsmith_cli/core/cache_utils.py +160 -0
  78. cloudsmith_cli/core/config.py +140 -0
  79. cloudsmith_cli/core/credentials/__init__.py +0 -0
  80. cloudsmith_cli/core/credentials/chain.py +69 -0
  81. cloudsmith_cli/core/credentials/models.py +44 -0
  82. cloudsmith_cli/core/credentials/oidc/__init__.py +6 -0
  83. cloudsmith_cli/core/credentials/oidc/cache.py +220 -0
  84. cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +122 -0
  85. cloudsmith_cli/core/credentials/oidc/detectors/aws.py +85 -0
  86. cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +70 -0
  87. cloudsmith_cli/core/credentials/oidc/detectors/base.py +26 -0
  88. cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +35 -0
  89. cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +40 -0
  90. cloudsmith_cli/core/credentials/oidc/detectors/generic.py +42 -0
  91. cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +64 -0
  92. cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +49 -0
  93. cloudsmith_cli/core/credentials/oidc/exchange.py +87 -0
  94. cloudsmith_cli/core/credentials/provider.py +17 -0
  95. cloudsmith_cli/core/credentials/providers/__init__.py +15 -0
  96. cloudsmith_cli/core/credentials/providers/cli_flag.py +24 -0
  97. cloudsmith_cli/core/credentials/providers/credentials_file.py +24 -0
  98. cloudsmith_cli/core/credentials/providers/env_var.py +24 -0
  99. cloudsmith_cli/core/credentials/providers/keyring_provider.py +59 -0
  100. cloudsmith_cli/core/credentials/providers/oidc_provider.py +115 -0
  101. cloudsmith_cli/core/download.py +594 -0
  102. cloudsmith_cli/core/keyring.py +171 -0
  103. cloudsmith_cli/core/mcp/__init__.py +0 -0
  104. cloudsmith_cli/core/mcp/data.py +17 -0
  105. cloudsmith_cli/core/mcp/server.py +786 -0
  106. cloudsmith_cli/core/pagination.py +131 -0
  107. cloudsmith_cli/core/ratelimits.py +88 -0
  108. cloudsmith_cli/core/rest.py +255 -0
  109. cloudsmith_cli/core/utils.py +95 -0
  110. cloudsmith_cli/core/version.py +20 -0
  111. cloudsmith_cli/credential_helpers/__init__.py +7 -0
  112. cloudsmith_cli/credential_helpers/backends.py +41 -0
  113. cloudsmith_cli/credential_helpers/common.py +111 -0
  114. cloudsmith_cli/credential_helpers/custom_domains.py +281 -0
  115. cloudsmith_cli/credential_helpers/docker/__init__.py +4 -0
  116. cloudsmith_cli/credential_helpers/docker/installer.py +349 -0
  117. cloudsmith_cli/credential_helpers/docker/runtime.py +117 -0
  118. cloudsmith_cli/credential_helpers/launchers.py +175 -0
  119. cloudsmith_cli/data/VERSION +1 -0
  120. cloudsmith_cli/data/config.ini +23 -0
  121. cloudsmith_cli/data/credentials.ini +14 -0
  122. cloudsmith_cli/templates/__init__.py +3 -0
  123. cloudsmith_cli/templates/auth_error.html +45 -0
  124. cloudsmith_cli/templates/auth_success.html +37 -0
  125. cloudsmith_cli-1.20.0.dist-info/METADATA +610 -0
  126. cloudsmith_cli-1.20.0.dist-info/RECORD +130 -0
  127. cloudsmith_cli-1.20.0.dist-info/WHEEL +5 -0
  128. cloudsmith_cli-1.20.0.dist-info/entry_points.txt +2 -0
  129. cloudsmith_cli-1.20.0.dist-info/licenses/LICENSE +201 -0
  130. cloudsmith_cli-1.20.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,479 @@
1
+ """CLI/Commands - create, retrieve, update or delete repository upstreams."""
2
+
3
+ import json
4
+
5
+ import click
6
+
7
+ from ...core.api import upstreams as api
8
+ from ...core.pagination import paginate_results
9
+ from .. import command, decorators, utils, validators
10
+ from ..exceptions import handle_api_exceptions
11
+ from ..utils import (
12
+ fmt_bool,
13
+ fmt_datetime,
14
+ maybe_spinner,
15
+ maybe_truncate_list,
16
+ maybe_truncate_string,
17
+ )
18
+ from .main import main
19
+
20
+ UPSTREAM_FORMATS = [
21
+ "alpine",
22
+ "cargo",
23
+ "conda",
24
+ "cran",
25
+ "dart",
26
+ "deb",
27
+ "docker",
28
+ "generic",
29
+ "go",
30
+ "helm",
31
+ "hex",
32
+ "huggingface",
33
+ "maven",
34
+ "npm",
35
+ "nuget",
36
+ "python",
37
+ "rpm",
38
+ "ruby",
39
+ "swift",
40
+ ]
41
+
42
+
43
+ def print_upstreams(upstreams, upstream_fmt, page_info=None, page_all=False):
44
+ """Print upstreams as a table or output in another format."""
45
+
46
+ def build_row(u):
47
+ row = [
48
+ click.style(u["name"], fg="cyan"),
49
+ click.style(maybe_truncate_string(u["upstream_url"]), fg="cyan"),
50
+ click.style(str(u["auth_mode"]), fg="yellow"),
51
+ click.style(
52
+ maybe_truncate_string(str(u["auth_secret"] or "")),
53
+ fg="yellow",
54
+ ),
55
+ click.style(str(u["auth_username"] or ""), fg="yellow"),
56
+ click.style(fmt_datetime(u["created_at"]), fg="blue"),
57
+ click.style(str(u["extra_header_1"] or ""), fg="yellow"),
58
+ click.style(str(u["extra_header_2"] or ""), fg="yellow"),
59
+ click.style(str(u["extra_value_1"] or ""), fg="yellow"),
60
+ click.style(str(u["extra_value_2"] or ""), fg="yellow"),
61
+ click.style(fmt_bool(u["is_active"]), fg="green"),
62
+ click.style(u["mode"], fg="green"),
63
+ click.style(str(u["priority"]), fg="green"),
64
+ click.style(u["slug_perm"], fg="green"),
65
+ click.style(fmt_datetime(u["updated_at"]), fg="blue"),
66
+ click.style(fmt_bool(u["verify_ssl"]), fg="green"),
67
+ ]
68
+
69
+ if upstream_fmt == "deb":
70
+ # `Component`, `Distribution Versions` and `Upstream Distribution` are deb-only
71
+ row.append(click.style(str(u.get("component", None)), fg="yellow"))
72
+ row.append(
73
+ click.style(
74
+ str(maybe_truncate_list(u.get("distro_versions", []))),
75
+ fg="yellow",
76
+ )
77
+ )
78
+ row.append(
79
+ click.style(str(u.get("upstream_distribution", None)), fg="yellow")
80
+ )
81
+
82
+ if upstream_fmt == "rpm":
83
+ # `Distribution Version` is rpm-only
84
+ row.append(click.style(str(u.get("distro_version", "")), fg="yellow"))
85
+
86
+ return row
87
+
88
+ headers = [
89
+ "Name",
90
+ "Upstream Url",
91
+ "Auth mode",
92
+ "Auth Secret",
93
+ "Auth Username",
94
+ "Created At",
95
+ "Extra Header 1",
96
+ "Extra Header 2",
97
+ "Extra Value 1",
98
+ "Extra Value 2",
99
+ "Active",
100
+ "Mode",
101
+ "Priority",
102
+ "Slug Perm",
103
+ "Updated At",
104
+ "Verify SSL",
105
+ ]
106
+
107
+ if upstream_fmt == "deb":
108
+ headers.append("Component")
109
+ headers.append("Distribution Versions")
110
+ headers.append("Upstream Distribution")
111
+
112
+ if upstream_fmt == "rpm":
113
+ headers.append("Distribution Version")
114
+
115
+ rows = [build_row(x) for x in upstreams]
116
+
117
+ click.echo()
118
+ utils.pretty_print_table(headers, rows)
119
+ click.echo()
120
+
121
+ num_results = len(upstreams)
122
+ list_suffix = "upstream%s" % ("" if num_results == 1 else "s")
123
+ utils.pretty_print_list_info(
124
+ num_results=num_results,
125
+ page_info=None if page_all else page_info,
126
+ suffix=list_suffix,
127
+ page_all=page_all,
128
+ )
129
+
130
+
131
+ @main.group(cls=command.AliasGroup, name="upstream", aliases=[])
132
+ @decorators.common_cli_config_options
133
+ @decorators.common_cli_output_options
134
+ @decorators.common_api_auth_options
135
+ @decorators.initialise_api
136
+ @click.pass_context
137
+ def upstream(*args, **kwargs):
138
+ """
139
+ Manage upstreams for a repository.
140
+
141
+ See the help for subcommands for more information on each.
142
+ """
143
+
144
+
145
+ def build_upstream_group_func(upstream_fmt):
146
+ @decorators.common_cli_config_options
147
+ @decorators.common_cli_output_options
148
+ @decorators.common_api_auth_options
149
+ @decorators.initialise_api
150
+ @click.pass_context
151
+ def func(ctx, opts):
152
+ pass
153
+
154
+ func.__doc__ = (
155
+ """
156
+ Manage %s upstreams for a repository.
157
+
158
+ See the help for subcommands for more information on each.
159
+ """
160
+ % upstream_fmt
161
+ )
162
+ return func
163
+
164
+
165
+ def build_upstream_list_command(upstream_fmt):
166
+ @decorators.common_cli_config_options
167
+ @decorators.common_cli_list_options
168
+ @decorators.common_cli_output_options
169
+ @decorators.common_api_auth_options
170
+ @decorators.initialise_api
171
+ @click.argument(
172
+ "owner_repo", metavar="OWNER/REPO", callback=validators.validate_owner_repo
173
+ )
174
+ @click.pass_context
175
+ def func(ctx, opts, owner_repo, page, page_size, page_all):
176
+ owner, repo = owner_repo
177
+
178
+ use_stderr = utils.should_use_stderr(opts)
179
+
180
+ if not use_stderr:
181
+ click.echo("Getting upstreams... ", nl=False, err=use_stderr)
182
+
183
+ context_msg = "Failed to get upstreams!"
184
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
185
+ with maybe_spinner(opts):
186
+ upstreams, page_info = paginate_results(
187
+ api.list_upstreams,
188
+ page_all,
189
+ page,
190
+ page_size,
191
+ owner=owner,
192
+ repo=repo,
193
+ upstream_format=upstream_fmt,
194
+ )
195
+
196
+ if not use_stderr:
197
+ click.secho("OK", fg="green", err=use_stderr)
198
+
199
+ if utils.maybe_print_as_json(opts, upstreams, page_info):
200
+ return
201
+
202
+ print_upstreams(upstreams, upstream_fmt, page_info, page_all)
203
+
204
+ func.__doc__ = f"""
205
+ List {upstream_fmt} upstreams for a repository.
206
+
207
+ This requires appropriate permissions for the owner (a member of the organisation, repository privileges and a valid API key).
208
+
209
+ - OWNER/REPO: Specify the OWNER namespace (organization) and REPO (repository) to target a specific Cloudsmith repository.
210
+
211
+ Example: 'your-org/your-repo'
212
+
213
+ Full CLI example:
214
+
215
+ $ cloudsmith upstream {upstream_fmt} ls your-org/your-repo
216
+ """
217
+ return func
218
+
219
+
220
+ def build_upstream_create_command(upstream_fmt):
221
+ @decorators.common_cli_config_options
222
+ @decorators.common_cli_output_options
223
+ @decorators.common_api_auth_options
224
+ @decorators.initialise_api
225
+ @click.argument(
226
+ "owner_repo", metavar="OWNER/REPO", callback=validators.validate_owner_repo
227
+ )
228
+ @click.argument("upstream_config_file", type=click.File("rb"), required=True)
229
+ @click.pass_context
230
+ def func(ctx, opts, owner_repo, upstream_config_file):
231
+ # Use stderr for messages if the output is something else (e.g. JSON)
232
+ use_stderr = opts.output != "pretty"
233
+
234
+ owner, repo = owner_repo
235
+
236
+ upstream_config = json.load(upstream_config_file)
237
+
238
+ upstream_name = upstream_config.get("name", None)
239
+
240
+ if upstream_name is None:
241
+ raise click.BadParameter(
242
+ "Name is a required field for creating an upstream.", param="name"
243
+ )
244
+
245
+ if not use_stderr:
246
+ click.secho(
247
+ 'Creating "%(name)s" upstream for the %(owner)s/%(repo)s repository...'
248
+ % {
249
+ "name": click.style(upstream_name, bold=True),
250
+ "owner": click.style(owner, bold=True),
251
+ "repo": click.style(repo, bold=True),
252
+ },
253
+ nl=False,
254
+ err=use_stderr,
255
+ )
256
+
257
+ context_msg = "Failed to create the upstream!"
258
+
259
+ with handle_api_exceptions(ctx, opts, context_msg=context_msg):
260
+ with maybe_spinner(opts):
261
+ upstream_resp_data = api.create_upstream(
262
+ owner, repo, upstream_fmt, upstream_config
263
+ )
264
+
265
+ if not use_stderr:
266
+ click.secho("OK", fg="green", err=use_stderr)
267
+
268
+ if utils.maybe_print_as_json(opts, upstream_resp_data):
269
+ return
270
+
271
+ print_upstreams([upstream_resp_data], upstream_fmt)
272
+
273
+ func.__doc__ = f"""
274
+ Create a {upstream_fmt} upstream for a repository.
275
+
276
+ This requires appropriate permissions for the owner (a member of the organisation, repository privileges and a valid API key).
277
+
278
+ - OWNER/REPO: Specify the OWNER namespace (organization) and REPO (repository) to target a specific Cloudsmith repository.
279
+
280
+ Example: 'your-org/your-repo'
281
+
282
+ - UPSTREAM_CONFIG_FILE: Config json file specifying the settings for the upstream to be updated.
283
+
284
+ For a full list of supported config properties, please refer to the "body params" section of the api reference for the relevant endpoint at:
285
+
286
+ https://docs.cloudsmith.com/api/repos/upstream/{upstream_fmt}/create
287
+
288
+ \b
289
+ Example:
290
+ {{
291
+ "name": "Some Upstream",
292
+ "upstream_url": "https://someupstream.net",
293
+ "mode": "Proxy Only",
294
+ "auth_mode": "None",
295
+ "priority": 5,
296
+ ...
297
+ }}
298
+
299
+ Full CLI example:
300
+
301
+ $ cloudsmith upstream {upstream_fmt} create your-org/your-repo ./path/to/upstream-config.json
302
+ """
303
+
304
+ return func
305
+
306
+
307
+ def build_upstream_update_command(upstream_fmt):
308
+ @decorators.common_cli_config_options
309
+ @decorators.common_cli_output_options
310
+ @decorators.common_api_auth_options
311
+ @decorators.initialise_api
312
+ @click.argument(
313
+ "owner_repo_slug_perm",
314
+ metavar="OWNER/REPO/SLUG_PERM",
315
+ callback=validators.validate_owner_repo_slug_perm,
316
+ )
317
+ @click.argument("upstream_config_file", type=click.File("rb"), required=True)
318
+ @click.pass_context
319
+ def func(ctx, opts, owner_repo_slug_perm, upstream_config_file):
320
+ # Use stderr for message if the output is something else (e.g. JSON)
321
+ use_stderr = opts.output != "pretty"
322
+
323
+ owner, repo, slug_perm = owner_repo_slug_perm
324
+
325
+ upstream_config = json.load(upstream_config_file)
326
+
327
+ if not use_stderr:
328
+ click.secho(
329
+ "Updating the %(slug_perm)s upstream from the %(owner)s/%(repo)s repository ... "
330
+ % {
331
+ "owner": click.style(owner, bold=True),
332
+ "repo": click.style(repo, bold=True),
333
+ "slug_perm": click.style(slug_perm, bold=True),
334
+ },
335
+ nl=False,
336
+ err=use_stderr,
337
+ )
338
+
339
+ context_msg = "Failed to update the upstream!"
340
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
341
+ with maybe_spinner(opts):
342
+ upstream_resp_data = api.update_upstream(
343
+ owner, repo, slug_perm, upstream_fmt, upstream_config
344
+ )
345
+
346
+ if not use_stderr:
347
+ click.secho("OK", fg="green", err=use_stderr)
348
+
349
+ if utils.maybe_print_as_json(opts, upstream_resp_data):
350
+ return
351
+
352
+ print_upstreams([upstream_resp_data], upstream_fmt)
353
+
354
+ func.__doc__ = f"""
355
+ Update a {upstream_fmt} upstream for a repository.
356
+
357
+ This requires appropriate permissions for the owner (a member of the organisation, repository privileges and a valid API key).
358
+
359
+ - OWNER/REPO/SLUG_PERM: Specify the OWNER namespace (organization), REPO (repository) and SLUG_PERM (upstream) to target a specific upstream belonging to a repo.
360
+
361
+ Example: 'your-org/your-repo/abcdefg'
362
+
363
+ - UPSTREAM_CONFIG_FILE: Config json file specifying the settings for the upstream to be updated.
364
+
365
+ For a full list of supported config properties, please refer to the "body params" section of the api reference for the relevant endpoint at:
366
+
367
+ https://docs.cloudsmith.com/api/repos/upstream/{upstream_fmt}/partial-update
368
+
369
+ \b
370
+ Example:
371
+ {{
372
+ "name": "Some Upstream",
373
+ "upstream_url": "https://someupstream.net",
374
+ "mode": "Proxy Only",
375
+ "auth_mode": "None",
376
+ "priority": 5,
377
+ ...
378
+ }}
379
+
380
+ Full CLI example:
381
+
382
+ $ cloudsmith upstream {upstream_fmt} update your-org/your-repo/abcdefg ./path/to/upstream-config.json
383
+ """
384
+
385
+ return func
386
+
387
+
388
+ def build_upstream_delete_command(upstream_fmt):
389
+ @decorators.common_cli_config_options
390
+ @decorators.common_cli_output_options
391
+ @decorators.common_api_auth_options
392
+ @decorators.initialise_api
393
+ @click.argument(
394
+ "owner_repo_slug_perm",
395
+ metavar="OWNER/REPO/SLUG_PERM",
396
+ callback=validators.validate_owner_repo_slug_perm,
397
+ )
398
+ @click.option(
399
+ "-y",
400
+ "--yes",
401
+ default=False,
402
+ is_flag=True,
403
+ help="Assume yes as default answer to questions (this is dangerous!)",
404
+ )
405
+ @click.pass_context
406
+ def func(ctx, opts, owner_repo_slug_perm, yes):
407
+ # Use stderr for message if the output is something else (e.g. JSON)
408
+ use_stderr = opts.output != "pretty"
409
+
410
+ owner, repo, slug_perm = owner_repo_slug_perm
411
+
412
+ delete_args = {
413
+ "owner": click.style(owner, bold=True),
414
+ "repo": click.style(repo, bold=True),
415
+ "slug_perm": click.style(slug_perm, bold=True),
416
+ }
417
+
418
+ prompt = (
419
+ "delete the %(slug_perm)s upstream from the %(owner)s/%(repo)s repository"
420
+ % delete_args
421
+ )
422
+ if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
423
+ return
424
+
425
+ if not use_stderr:
426
+ click.secho(
427
+ "Deleting the %(slug_perm)s upstream from the %(owner)s/%(repo)s repository ... "
428
+ % delete_args,
429
+ nl=False,
430
+ err=use_stderr,
431
+ )
432
+
433
+ context_msg = "Failed to delete the upstream!"
434
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
435
+ with maybe_spinner(opts):
436
+ api.delete_upstream(owner, repo, upstream_fmt, slug_perm)
437
+
438
+ if not use_stderr:
439
+ click.secho("OK", fg="green", err=use_stderr)
440
+
441
+ func.__doc__ = f"""
442
+ Delete a {upstream_fmt} upstream for a repository.
443
+
444
+ This requires appropriate permissions for the owner (a member of the organisation, repository privileges and a valid API key).
445
+
446
+ - OWNER/REPO/SLUG_PERM: Specify the OWNER namespace (organization), REPO (repository) and SLUG_PERM (upstream) to target a specific upstream belonging to a repo.
447
+
448
+ Example: 'your-org/your-repo/abcdefg'
449
+
450
+ Full CLI example:
451
+
452
+ $ cloudsmith upstream {upstream_fmt} delete your-org/your-repo/abcdefg
453
+ """
454
+
455
+ return func
456
+
457
+
458
+ for upstream_format in UPSTREAM_FORMATS:
459
+ # Build a click group for the upstream name e.g. dart, npm, ruby.
460
+ # Add it to the "upstream" parent group.
461
+ # This gives us e.g. `cloudsmith upstream dart` in the cli.
462
+ upstream_group = upstream.group(
463
+ cls=command.AliasGroup, name=upstream_format, aliases=[]
464
+ )(build_upstream_group_func(upstream_format))
465
+
466
+ # Add create/list/update/delete commands to the child group we created above.
467
+ # This gives us e.g. `cloudsmith upstream dart ls`.
468
+ upstream_group.command(name="list", aliases=["ls"])(
469
+ build_upstream_list_command(upstream_format)
470
+ )
471
+ upstream_group.command(name="create", aliases=["new"])(
472
+ build_upstream_create_command(upstream_format)
473
+ )
474
+ upstream_group.command(name="delete", aliases=["rm"])(
475
+ build_upstream_delete_command(upstream_format)
476
+ )
477
+ upstream_group.command(name="update")(
478
+ build_upstream_update_command(upstream_format)
479
+ )
@@ -0,0 +1,141 @@
1
+ """CLI/Commands - Vulnerabilities."""
2
+
3
+ import click
4
+
5
+ from ...core.api.vulnerabilities import (
6
+ _print_vulnerabilities_assessment_table,
7
+ _print_vulnerabilities_summary_table,
8
+ get_package_scan_result,
9
+ )
10
+ from .. import decorators, utils, validators
11
+ from ..exceptions import handle_api_exceptions
12
+ from .main import main
13
+
14
+
15
+ @main.command()
16
+ @decorators.common_cli_config_options
17
+ @decorators.common_cli_output_options
18
+ @decorators.common_api_auth_options
19
+ @decorators.initialise_api
20
+ @click.argument(
21
+ "owner_repo_package",
22
+ metavar="OWNER/REPO/PACKAGE",
23
+ callback=validators.validate_owner_repo_package,
24
+ )
25
+ @click.option(
26
+ "-A",
27
+ "--show-assessment",
28
+ is_flag=True,
29
+ help="Show assessment with vulnerability details.",
30
+ )
31
+ @click.option(
32
+ "--fixable/--non-fixable",
33
+ is_flag=True,
34
+ default=None, # Changed to allow None (Show All) vs True/False
35
+ help="Filter by fixable status (only fixable vs only non-fixable).",
36
+ )
37
+ @click.option(
38
+ "--severity",
39
+ "severity_filter",
40
+ help="Filter by severities (e.g., 'CRITICAL', 'HIGH', 'MEDIUM', 'LOW').",
41
+ )
42
+ @click.pass_context
43
+ def vulnerabilities(
44
+ ctx, opts, owner_repo_package, show_assessment, fixable, severity_filter
45
+ ):
46
+ """
47
+ Retrieve vulnerability scan results for a package.
48
+
49
+ \b
50
+ Usage:
51
+ cloudsmith vulnerabilities myorg/repo/pkg_identifier [flags]
52
+
53
+ \b
54
+ Aliases:
55
+ vulnerabilities, vuln
56
+
57
+ Examples:
58
+
59
+ \b
60
+ # Display the vulnerability summary
61
+ cloudsmith vulnerabilities myorg/repo/pkg_identifier
62
+
63
+ \b
64
+ # Display detailed vulnerability assessment
65
+ cloudsmith vulnerabilities myorg/repo/pkg_identifier -A / --show-assessment
66
+
67
+ \b
68
+ # Filter the result by severity
69
+ cloudsmith vulnerabilities myorg/repo/pkg_identifier --severity critical,high
70
+
71
+ \b
72
+ # Filter by fixable or non-fixable vulnerabilities
73
+ cloudsmith vulnerabilities myorg/repo/pkg_identifier --fixable / --non-fixable
74
+
75
+
76
+ """
77
+ use_stderr = utils.should_use_stderr(opts)
78
+
79
+ owner, repo, slug = owner_repo_package
80
+
81
+ total_filtered_vulns = 0
82
+
83
+ context_msg = "Failed to retrieve vulnerability report!"
84
+ with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
85
+ with utils.maybe_spinner(opts):
86
+ data = get_package_scan_result(
87
+ opts=opts,
88
+ owner=owner,
89
+ repo=repo,
90
+ package=slug,
91
+ show_assessment=show_assessment,
92
+ severity_filter=severity_filter,
93
+ fixable=fixable,
94
+ )
95
+
96
+ click.secho("OK", fg="green", err=use_stderr)
97
+
98
+ # Filter results if severity or fixable flags are active
99
+ if severity_filter or fixable is not None:
100
+ scans = getattr(data, "scans", [])
101
+
102
+ allowed_severities = (
103
+ [s.strip().lower() for s in severity_filter.split(",")]
104
+ if severity_filter
105
+ else None
106
+ )
107
+
108
+ for scan in scans:
109
+ results = getattr(scan, "results", [])
110
+
111
+ # 1. Filter by Severity
112
+ if allowed_severities:
113
+ results = [
114
+ res
115
+ for res in results
116
+ if getattr(res, "severity", "unknown").lower() in allowed_severities
117
+ ]
118
+
119
+ # 2. Filter by Fixable Status
120
+ # fixable=True: Keep only if has fix_version
121
+ # fixable=False: Keep only if NO fix_version
122
+ if fixable is not None:
123
+ results = [
124
+ res
125
+ for res in results
126
+ if bool(
127
+ getattr(res, "fix_version", getattr(res, "fixed_version", None))
128
+ )
129
+ is fixable
130
+ ]
131
+
132
+ scan.results = results
133
+ total_filtered_vulns += len(results)
134
+
135
+ if utils.maybe_print_as_json(opts, data):
136
+ return
137
+
138
+ _print_vulnerabilities_summary_table(data, severity_filter, total_filtered_vulns)
139
+
140
+ if show_assessment:
141
+ _print_vulnerabilities_assessment_table(data, severity_filter)