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.
- cloudsmith_cli/__init__.py +10 -0
- cloudsmith_cli/__main__.py +8 -0
- cloudsmith_cli/cli/__init__.py +1 -0
- cloudsmith_cli/cli/command.py +160 -0
- cloudsmith_cli/cli/commands/__init__.py +33 -0
- cloudsmith_cli/cli/commands/auth.py +173 -0
- cloudsmith_cli/cli/commands/check.py +129 -0
- cloudsmith_cli/cli/commands/copy.py +98 -0
- cloudsmith_cli/cli/commands/credential_helper/__init__.py +39 -0
- cloudsmith_cli/cli/commands/credential_helper/docker.py +66 -0
- cloudsmith_cli/cli/commands/credential_helper/manage.py +299 -0
- cloudsmith_cli/cli/commands/delete.py +67 -0
- cloudsmith_cli/cli/commands/dependencies.py +108 -0
- cloudsmith_cli/cli/commands/docs.py +16 -0
- cloudsmith_cli/cli/commands/download.py +620 -0
- cloudsmith_cli/cli/commands/entitlements.py +819 -0
- cloudsmith_cli/cli/commands/help_.py +12 -0
- cloudsmith_cli/cli/commands/list_.py +317 -0
- cloudsmith_cli/cli/commands/login.py +99 -0
- cloudsmith_cli/cli/commands/logout.py +151 -0
- cloudsmith_cli/cli/commands/main.py +73 -0
- cloudsmith_cli/cli/commands/mcp.py +523 -0
- cloudsmith_cli/cli/commands/metadata.py +503 -0
- cloudsmith_cli/cli/commands/metrics/__init__.py +2 -0
- cloudsmith_cli/cli/commands/metrics/command.py +20 -0
- cloudsmith_cli/cli/commands/metrics/entitlements.py +148 -0
- cloudsmith_cli/cli/commands/metrics/packages.py +134 -0
- cloudsmith_cli/cli/commands/move.py +111 -0
- cloudsmith_cli/cli/commands/policy/__init__.py +3 -0
- cloudsmith_cli/cli/commands/policy/command.py +20 -0
- cloudsmith_cli/cli/commands/policy/deny.py +248 -0
- cloudsmith_cli/cli/commands/policy/license.py +335 -0
- cloudsmith_cli/cli/commands/policy/vulnerability.py +322 -0
- cloudsmith_cli/cli/commands/push.py +1323 -0
- cloudsmith_cli/cli/commands/quarantine.py +148 -0
- cloudsmith_cli/cli/commands/quota/__init__.py +2 -0
- cloudsmith_cli/cli/commands/quota/command.py +20 -0
- cloudsmith_cli/cli/commands/quota/history.py +122 -0
- cloudsmith_cli/cli/commands/quota/quota.py +107 -0
- cloudsmith_cli/cli/commands/repos.py +330 -0
- cloudsmith_cli/cli/commands/resync.py +90 -0
- cloudsmith_cli/cli/commands/status.py +92 -0
- cloudsmith_cli/cli/commands/tags.py +375 -0
- cloudsmith_cli/cli/commands/tokens.py +318 -0
- cloudsmith_cli/cli/commands/upstream.py +479 -0
- cloudsmith_cli/cli/commands/vulnerabilities.py +141 -0
- cloudsmith_cli/cli/commands/whoami.py +188 -0
- cloudsmith_cli/cli/config.py +635 -0
- cloudsmith_cli/cli/decorators.py +624 -0
- cloudsmith_cli/cli/exceptions.py +215 -0
- cloudsmith_cli/cli/metadata_common.py +146 -0
- cloudsmith_cli/cli/saml.py +109 -0
- cloudsmith_cli/cli/table.py +59 -0
- cloudsmith_cli/cli/types.py +14 -0
- cloudsmith_cli/cli/utils.py +267 -0
- cloudsmith_cli/cli/validators.py +378 -0
- cloudsmith_cli/cli/webserver.py +263 -0
- cloudsmith_cli/core/__init__.py +1 -0
- cloudsmith_cli/core/api/__init__.py +1 -0
- cloudsmith_cli/core/api/distros.py +31 -0
- cloudsmith_cli/core/api/entitlements.py +130 -0
- cloudsmith_cli/core/api/exceptions.py +57 -0
- cloudsmith_cli/core/api/files.py +131 -0
- cloudsmith_cli/core/api/init.py +109 -0
- cloudsmith_cli/core/api/metadata.py +217 -0
- cloudsmith_cli/core/api/metrics.py +78 -0
- cloudsmith_cli/core/api/orgs.py +201 -0
- cloudsmith_cli/core/api/packages.py +309 -0
- cloudsmith_cli/core/api/quota.py +64 -0
- cloudsmith_cli/core/api/rates.py +28 -0
- cloudsmith_cli/core/api/repos.py +81 -0
- cloudsmith_cli/core/api/status.py +27 -0
- cloudsmith_cli/core/api/upstreams.py +72 -0
- cloudsmith_cli/core/api/user.py +109 -0
- cloudsmith_cli/core/api/version.py +15 -0
- cloudsmith_cli/core/api/vulnerabilities.py +230 -0
- cloudsmith_cli/core/cache_utils.py +160 -0
- cloudsmith_cli/core/config.py +140 -0
- cloudsmith_cli/core/credentials/__init__.py +0 -0
- cloudsmith_cli/core/credentials/chain.py +69 -0
- cloudsmith_cli/core/credentials/models.py +44 -0
- cloudsmith_cli/core/credentials/oidc/__init__.py +6 -0
- cloudsmith_cli/core/credentials/oidc/cache.py +220 -0
- cloudsmith_cli/core/credentials/oidc/detectors/__init__.py +122 -0
- cloudsmith_cli/core/credentials/oidc/detectors/aws.py +85 -0
- cloudsmith_cli/core/credentials/oidc/detectors/azure_devops.py +70 -0
- cloudsmith_cli/core/credentials/oidc/detectors/base.py +26 -0
- cloudsmith_cli/core/credentials/oidc/detectors/bitbucket_pipelines.py +35 -0
- cloudsmith_cli/core/credentials/oidc/detectors/circleci.py +40 -0
- cloudsmith_cli/core/credentials/oidc/detectors/generic.py +42 -0
- cloudsmith_cli/core/credentials/oidc/detectors/github_actions.py +64 -0
- cloudsmith_cli/core/credentials/oidc/detectors/gitlab_ci.py +49 -0
- cloudsmith_cli/core/credentials/oidc/exchange.py +87 -0
- cloudsmith_cli/core/credentials/provider.py +17 -0
- cloudsmith_cli/core/credentials/providers/__init__.py +15 -0
- cloudsmith_cli/core/credentials/providers/cli_flag.py +24 -0
- cloudsmith_cli/core/credentials/providers/credentials_file.py +24 -0
- cloudsmith_cli/core/credentials/providers/env_var.py +24 -0
- cloudsmith_cli/core/credentials/providers/keyring_provider.py +59 -0
- cloudsmith_cli/core/credentials/providers/oidc_provider.py +115 -0
- cloudsmith_cli/core/download.py +594 -0
- cloudsmith_cli/core/keyring.py +171 -0
- cloudsmith_cli/core/mcp/__init__.py +0 -0
- cloudsmith_cli/core/mcp/data.py +17 -0
- cloudsmith_cli/core/mcp/server.py +786 -0
- cloudsmith_cli/core/pagination.py +131 -0
- cloudsmith_cli/core/ratelimits.py +88 -0
- cloudsmith_cli/core/rest.py +255 -0
- cloudsmith_cli/core/utils.py +95 -0
- cloudsmith_cli/core/version.py +20 -0
- cloudsmith_cli/credential_helpers/__init__.py +7 -0
- cloudsmith_cli/credential_helpers/backends.py +41 -0
- cloudsmith_cli/credential_helpers/common.py +111 -0
- cloudsmith_cli/credential_helpers/custom_domains.py +281 -0
- cloudsmith_cli/credential_helpers/docker/__init__.py +4 -0
- cloudsmith_cli/credential_helpers/docker/installer.py +349 -0
- cloudsmith_cli/credential_helpers/docker/runtime.py +117 -0
- cloudsmith_cli/credential_helpers/launchers.py +175 -0
- cloudsmith_cli/data/VERSION +1 -0
- cloudsmith_cli/data/config.ini +23 -0
- cloudsmith_cli/data/credentials.ini +14 -0
- cloudsmith_cli/templates/__init__.py +3 -0
- cloudsmith_cli/templates/auth_error.html +45 -0
- cloudsmith_cli/templates/auth_success.html +37 -0
- cloudsmith_cli-1.20.0.dist-info/METADATA +610 -0
- cloudsmith_cli-1.20.0.dist-info/RECORD +130 -0
- cloudsmith_cli-1.20.0.dist-info/WHEEL +5 -0
- cloudsmith_cli-1.20.0.dist-info/entry_points.txt +2 -0
- cloudsmith_cli-1.20.0.dist-info/licenses/LICENSE +201 -0
- cloudsmith_cli-1.20.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
"""CLI/Commands - Manage metadata attached to packages."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ...core.api.metadata import (
|
|
8
|
+
create_metadata as api_create_metadata,
|
|
9
|
+
delete_metadata as api_delete_metadata,
|
|
10
|
+
get_metadata as api_get_metadata,
|
|
11
|
+
list_metadata as api_list_metadata,
|
|
12
|
+
update_metadata as api_update_metadata,
|
|
13
|
+
)
|
|
14
|
+
from ...core.api.packages import get_package_slug_perm as api_get_package_slug_perm
|
|
15
|
+
from ...core.pagination import paginate_results
|
|
16
|
+
from .. import command, decorators, utils, validators
|
|
17
|
+
from ..exceptions import handle_api_exceptions
|
|
18
|
+
from ..metadata_common import (
|
|
19
|
+
attach_metadata_options,
|
|
20
|
+
require_metadata_content_type,
|
|
21
|
+
resolve_metadata_content,
|
|
22
|
+
)
|
|
23
|
+
from ..utils import maybe_spinner
|
|
24
|
+
from .main import main
|
|
25
|
+
|
|
26
|
+
_METADATA_HEADERS = [
|
|
27
|
+
"Slug",
|
|
28
|
+
"Content type",
|
|
29
|
+
"Classification",
|
|
30
|
+
"Source kind",
|
|
31
|
+
"Source identity",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _format_metadata_row(entry):
|
|
36
|
+
return [
|
|
37
|
+
click.style(entry.get("slug_perm") or "", fg="cyan"),
|
|
38
|
+
click.style(entry.get("content_type") or "", fg="yellow"),
|
|
39
|
+
click.style(str(entry.get("classification", "")), fg="magenta"),
|
|
40
|
+
click.style(str(entry.get("source_kind", "")), fg="blue"),
|
|
41
|
+
click.style(entry.get("source_identity") or "", fg="green"),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _echo_action(message, use_stderr):
|
|
46
|
+
"""Print an in-progress status message."""
|
|
47
|
+
click.echo(message, nl=False, err=use_stderr)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _print_metadata_table(opts, entries, page_info=None, page_all=False):
|
|
51
|
+
"""Print a list of metadata entries as a table or JSON."""
|
|
52
|
+
if utils.maybe_print_as_json(
|
|
53
|
+
opts, list(entries), page_info=None if page_all else page_info
|
|
54
|
+
):
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
rows = [
|
|
58
|
+
_format_metadata_row(e)
|
|
59
|
+
for e in sorted(entries, key=lambda e: e.get("slug_perm") or "")
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
if rows:
|
|
63
|
+
click.echo()
|
|
64
|
+
utils.pretty_print_table(_METADATA_HEADERS, rows)
|
|
65
|
+
|
|
66
|
+
click.echo()
|
|
67
|
+
|
|
68
|
+
num_results = len(rows)
|
|
69
|
+
list_suffix = f"metadata entr{'ies' if num_results != 1 else 'y'}"
|
|
70
|
+
utils.pretty_print_list_info(
|
|
71
|
+
num_results=num_results,
|
|
72
|
+
page_info=None if page_all else page_info,
|
|
73
|
+
suffix=f"{list_suffix} retrieved" if page_all else f"{list_suffix} visible",
|
|
74
|
+
page_all=page_all,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _print_metadata_entry(opts, entry):
|
|
79
|
+
"""Print a single metadata entry as a table + indented JSON content."""
|
|
80
|
+
if utils.maybe_print_as_json(opts, entry):
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
click.echo()
|
|
84
|
+
utils.pretty_print_table(_METADATA_HEADERS, [_format_metadata_row(entry)])
|
|
85
|
+
click.echo()
|
|
86
|
+
|
|
87
|
+
content = entry.get("content")
|
|
88
|
+
if content is not None:
|
|
89
|
+
click.secho("Content:", bold=True)
|
|
90
|
+
click.echo(json.dumps(content, indent=2, sort_keys=True))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@main.group(name="metadata", cls=command.AliasGroup)
|
|
94
|
+
@decorators.common_cli_config_options
|
|
95
|
+
@decorators.common_cli_output_options
|
|
96
|
+
@decorators.common_api_auth_options
|
|
97
|
+
@decorators.initialise_api
|
|
98
|
+
@click.pass_context
|
|
99
|
+
def metadata_(ctx, opts): # pylint: disable=unused-argument
|
|
100
|
+
"""
|
|
101
|
+
Manage package metadata.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@metadata_.command(name="list", aliases=["ls"])
|
|
106
|
+
@decorators.common_cli_config_options
|
|
107
|
+
@decorators.common_cli_output_options
|
|
108
|
+
@decorators.common_cli_list_options
|
|
109
|
+
@decorators.common_api_auth_options
|
|
110
|
+
@decorators.initialise_api
|
|
111
|
+
@click.argument(
|
|
112
|
+
"owner_repo_package",
|
|
113
|
+
metavar="OWNER/REPO/PACKAGE",
|
|
114
|
+
callback=validators.validate_owner_repo_package,
|
|
115
|
+
)
|
|
116
|
+
@click.argument("metadata_slug_perm", required=False, default=None)
|
|
117
|
+
@click.option(
|
|
118
|
+
"--source-kind",
|
|
119
|
+
"source_kind",
|
|
120
|
+
default=None,
|
|
121
|
+
help=(
|
|
122
|
+
"Filter by source kind name (one of: unknown, system, upstream, "
|
|
123
|
+
"custom, third_party). Ignored when METADATA_SLUG_PERM is given."
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
@click.option(
|
|
127
|
+
"--classification",
|
|
128
|
+
"classification",
|
|
129
|
+
default=None,
|
|
130
|
+
help=(
|
|
131
|
+
"Filter by classification name (one of: unknown, intrinsic, security, "
|
|
132
|
+
"provenance, sbom, generic). Ignored when METADATA_SLUG_PERM is given."
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
@click.pass_context
|
|
136
|
+
def list_metadata(
|
|
137
|
+
ctx,
|
|
138
|
+
opts,
|
|
139
|
+
owner_repo_package,
|
|
140
|
+
metadata_slug_perm,
|
|
141
|
+
page,
|
|
142
|
+
page_size,
|
|
143
|
+
page_all,
|
|
144
|
+
source_kind,
|
|
145
|
+
classification,
|
|
146
|
+
):
|
|
147
|
+
"""
|
|
148
|
+
List package metadata.
|
|
149
|
+
|
|
150
|
+
OWNER/REPO/PACKAGE: target package.
|
|
151
|
+
|
|
152
|
+
METADATA_SLUG_PERM (optional): fetch one metadata entry. Pagination and
|
|
153
|
+
filters are ignored.
|
|
154
|
+
|
|
155
|
+
\b
|
|
156
|
+
Examples:
|
|
157
|
+
$ cloudsmith metadata list your-org/your-repo/your-pkg
|
|
158
|
+
$ cloudsmith metadata list your-org/your-repo/your-pkg --classification provenance
|
|
159
|
+
$ cloudsmith metadata list your-org/your-repo/your-pkg meta-slug-perm
|
|
160
|
+
"""
|
|
161
|
+
owner, repo, package = owner_repo_package
|
|
162
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
163
|
+
|
|
164
|
+
if metadata_slug_perm:
|
|
165
|
+
_echo_action(
|
|
166
|
+
"Fetching metadata %(metadata)s for %(package)s ... "
|
|
167
|
+
% {
|
|
168
|
+
"metadata": click.style(metadata_slug_perm, bold=True),
|
|
169
|
+
"package": click.style(package, bold=True),
|
|
170
|
+
},
|
|
171
|
+
use_stderr,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
context_msg = "Could not fetch package metadata."
|
|
175
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
176
|
+
with maybe_spinner(opts):
|
|
177
|
+
slug_perm = api_get_package_slug_perm(
|
|
178
|
+
owner=owner, repo=repo, identifier=package
|
|
179
|
+
)
|
|
180
|
+
entry = api_get_metadata(slug_perm, metadata_slug_perm)
|
|
181
|
+
|
|
182
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
183
|
+
_print_metadata_entry(opts, entry)
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
_echo_action(
|
|
187
|
+
"Listing metadata for %(package)s ... "
|
|
188
|
+
% {"package": click.style(package, bold=True)},
|
|
189
|
+
use_stderr,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
context_msg = "Could not list package metadata."
|
|
193
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
194
|
+
with maybe_spinner(opts):
|
|
195
|
+
slug_perm = api_get_package_slug_perm(
|
|
196
|
+
owner=owner, repo=repo, identifier=package
|
|
197
|
+
)
|
|
198
|
+
entries, page_info = paginate_results(
|
|
199
|
+
api_list_metadata,
|
|
200
|
+
page_all=page_all,
|
|
201
|
+
page=page,
|
|
202
|
+
page_size=page_size,
|
|
203
|
+
package_slug_perm=slug_perm,
|
|
204
|
+
source_kind=source_kind,
|
|
205
|
+
classification=classification,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
209
|
+
_print_metadata_table(opts, entries, page_info=page_info, page_all=page_all)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@metadata_.command(name="add")
|
|
213
|
+
@decorators.common_cli_config_options
|
|
214
|
+
@decorators.common_cli_output_options
|
|
215
|
+
@decorators.common_api_auth_options
|
|
216
|
+
@decorators.initialise_api
|
|
217
|
+
@click.argument(
|
|
218
|
+
"owner_repo_package",
|
|
219
|
+
metavar="OWNER/REPO/PACKAGE",
|
|
220
|
+
callback=validators.validate_owner_repo_package,
|
|
221
|
+
)
|
|
222
|
+
@click.option(
|
|
223
|
+
"--content-type",
|
|
224
|
+
"content_type",
|
|
225
|
+
required=True,
|
|
226
|
+
help=(
|
|
227
|
+
"Content type for metadata content (for example, 'application/json'). "
|
|
228
|
+
"Content type is immutable after creation."
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
@click.option(
|
|
232
|
+
"--file",
|
|
233
|
+
"content_file",
|
|
234
|
+
type=click.Path(
|
|
235
|
+
exists=True,
|
|
236
|
+
dir_okay=False,
|
|
237
|
+
readable=True,
|
|
238
|
+
resolve_path=True,
|
|
239
|
+
allow_dash=True,
|
|
240
|
+
),
|
|
241
|
+
default=None,
|
|
242
|
+
help="Read metadata content from a JSON file. Use '-' for stdin.",
|
|
243
|
+
)
|
|
244
|
+
@click.option(
|
|
245
|
+
"--content",
|
|
246
|
+
"inline_content",
|
|
247
|
+
default=None,
|
|
248
|
+
help="Set metadata content from inline JSON. Cannot be used with --file.",
|
|
249
|
+
)
|
|
250
|
+
@click.option(
|
|
251
|
+
"--source-identity",
|
|
252
|
+
"source_identity",
|
|
253
|
+
default=None,
|
|
254
|
+
help=(
|
|
255
|
+
"Identifier for the metadata source. " "Defaults to 'cloudsmith-cli@<version>'."
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
@click.pass_context
|
|
259
|
+
def add_metadata(
|
|
260
|
+
ctx,
|
|
261
|
+
opts,
|
|
262
|
+
owner_repo_package,
|
|
263
|
+
content_type,
|
|
264
|
+
content_file,
|
|
265
|
+
inline_content,
|
|
266
|
+
source_identity,
|
|
267
|
+
):
|
|
268
|
+
"""
|
|
269
|
+
Attach metadata to a package.
|
|
270
|
+
|
|
271
|
+
OWNER/REPO/PACKAGE: target package.
|
|
272
|
+
|
|
273
|
+
Exactly one of --file or --content must be provided.
|
|
274
|
+
Content type is set on creation and cannot be changed.
|
|
275
|
+
|
|
276
|
+
\b
|
|
277
|
+
Examples:
|
|
278
|
+
$ cloudsmith metadata add your-org/your-repo/your-pkg \\
|
|
279
|
+
--content-type application/json \\
|
|
280
|
+
--content '{"foo": "bar"}'
|
|
281
|
+
$ cat metadata.json | cloudsmith metadata add your-org/your-repo/your-pkg \\
|
|
282
|
+
--content-type application/json \\
|
|
283
|
+
--file -
|
|
284
|
+
$ cloudsmith metadata add your-org/your-repo/your-pkg \\
|
|
285
|
+
--content-type application/vnd.jfrog.buildinfo+json \\
|
|
286
|
+
--file buildinfo.json
|
|
287
|
+
"""
|
|
288
|
+
owner, repo, package = owner_repo_package
|
|
289
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
290
|
+
|
|
291
|
+
metadata = resolve_metadata_content(
|
|
292
|
+
content_file=content_file,
|
|
293
|
+
inline_content=inline_content,
|
|
294
|
+
required=True,
|
|
295
|
+
file_option_name="--file",
|
|
296
|
+
content_option_name="--content",
|
|
297
|
+
)
|
|
298
|
+
require_metadata_content_type(
|
|
299
|
+
content_type=content_type,
|
|
300
|
+
content_provided=metadata.provided,
|
|
301
|
+
option_name="--content-type",
|
|
302
|
+
)
|
|
303
|
+
metadata = attach_metadata_options(
|
|
304
|
+
metadata,
|
|
305
|
+
content_type=content_type,
|
|
306
|
+
source_identity=source_identity,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
_echo_action(
|
|
310
|
+
"Attaching metadata to %(package)s ... "
|
|
311
|
+
% {"package": click.style(package, bold=True)},
|
|
312
|
+
use_stderr,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
context_msg = "Could not attach metadata."
|
|
316
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
317
|
+
with maybe_spinner(opts):
|
|
318
|
+
slug_perm = api_get_package_slug_perm(
|
|
319
|
+
owner=owner, repo=repo, identifier=package
|
|
320
|
+
)
|
|
321
|
+
entry = api_create_metadata(
|
|
322
|
+
slug_perm,
|
|
323
|
+
content=metadata.content,
|
|
324
|
+
content_type=metadata.content_type,
|
|
325
|
+
source_identity=metadata.source_identity,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
329
|
+
_print_metadata_entry(opts, entry)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@metadata_.command(name="update")
|
|
333
|
+
@decorators.common_cli_config_options
|
|
334
|
+
@decorators.common_cli_output_options
|
|
335
|
+
@decorators.common_api_auth_options
|
|
336
|
+
@decorators.initialise_api
|
|
337
|
+
@click.argument(
|
|
338
|
+
"owner_repo_package",
|
|
339
|
+
metavar="OWNER/REPO/PACKAGE",
|
|
340
|
+
callback=validators.validate_owner_repo_package,
|
|
341
|
+
)
|
|
342
|
+
@click.argument("metadata_slug_perm")
|
|
343
|
+
@click.option(
|
|
344
|
+
"--file",
|
|
345
|
+
"content_file",
|
|
346
|
+
type=click.Path(
|
|
347
|
+
exists=True,
|
|
348
|
+
dir_okay=False,
|
|
349
|
+
readable=True,
|
|
350
|
+
resolve_path=True,
|
|
351
|
+
allow_dash=True,
|
|
352
|
+
),
|
|
353
|
+
default=None,
|
|
354
|
+
help="Read replacement metadata content from a JSON file. Use '-' for stdin.",
|
|
355
|
+
)
|
|
356
|
+
@click.option(
|
|
357
|
+
"--content",
|
|
358
|
+
"inline_content",
|
|
359
|
+
default=None,
|
|
360
|
+
help=(
|
|
361
|
+
"Set replacement metadata content from inline JSON. Cannot be used with "
|
|
362
|
+
"--file."
|
|
363
|
+
),
|
|
364
|
+
)
|
|
365
|
+
@click.option(
|
|
366
|
+
"--source-identity",
|
|
367
|
+
"source_identity",
|
|
368
|
+
default=None,
|
|
369
|
+
help="Update the metadata source identity.",
|
|
370
|
+
)
|
|
371
|
+
@click.pass_context
|
|
372
|
+
def update_metadata(
|
|
373
|
+
ctx,
|
|
374
|
+
opts,
|
|
375
|
+
owner_repo_package,
|
|
376
|
+
metadata_slug_perm,
|
|
377
|
+
content_file,
|
|
378
|
+
inline_content,
|
|
379
|
+
source_identity,
|
|
380
|
+
):
|
|
381
|
+
"""
|
|
382
|
+
Update package metadata.
|
|
383
|
+
|
|
384
|
+
OWNER/REPO/PACKAGE: target package.
|
|
385
|
+
METADATA_SLUG_PERM: permanent slug for the metadata entry.
|
|
386
|
+
|
|
387
|
+
Content type cannot be changed after creation.
|
|
388
|
+
|
|
389
|
+
\b
|
|
390
|
+
Examples:
|
|
391
|
+
$ cloudsmith metadata update your-org/your-repo/your-pkg meta-slug \\
|
|
392
|
+
--content '{"foo": "baz"}'
|
|
393
|
+
$ cat metadata.json | cloudsmith metadata update your-org/your-repo/your-pkg meta-slug \\
|
|
394
|
+
--file -
|
|
395
|
+
"""
|
|
396
|
+
owner, repo, package = owner_repo_package
|
|
397
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
398
|
+
|
|
399
|
+
metadata = resolve_metadata_content(
|
|
400
|
+
content_file=content_file,
|
|
401
|
+
inline_content=inline_content,
|
|
402
|
+
required=False,
|
|
403
|
+
file_option_name="--file",
|
|
404
|
+
content_option_name="--content",
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
patch_kwargs = {}
|
|
408
|
+
if metadata.provided:
|
|
409
|
+
patch_kwargs["content"] = metadata.content
|
|
410
|
+
if source_identity is not None:
|
|
411
|
+
patch_kwargs["source_identity"] = source_identity
|
|
412
|
+
if not patch_kwargs:
|
|
413
|
+
raise click.UsageError(
|
|
414
|
+
"Nothing to update. Provide --file, --content, or --source-identity."
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
_echo_action(
|
|
418
|
+
"Updating metadata %(metadata)s for %(package)s ... "
|
|
419
|
+
% {
|
|
420
|
+
"metadata": click.style(metadata_slug_perm, bold=True),
|
|
421
|
+
"package": click.style(package, bold=True),
|
|
422
|
+
},
|
|
423
|
+
use_stderr,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
context_msg = "Could not update package metadata."
|
|
427
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
428
|
+
with maybe_spinner(opts):
|
|
429
|
+
slug_perm = api_get_package_slug_perm(
|
|
430
|
+
owner=owner, repo=repo, identifier=package
|
|
431
|
+
)
|
|
432
|
+
entry = api_update_metadata(slug_perm, metadata_slug_perm, **patch_kwargs)
|
|
433
|
+
|
|
434
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
435
|
+
_print_metadata_entry(opts, entry)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@metadata_.command(name="remove", aliases=["rm"])
|
|
439
|
+
@decorators.common_cli_config_options
|
|
440
|
+
@decorators.common_cli_output_options
|
|
441
|
+
@decorators.common_api_auth_options
|
|
442
|
+
@decorators.initialise_api
|
|
443
|
+
@click.argument(
|
|
444
|
+
"owner_repo_package",
|
|
445
|
+
metavar="OWNER/REPO/PACKAGE",
|
|
446
|
+
callback=validators.validate_owner_repo_package,
|
|
447
|
+
)
|
|
448
|
+
@click.argument("metadata_slug_perm")
|
|
449
|
+
@click.option(
|
|
450
|
+
"-y",
|
|
451
|
+
"--yes",
|
|
452
|
+
default=False,
|
|
453
|
+
is_flag=True,
|
|
454
|
+
help="Skip confirmation prompt.",
|
|
455
|
+
)
|
|
456
|
+
@click.pass_context
|
|
457
|
+
def remove_metadata(ctx, opts, owner_repo_package, metadata_slug_perm, yes):
|
|
458
|
+
"""
|
|
459
|
+
Remove package metadata.
|
|
460
|
+
|
|
461
|
+
OWNER/REPO/PACKAGE: target package.
|
|
462
|
+
METADATA_SLUG_PERM: permanent slug for the metadata entry.
|
|
463
|
+
|
|
464
|
+
\b
|
|
465
|
+
Example:
|
|
466
|
+
$ cloudsmith metadata remove your-org/your-repo/your-pkg meta-slug
|
|
467
|
+
"""
|
|
468
|
+
owner, repo, package = owner_repo_package
|
|
469
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
470
|
+
|
|
471
|
+
remove_args = {
|
|
472
|
+
"metadata": click.style(metadata_slug_perm, bold=True),
|
|
473
|
+
"package": click.style(package, bold=True),
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
prompt = "remove metadata %(metadata)s from package %(package)s" % remove_args
|
|
477
|
+
if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
|
|
478
|
+
return
|
|
479
|
+
|
|
480
|
+
_echo_action(
|
|
481
|
+
"Removing metadata %(metadata)s from %(package)s ... " % remove_args,
|
|
482
|
+
use_stderr,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
context_msg = "Could not remove package metadata."
|
|
486
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
487
|
+
with maybe_spinner(opts):
|
|
488
|
+
slug_perm = api_get_package_slug_perm(
|
|
489
|
+
owner=owner, repo=repo, identifier=package
|
|
490
|
+
)
|
|
491
|
+
api_delete_metadata(slug_perm, metadata_slug_perm)
|
|
492
|
+
|
|
493
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
494
|
+
|
|
495
|
+
result_payload = {"deleted": True, "slug_perm": metadata_slug_perm}
|
|
496
|
+
if utils.maybe_print_as_json(opts, result_payload):
|
|
497
|
+
return
|
|
498
|
+
|
|
499
|
+
click.echo()
|
|
500
|
+
click.secho(
|
|
501
|
+
"Metadata removed: %(slug)s."
|
|
502
|
+
% {"slug": click.style(metadata_slug_perm, bold=True)}
|
|
503
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""CLI/Commands - Import all metric commands."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from ... import command, decorators
|
|
6
|
+
from ..main import main
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@main.group(cls=command.AliasGroup, name="metrics", aliases=[])
|
|
10
|
+
@decorators.common_cli_config_options
|
|
11
|
+
@decorators.common_cli_output_options
|
|
12
|
+
@decorators.common_api_auth_options
|
|
13
|
+
@decorators.initialise_api
|
|
14
|
+
@click.pass_context
|
|
15
|
+
def metrics(ctx, opts): # pylink: disable=unused-argument
|
|
16
|
+
"""
|
|
17
|
+
Retrieve Metrics.
|
|
18
|
+
|
|
19
|
+
See the help for subcommands for more information on each.
|
|
20
|
+
"""
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""CLI/Commands - retrieve metrics."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from ....core.api import metrics as api
|
|
6
|
+
from ... import decorators, utils, validators
|
|
7
|
+
from ...exceptions import handle_api_exceptions
|
|
8
|
+
from ...utils import maybe_spinner
|
|
9
|
+
from .command import metrics
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _print_activity_table(opts, data):
|
|
13
|
+
"""Print token activity as a table."""
|
|
14
|
+
utils.pretty_print_table(
|
|
15
|
+
headers=["Active", "Inactive", "Total"],
|
|
16
|
+
rows=[
|
|
17
|
+
[
|
|
18
|
+
click.style(str(getattr(data, k, 0)), fg="green")
|
|
19
|
+
for k in ("active", "inactive", "total")
|
|
20
|
+
]
|
|
21
|
+
],
|
|
22
|
+
title="Activity Summary (Active = Has Downloads)",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
click.echo()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _print_metrics_table(opts, data):
|
|
29
|
+
"""Print metrics as a table."""
|
|
30
|
+
category_keys = {"Bandwidth": "bandwidth", "Downloads": "downloads"}
|
|
31
|
+
|
|
32
|
+
metrics_keys = {
|
|
33
|
+
"Average": "average",
|
|
34
|
+
"Lowest": "lowest",
|
|
35
|
+
"Highest": "highest",
|
|
36
|
+
"Total": "total",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
headers = ["Metric"]
|
|
40
|
+
headers.extend(metrics_keys.keys())
|
|
41
|
+
rows = []
|
|
42
|
+
|
|
43
|
+
for category_header, category_key in category_keys.items():
|
|
44
|
+
category_data = getattr(data, category_key)
|
|
45
|
+
cols = [category_header]
|
|
46
|
+
for metric_key in metrics_keys.values():
|
|
47
|
+
metric_data = getattr(category_data, metric_key, {})
|
|
48
|
+
if hasattr(metric_data, "display"):
|
|
49
|
+
value = getattr(metric_data, "display")
|
|
50
|
+
else:
|
|
51
|
+
value = getattr(metric_data, "value")
|
|
52
|
+
value = str(value or 0)
|
|
53
|
+
cols.append(click.style(value, fg="green"))
|
|
54
|
+
rows.append(cols)
|
|
55
|
+
|
|
56
|
+
utils.pretty_print_table(headers=headers, rows=rows, title="Entitlement Metrics")
|
|
57
|
+
|
|
58
|
+
click.echo()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@metrics.command(name="entitlements", aliases=["ents", "tokens"])
|
|
62
|
+
@decorators.common_cli_config_options
|
|
63
|
+
@decorators.common_cli_output_options
|
|
64
|
+
@decorators.common_api_auth_options
|
|
65
|
+
@decorators.initialise_api
|
|
66
|
+
@click.argument(
|
|
67
|
+
"owner_repo",
|
|
68
|
+
metavar="OWNER or OWNER/REPO",
|
|
69
|
+
callback=validators.validate_required_owner_optional_repo,
|
|
70
|
+
default="",
|
|
71
|
+
required=True,
|
|
72
|
+
)
|
|
73
|
+
@click.option(
|
|
74
|
+
"--tokens",
|
|
75
|
+
type=str,
|
|
76
|
+
required=False,
|
|
77
|
+
help=(
|
|
78
|
+
"A comma separated list of entitlement token identifiers (i.e. slug_perm) or "
|
|
79
|
+
"token secrets. If a list is not specified then all entitlement tokens will "
|
|
80
|
+
"be included for a given namespace or repository."
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--start",
|
|
85
|
+
type=str,
|
|
86
|
+
required=False,
|
|
87
|
+
help=(
|
|
88
|
+
"Include metrics from and including this UTC date or UTC datetime. "
|
|
89
|
+
"For example '2020-12-31' or '2021-12-13T00:00:00Z'."
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--finish",
|
|
94
|
+
type=str,
|
|
95
|
+
required=False,
|
|
96
|
+
help=(
|
|
97
|
+
"Include metrics upto and including this UTC date or UTC datetime. "
|
|
98
|
+
"For example '2020-12-31' or '2021-12-13T00:00:00Z'."
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
@click.pass_context
|
|
102
|
+
def usage(ctx, opts, owner_repo, tokens, start, finish):
|
|
103
|
+
"""
|
|
104
|
+
Retrieve token usage metrics.
|
|
105
|
+
|
|
106
|
+
OWNER/REPO: Specify the OWNER namespace (i.e user or org) and repository to retrieve the
|
|
107
|
+
metrics for that namespace/repository combination.
|
|
108
|
+
|
|
109
|
+
If REPO isn't specified, all repositories will be included from the
|
|
110
|
+
OWNER namespace.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
114
|
+
|
|
115
|
+
click.echo("Getting usage metrics ... ", nl=False, err=use_stderr)
|
|
116
|
+
|
|
117
|
+
owner = None
|
|
118
|
+
repo = None
|
|
119
|
+
|
|
120
|
+
if isinstance(owner_repo, list) and len(owner_repo) == 2:
|
|
121
|
+
owner, repo = owner_repo
|
|
122
|
+
elif len(owner_repo) == 1:
|
|
123
|
+
owner = owner_repo[0]
|
|
124
|
+
repo = None
|
|
125
|
+
|
|
126
|
+
data = None
|
|
127
|
+
context_msg = "Failed to get list of metrics!"
|
|
128
|
+
data = {}
|
|
129
|
+
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
130
|
+
with maybe_spinner(opts):
|
|
131
|
+
if owner and repo:
|
|
132
|
+
data = api.get_repository_entitlements_metrics(
|
|
133
|
+
owner=owner, repo=repo, tokens=tokens, start=start, finish=finish
|
|
134
|
+
)
|
|
135
|
+
elif owner:
|
|
136
|
+
data = api.get_namespace_entitlements_metrics(
|
|
137
|
+
owner=owner, repo=repo, tokens=tokens, start=start, finish=finish
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
141
|
+
|
|
142
|
+
if utils.maybe_print_as_json(opts, data):
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
click.echo()
|
|
146
|
+
|
|
147
|
+
_print_activity_table(opts, data)
|
|
148
|
+
_print_metrics_table(opts, data)
|