cloudsmith-cli 1.10.2__py2.py3-none-any.whl → 1.10.4__py2.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/cli/command.py +66 -0
- cloudsmith_cli/cli/commands/auth.py +25 -12
- cloudsmith_cli/cli/commands/check.py +18 -4
- cloudsmith_cli/cli/commands/copy.py +8 -2
- cloudsmith_cli/cli/commands/delete.py +9 -3
- cloudsmith_cli/cli/commands/dependencies.py +1 -1
- cloudsmith_cli/cli/commands/download.py +89 -8
- cloudsmith_cli/cli/commands/entitlements.py +11 -7
- cloudsmith_cli/cli/commands/list_.py +2 -2
- cloudsmith_cli/cli/commands/login.py +21 -13
- cloudsmith_cli/cli/commands/metrics/entitlements.py +2 -2
- cloudsmith_cli/cli/commands/metrics/packages.py +1 -1
- cloudsmith_cli/cli/commands/move.py +10 -3
- cloudsmith_cli/cli/commands/policy/deny.py +8 -6
- cloudsmith_cli/cli/commands/policy/license.py +5 -3
- cloudsmith_cli/cli/commands/policy/vulnerability.py +6 -3
- cloudsmith_cli/cli/commands/push.py +146 -64
- cloudsmith_cli/cli/commands/quarantine.py +7 -4
- cloudsmith_cli/cli/commands/quota/history.py +1 -1
- cloudsmith_cli/cli/commands/quota/quota.py +1 -1
- cloudsmith_cli/cli/commands/repos.py +5 -3
- cloudsmith_cli/cli/commands/resync.py +7 -2
- cloudsmith_cli/cli/commands/status.py +21 -3
- cloudsmith_cli/cli/commands/tags.py +20 -5
- cloudsmith_cli/cli/commands/tokens.py +37 -25
- cloudsmith_cli/cli/commands/upstream.py +2 -3
- cloudsmith_cli/cli/commands/whoami.py +4 -5
- cloudsmith_cli/cli/exceptions.py +86 -49
- cloudsmith_cli/cli/utils.py +17 -2
- cloudsmith_cli/cli/webserver.py +1 -1
- cloudsmith_cli/data/VERSION +1 -1
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/METADATA +1 -1
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/RECORD +37 -37
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/WHEEL +0 -0
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/entry_points.txt +0 -0
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/licenses/LICENSE +0 -0
- {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
from ...core.api.packages import get_package_status
|
|
6
|
-
from .. import decorators, validators
|
|
6
|
+
from .. import decorators, utils, validators
|
|
7
7
|
from ..exceptions import handle_api_exceptions
|
|
8
8
|
from ..utils import maybe_spinner
|
|
9
9
|
from .main import main
|
|
@@ -32,6 +32,8 @@ def status(ctx, opts, owner_repo_package):
|
|
|
32
32
|
"""
|
|
33
33
|
owner, repo, slug = owner_repo_package
|
|
34
34
|
|
|
35
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
36
|
+
|
|
35
37
|
click.echo(
|
|
36
38
|
"Getting status of %(package)s in %(owner)s/%(repo)s ... "
|
|
37
39
|
% {
|
|
@@ -40,6 +42,7 @@ def status(ctx, opts, owner_repo_package):
|
|
|
40
42
|
"package": click.style(slug, bold=True),
|
|
41
43
|
},
|
|
42
44
|
nl=False,
|
|
45
|
+
err=use_stderr,
|
|
43
46
|
)
|
|
44
47
|
|
|
45
48
|
context_msg = "Failed to get status of package!"
|
|
@@ -48,7 +51,7 @@ def status(ctx, opts, owner_repo_package):
|
|
|
48
51
|
res = get_package_status(owner, repo, slug)
|
|
49
52
|
ok, failed, _, status_str, stage_str, reason = res
|
|
50
53
|
|
|
51
|
-
click.secho("OK", fg="green")
|
|
54
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
52
55
|
|
|
53
56
|
if not stage_str:
|
|
54
57
|
package_status = status_str
|
|
@@ -62,13 +65,28 @@ def status(ctx, opts, owner_repo_package):
|
|
|
62
65
|
else:
|
|
63
66
|
status_colour = "magenta"
|
|
64
67
|
|
|
68
|
+
if utils.maybe_print_as_json(
|
|
69
|
+
opts,
|
|
70
|
+
{
|
|
71
|
+
"ok": ok,
|
|
72
|
+
"failed": failed,
|
|
73
|
+
"status": status_str,
|
|
74
|
+
"stage": stage_str,
|
|
75
|
+
"reason": reason,
|
|
76
|
+
"slug": slug,
|
|
77
|
+
},
|
|
78
|
+
):
|
|
79
|
+
return
|
|
80
|
+
|
|
65
81
|
click.secho(
|
|
66
82
|
"The package status is: %(status)s"
|
|
67
|
-
% {"status": click.style(package_status, fg=status_colour)}
|
|
83
|
+
% {"status": click.style(package_status, fg=status_colour)},
|
|
84
|
+
err=use_stderr,
|
|
68
85
|
)
|
|
69
86
|
|
|
70
87
|
if reason:
|
|
71
88
|
click.secho(
|
|
72
89
|
f"Reason given: {click.style(reason, fg='yellow')}",
|
|
73
90
|
fg=status_colour,
|
|
91
|
+
err=use_stderr,
|
|
74
92
|
)
|
|
@@ -95,10 +95,13 @@ def list_tags(ctx, opts, owner_repo_package):
|
|
|
95
95
|
"""
|
|
96
96
|
owner, repo, package = owner_repo_package
|
|
97
97
|
|
|
98
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
99
|
+
|
|
98
100
|
click.echo(
|
|
99
101
|
"Listing tags for the '%(package)s' package ... "
|
|
100
102
|
% {"package": click.style(package, bold=True)},
|
|
101
103
|
nl=False,
|
|
104
|
+
err=use_stderr,
|
|
102
105
|
)
|
|
103
106
|
|
|
104
107
|
context_msg = "Failed to list tags for the package!"
|
|
@@ -108,7 +111,7 @@ def list_tags(ctx, opts, owner_repo_package):
|
|
|
108
111
|
owner=owner, repo=repo, identifier=package
|
|
109
112
|
)
|
|
110
113
|
|
|
111
|
-
click.secho("OK", fg="green")
|
|
114
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
112
115
|
|
|
113
116
|
_print_tags(opts, package_tags, package_tags_immutable)
|
|
114
117
|
|
|
@@ -158,6 +161,8 @@ def add_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
158
161
|
owner, repo, package = owner_repo_package
|
|
159
162
|
tags = _parse_tags(tags)
|
|
160
163
|
|
|
164
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
165
|
+
|
|
161
166
|
click.echo(
|
|
162
167
|
"Adding '%(tags)s' tag%(s)s to the '%(package)s' package ... "
|
|
163
168
|
% {
|
|
@@ -166,6 +171,7 @@ def add_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
166
171
|
"s": "s" if len(tags) != 1 else "",
|
|
167
172
|
},
|
|
168
173
|
nl=False,
|
|
174
|
+
err=use_stderr,
|
|
169
175
|
)
|
|
170
176
|
|
|
171
177
|
context_msg = "Failed to add tags to package!"
|
|
@@ -178,7 +184,7 @@ def add_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
178
184
|
data={"action": "add", "tags": tags, "is_immutable": immutable},
|
|
179
185
|
)
|
|
180
186
|
|
|
181
|
-
click.secho("OK", fg="green")
|
|
187
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
182
188
|
|
|
183
189
|
_print_tags(opts, package_tags, package_tags_immutable)
|
|
184
190
|
|
|
@@ -212,10 +218,13 @@ def clear_tags(ctx, opts, owner_repo_package):
|
|
|
212
218
|
"""
|
|
213
219
|
owner, repo, package = owner_repo_package
|
|
214
220
|
|
|
221
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
222
|
+
|
|
215
223
|
click.echo(
|
|
216
224
|
"Clearing tags on the '%(package)s' package ... "
|
|
217
225
|
% {"package": click.style(package, bold=True)},
|
|
218
226
|
nl=False,
|
|
227
|
+
err=use_stderr,
|
|
219
228
|
)
|
|
220
229
|
|
|
221
230
|
context_msg = "Failed to clear tags on package!"
|
|
@@ -225,7 +234,7 @@ def clear_tags(ctx, opts, owner_repo_package):
|
|
|
225
234
|
owner=owner, repo=repo, identifier=package, data={"action": "clear"}
|
|
226
235
|
)
|
|
227
236
|
|
|
228
|
-
click.secho("OK", fg="green")
|
|
237
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
229
238
|
|
|
230
239
|
_print_tags(opts, package_tags, package_tags_immutable)
|
|
231
240
|
|
|
@@ -265,6 +274,8 @@ def remove_tags(ctx, opts, owner_repo_package, tags):
|
|
|
265
274
|
owner, repo, package = owner_repo_package
|
|
266
275
|
tags = _parse_tags(tags)
|
|
267
276
|
|
|
277
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
278
|
+
|
|
268
279
|
click.echo(
|
|
269
280
|
"Removing '%(tags)s' tag%(s)s from the '%(package)s' package ... "
|
|
270
281
|
% {
|
|
@@ -273,6 +284,7 @@ def remove_tags(ctx, opts, owner_repo_package, tags):
|
|
|
273
284
|
"s": "s" if len(tags) != 1 else "",
|
|
274
285
|
},
|
|
275
286
|
nl=False,
|
|
287
|
+
err=use_stderr,
|
|
276
288
|
)
|
|
277
289
|
|
|
278
290
|
context_msg = "Failed to remove tags from package!"
|
|
@@ -285,7 +297,7 @@ def remove_tags(ctx, opts, owner_repo_package, tags):
|
|
|
285
297
|
data={"action": "remove", "tags": tags},
|
|
286
298
|
)
|
|
287
299
|
|
|
288
|
-
click.secho("OK", fg="green")
|
|
300
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
289
301
|
|
|
290
302
|
_print_tags(opts, package_tags, package_tags_immutable)
|
|
291
303
|
|
|
@@ -335,6 +347,8 @@ def replace_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
335
347
|
owner, repo, package = owner_repo_package
|
|
336
348
|
tags = _parse_tags(tags)
|
|
337
349
|
|
|
350
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
351
|
+
|
|
338
352
|
click.echo(
|
|
339
353
|
"Replacing existing with '%(tags)s' tag%(s)s on the '%(package)s' package ... "
|
|
340
354
|
% {
|
|
@@ -343,6 +357,7 @@ def replace_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
343
357
|
"s": "s" if len(tags) != 1 else "",
|
|
344
358
|
},
|
|
345
359
|
nl=False,
|
|
360
|
+
err=use_stderr,
|
|
346
361
|
)
|
|
347
362
|
|
|
348
363
|
context_msg = "Failed to replace tags on package!"
|
|
@@ -355,6 +370,6 @@ def replace_tags(ctx, opts, owner_repo_package, tags, immutable):
|
|
|
355
370
|
data={"action": "replace", "tags": tags, "is_immutable": immutable},
|
|
356
371
|
)
|
|
357
372
|
|
|
358
|
-
click.secho("OK", fg="green")
|
|
373
|
+
click.secho("OK", fg="green", err=use_stderr)
|
|
359
374
|
|
|
360
375
|
_print_tags(opts, package_tags, package_tags_immutable)
|
|
@@ -2,9 +2,8 @@ import click
|
|
|
2
2
|
|
|
3
3
|
from ...core.api import exceptions, user as api
|
|
4
4
|
from ...core.config import create_config_files, new_config_messaging
|
|
5
|
-
from .. import command, decorators
|
|
5
|
+
from .. import command, decorators, utils
|
|
6
6
|
from ..exceptions import handle_api_exceptions
|
|
7
|
-
from ..utils import maybe_print_as_json, maybe_spinner
|
|
8
7
|
from .main import main
|
|
9
8
|
|
|
10
9
|
|
|
@@ -23,6 +22,7 @@ def handle_duplicate_token_error(exc, ctx, opts, save_config, force, json):
|
|
|
23
22
|
if not click.confirm(
|
|
24
23
|
"User already has a token. Would you like to recreate it?",
|
|
25
24
|
abort=False,
|
|
25
|
+
err=json,
|
|
26
26
|
):
|
|
27
27
|
return None
|
|
28
28
|
return refresh_existing_token_interactive(
|
|
@@ -47,17 +47,17 @@ def tokens(ctx, opts):
|
|
|
47
47
|
@click.pass_context
|
|
48
48
|
def list_tokens(ctx, opts):
|
|
49
49
|
"""List all user API tokens."""
|
|
50
|
-
use_stderr =
|
|
50
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
51
51
|
|
|
52
52
|
click.echo("Retrieving API tokens... ", nl=False, err=use_stderr)
|
|
53
53
|
|
|
54
54
|
context_msg = "Failed to retrieve API tokens!"
|
|
55
55
|
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
56
|
-
with maybe_spinner(opts):
|
|
56
|
+
with utils.maybe_spinner(opts):
|
|
57
57
|
tokens = api.list_user_tokens()
|
|
58
58
|
click.secho("OK", fg="green", err=use_stderr)
|
|
59
59
|
|
|
60
|
-
if maybe_print_as_json(opts, tokens):
|
|
60
|
+
if utils.maybe_print_as_json(opts, tokens):
|
|
61
61
|
return
|
|
62
62
|
|
|
63
63
|
print_tokens(tokens)
|
|
@@ -123,7 +123,7 @@ def refresh(ctx, opts, token_slug, force, save_config):
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
if new_token:
|
|
126
|
-
if maybe_print_as_json(opts, new_token):
|
|
126
|
+
if utils.maybe_print_as_json(opts, new_token):
|
|
127
127
|
return new_token
|
|
128
128
|
|
|
129
129
|
|
|
@@ -140,6 +140,8 @@ def refresh_existing_token_interactive(
|
|
|
140
140
|
ctx, opts, token_slug=None, save_config=False, force=False, json=False
|
|
141
141
|
):
|
|
142
142
|
"""Refresh an existing API token with interactive token selection, or create new if none exist."""
|
|
143
|
+
json = json or utils.should_use_stderr(opts)
|
|
144
|
+
use_stderr = json
|
|
143
145
|
context_msg = "Failed to refresh the token!"
|
|
144
146
|
|
|
145
147
|
if not token_slug:
|
|
@@ -151,13 +153,17 @@ def refresh_existing_token_interactive(
|
|
|
151
153
|
if opts.debug:
|
|
152
154
|
click.echo(f"Debug: Failed to list tokens with error: {exc}", err=True)
|
|
153
155
|
click.echo(
|
|
154
|
-
"Unable to list existing tokens. Creating a new token instead..."
|
|
156
|
+
"Unable to list existing tokens. Creating a new token instead...",
|
|
157
|
+
err=use_stderr,
|
|
155
158
|
)
|
|
156
|
-
return _create(ctx, opts, save_config=save_config, force=force)
|
|
159
|
+
return _create(ctx, opts, save_config=save_config, force=force, json=json)
|
|
157
160
|
|
|
158
161
|
if not api_tokens:
|
|
159
|
-
click.echo(
|
|
160
|
-
|
|
162
|
+
click.echo(
|
|
163
|
+
"No existing tokens found. Creating a new token instead...",
|
|
164
|
+
err=use_stderr,
|
|
165
|
+
)
|
|
166
|
+
return _create(ctx, opts, save_config=save_config, force=force, json=json)
|
|
161
167
|
|
|
162
168
|
if not json:
|
|
163
169
|
click.echo("Current tokens:")
|
|
@@ -165,7 +171,8 @@ def refresh_existing_token_interactive(
|
|
|
165
171
|
|
|
166
172
|
if not force:
|
|
167
173
|
token_slug = click.prompt(
|
|
168
|
-
"Please enter the slug_perm of the token you would like to refresh"
|
|
174
|
+
"Please enter the slug_perm of the token you would like to refresh",
|
|
175
|
+
err=use_stderr,
|
|
169
176
|
)
|
|
170
177
|
else:
|
|
171
178
|
# Use the first available slug_perm when force is enabled
|
|
@@ -175,9 +182,13 @@ def refresh_existing_token_interactive(
|
|
|
175
182
|
|
|
176
183
|
if not json:
|
|
177
184
|
click.echo(f"Refreshing token {token_slug}... ", nl=False)
|
|
185
|
+
else:
|
|
186
|
+
# In JSON mode, print info to stderr
|
|
187
|
+
click.echo(f"Refreshing token {token_slug}... ", nl=False, err=json)
|
|
188
|
+
|
|
178
189
|
try:
|
|
179
190
|
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
180
|
-
with maybe_spinner(opts):
|
|
191
|
+
with utils.maybe_spinner(opts):
|
|
181
192
|
new_token = api.refresh_user_token(token_slug)
|
|
182
193
|
|
|
183
194
|
if save_config:
|
|
@@ -186,7 +197,7 @@ def refresh_existing_token_interactive(
|
|
|
186
197
|
)
|
|
187
198
|
new_config_messaging(has_errors, opts, create, api_key=new_token.key)
|
|
188
199
|
|
|
189
|
-
if maybe_print_as_json(opts, new_token):
|
|
200
|
+
if utils.maybe_print_as_json(opts, new_token):
|
|
190
201
|
return
|
|
191
202
|
|
|
192
203
|
if not json:
|
|
@@ -198,35 +209,36 @@ def refresh_existing_token_interactive(
|
|
|
198
209
|
# If refresh fails due to API error, offer to create a new token instead
|
|
199
210
|
if opts.debug:
|
|
200
211
|
click.echo(f"\nDebug: Refresh failed with error: {exc}", err=True)
|
|
201
|
-
click.echo("\nRefresh failed. Creating a new token instead...")
|
|
202
|
-
return _create(ctx, opts, save_config=save_config, force=force)
|
|
212
|
+
click.echo("\nRefresh failed. Creating a new token instead...", err=use_stderr)
|
|
213
|
+
return _create(ctx, opts, save_config=save_config, force=force, json=json)
|
|
203
214
|
|
|
204
215
|
|
|
205
216
|
def _create(ctx, opts, save_config=False, force=False, json=False):
|
|
206
217
|
"""Create a new API token."""
|
|
218
|
+
json = json or utils.should_use_stderr(opts)
|
|
219
|
+
|
|
207
220
|
try:
|
|
208
221
|
new_token = api.create_user_token_saml()
|
|
209
222
|
|
|
210
223
|
if new_token:
|
|
211
|
-
|
|
224
|
+
# NOTE: This mutation is necessary during the deprecation period of the --json flag.
|
|
225
|
+
if json and opts.output not in ("json", "pretty_json"):
|
|
212
226
|
opts.output = "json"
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if maybe_print_as_json(opts, new_token):
|
|
227
|
+
|
|
228
|
+
if utils.maybe_print_as_json(opts, new_token):
|
|
216
229
|
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
f"New token value: {click.style(new_token.key, fg='magenta')}"
|
|
220
|
-
)
|
|
230
|
+
|
|
231
|
+
click.echo(f"New token value: {click.style(new_token.key, fg='magenta')}")
|
|
221
232
|
return new_token
|
|
222
233
|
|
|
223
234
|
return new_token
|
|
224
235
|
|
|
225
236
|
except exceptions.ApiException as exc:
|
|
226
237
|
if exc.status == 401:
|
|
227
|
-
click.echo(f"{exc.detail}")
|
|
238
|
+
click.echo(f"{exc.detail}", err=True)
|
|
228
239
|
return
|
|
229
|
-
|
|
240
|
+
# NOTE: This mutation is necessary during the deprecation period of the --json flag.
|
|
241
|
+
if json and opts.output not in ("json", "pretty_json"):
|
|
230
242
|
opts.output = "json"
|
|
231
243
|
if exc.status == 400:
|
|
232
244
|
new_token = handle_duplicate_token_error(
|
|
@@ -173,8 +173,7 @@ def build_upstream_list_command(upstream_fmt):
|
|
|
173
173
|
def func(ctx, opts, owner_repo, page, page_size, page_all):
|
|
174
174
|
owner, repo = owner_repo
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
use_stderr = opts.output != "pretty"
|
|
176
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
178
177
|
|
|
179
178
|
if not use_stderr:
|
|
180
179
|
click.echo("Getting upstreams... ", nl=False, err=use_stderr)
|
|
@@ -418,7 +417,7 @@ def build_upstream_delete_command(upstream_fmt):
|
|
|
418
417
|
"delete the %(slug_perm)s upstream from the %(owner)s/%(repo)s repository"
|
|
419
418
|
% delete_args
|
|
420
419
|
)
|
|
421
|
-
if not utils.confirm_operation(prompt, assume_yes=yes):
|
|
420
|
+
if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
|
|
422
421
|
return
|
|
423
422
|
|
|
424
423
|
if not use_stderr:
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
5
|
from ...core.api.user import get_user_brief
|
|
6
|
-
from .. import decorators
|
|
6
|
+
from .. import decorators, utils
|
|
7
7
|
from ..exceptions import handle_api_exceptions
|
|
8
|
-
from ..utils import maybe_print_as_json, maybe_spinner
|
|
9
8
|
from .main import main
|
|
10
9
|
|
|
11
10
|
|
|
@@ -17,7 +16,7 @@ from .main import main
|
|
|
17
16
|
@click.pass_context
|
|
18
17
|
def whoami(ctx, opts):
|
|
19
18
|
"""Retrieve your current authentication status."""
|
|
20
|
-
use_stderr =
|
|
19
|
+
use_stderr = utils.should_use_stderr(opts)
|
|
21
20
|
|
|
22
21
|
click.echo(
|
|
23
22
|
"Retrieving your authentication status from the API ... ",
|
|
@@ -27,7 +26,7 @@ def whoami(ctx, opts):
|
|
|
27
26
|
|
|
28
27
|
context_msg = "Failed to retrieve your authentication status!"
|
|
29
28
|
with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
|
|
30
|
-
with maybe_spinner(opts):
|
|
29
|
+
with utils.maybe_spinner(opts):
|
|
31
30
|
is_auth, username, email, name = get_user_brief()
|
|
32
31
|
click.secho("OK", fg="green", err=use_stderr)
|
|
33
32
|
|
|
@@ -38,7 +37,7 @@ def whoami(ctx, opts):
|
|
|
38
37
|
"name": name,
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
if maybe_print_as_json(opts, data):
|
|
40
|
+
if utils.maybe_print_as_json(opts, data):
|
|
42
41
|
return
|
|
43
42
|
|
|
44
43
|
click.echo("You are authenticated as:")
|
cloudsmith_cli/cli/exceptions.py
CHANGED
|
@@ -16,67 +16,103 @@ def handle_api_exceptions(
|
|
|
16
16
|
):
|
|
17
17
|
"""Context manager that handles API exceptions."""
|
|
18
18
|
# flake8: ignore=C901
|
|
19
|
+
|
|
19
20
|
# Use stderr for messages if the output is something else (e.g. # JSON)
|
|
20
|
-
|
|
21
|
+
is_json_output = getattr(opts, "output", None) in ("json", "pretty_json")
|
|
22
|
+
use_stderr = is_json_output
|
|
21
23
|
|
|
22
24
|
try:
|
|
23
25
|
yield
|
|
24
26
|
except ApiException as exc:
|
|
25
|
-
if nl:
|
|
26
|
-
click.echo(err=use_stderr)
|
|
27
|
-
click.secho("ERROR: ", fg="red", nl=False, err=use_stderr)
|
|
28
|
-
else:
|
|
29
|
-
click.secho("ERROR", fg="red", err=use_stderr)
|
|
30
|
-
|
|
31
27
|
context_msg = context_msg or "Failed to perform operation!"
|
|
32
|
-
click.secho(
|
|
33
|
-
"%(context)s (status: %(code)s - %(code_text)s)"
|
|
34
|
-
% {
|
|
35
|
-
"context": context_msg,
|
|
36
|
-
"code": exc.status,
|
|
37
|
-
"code_text": exc.status_description,
|
|
38
|
-
},
|
|
39
|
-
fg="red",
|
|
40
|
-
err=use_stderr,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
28
|
detail, fields = get_details(exc)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
hint = get_error_hint(ctx, opts, exc)
|
|
30
|
+
|
|
31
|
+
if is_json_output:
|
|
32
|
+
# Construct JSON error object
|
|
33
|
+
error_data = {
|
|
34
|
+
"detail": detail or exc.status_description,
|
|
35
|
+
"help": {
|
|
36
|
+
"context": context_msg,
|
|
37
|
+
"hint": hint,
|
|
38
|
+
},
|
|
39
|
+
"meta": {
|
|
40
|
+
"code": exc.status,
|
|
41
|
+
"description": exc.status_description,
|
|
42
|
+
},
|
|
43
|
+
}
|
|
54
44
|
|
|
55
45
|
if fields:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
% {
|
|
61
|
-
"field": click.style(field, bold=True),
|
|
62
|
-
"message": click.style(v, fg="red"),
|
|
63
|
-
},
|
|
64
|
-
err=use_stderr,
|
|
65
|
-
)
|
|
46
|
+
error_data["fields"] = fields
|
|
47
|
+
|
|
48
|
+
# Print to stdout
|
|
49
|
+
import json
|
|
66
50
|
|
|
67
|
-
hint = get_error_hint(ctx, opts, exc)
|
|
68
|
-
if hint:
|
|
69
51
|
click.echo(
|
|
70
|
-
|
|
52
|
+
json.dumps(
|
|
53
|
+
error_data, indent=4 if opts.output == "pretty_json" else None
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
# Standard CLI output to stderr (or interleaved if output != pretty, but we force use_stderr now)
|
|
59
|
+
if nl:
|
|
60
|
+
click.echo(err=use_stderr)
|
|
61
|
+
click.secho("ERROR: ", fg="red", nl=False, err=use_stderr)
|
|
62
|
+
else:
|
|
63
|
+
click.secho("ERROR", fg="red", err=use_stderr)
|
|
64
|
+
|
|
65
|
+
click.secho(
|
|
66
|
+
"%(context)s (status: %(code)s - %(code_text)s)"
|
|
67
|
+
% {
|
|
68
|
+
"context": context_msg,
|
|
69
|
+
"code": exc.status,
|
|
70
|
+
"code_text": exc.status_description,
|
|
71
|
+
},
|
|
72
|
+
fg="red",
|
|
71
73
|
err=use_stderr,
|
|
72
74
|
)
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
if exc.headers:
|
|
76
|
+
if detail or fields:
|
|
76
77
|
click.echo(err=use_stderr)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
click.
|
|
78
|
+
|
|
79
|
+
if detail:
|
|
80
|
+
click.secho(
|
|
81
|
+
"Detail: %(detail)s"
|
|
82
|
+
% {"detail": click.style(detail, fg="red", bold=False)},
|
|
83
|
+
bold=True,
|
|
84
|
+
err=use_stderr,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if fields:
|
|
88
|
+
for k, v in fields.items():
|
|
89
|
+
field = "%s Field" % k.capitalize()
|
|
90
|
+
|
|
91
|
+
# Flatten list/tuple error messages for text output
|
|
92
|
+
if isinstance(v, (list, tuple)):
|
|
93
|
+
v = " ".join(v)
|
|
94
|
+
|
|
95
|
+
click.secho(
|
|
96
|
+
"%(field)s: %(message)s"
|
|
97
|
+
% {
|
|
98
|
+
"field": click.style(field, bold=True),
|
|
99
|
+
"message": click.style(v, fg="red"),
|
|
100
|
+
},
|
|
101
|
+
err=use_stderr,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if hint:
|
|
105
|
+
click.echo(
|
|
106
|
+
f"Hint: {click.style(hint, fg='yellow')}",
|
|
107
|
+
err=use_stderr,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if opts.verbose and not opts.debug:
|
|
111
|
+
if exc.headers:
|
|
112
|
+
click.echo(err=use_stderr)
|
|
113
|
+
click.echo("Headers in Reply:", err=use_stderr)
|
|
114
|
+
for k, v in exc.headers.items():
|
|
115
|
+
click.echo(f"{k} = {v}", err=use_stderr)
|
|
80
116
|
|
|
81
117
|
if reraise_on_error:
|
|
82
118
|
raise
|
|
@@ -100,10 +136,11 @@ def get_details(exc):
|
|
|
100
136
|
except (TypeError, KeyError):
|
|
101
137
|
field_detail = v
|
|
102
138
|
|
|
103
|
-
if isinstance(field_detail, (list, tuple)):
|
|
104
|
-
field_detail = " ".join(field_detail)
|
|
105
|
-
|
|
106
139
|
if k == "non_field_errors":
|
|
140
|
+
# Ensure we handle list/tuple for non_field_errors details joining
|
|
141
|
+
if isinstance(field_detail, (list, tuple)):
|
|
142
|
+
field_detail = " ".join(field_detail)
|
|
143
|
+
|
|
107
144
|
if detail:
|
|
108
145
|
detail += " " + field_detail
|
|
109
146
|
else:
|
cloudsmith_cli/cli/utils.py
CHANGED
|
@@ -191,10 +191,25 @@ def confirm_operation(prompt, prefix=None, assume_yes=False, err=False):
|
|
|
191
191
|
|
|
192
192
|
@contextmanager
|
|
193
193
|
def maybe_spinner(opts):
|
|
194
|
-
"""Only activate the spinner if not in debug mode."""
|
|
195
|
-
if opts
|
|
194
|
+
"""Only activate the spinner if not in debug mode or using json output."""
|
|
195
|
+
if should_use_stderr(opts) or get_output_format(opts) in ("json", "pretty_json"):
|
|
196
196
|
# No spinner
|
|
197
197
|
yield
|
|
198
198
|
else:
|
|
199
199
|
with spinner() as spin:
|
|
200
200
|
yield spin
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def get_output_format(opts):
|
|
204
|
+
"""Get the output format from opts."""
|
|
205
|
+
return getattr(opts, "output", None)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def should_use_stderr(opts):
|
|
209
|
+
"""Check if stdout should be avoided for informational messages."""
|
|
210
|
+
return get_output_format(opts) in ("json", "pretty_json")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def maybe_print_status_json(opts, status_dict):
|
|
214
|
+
"""Maybe print a status dict as JSON."""
|
|
215
|
+
return maybe_print_as_json(opts, status_dict)
|
cloudsmith_cli/cli/webserver.py
CHANGED
|
@@ -214,7 +214,7 @@ class AuthenticationWebRequestHandler(BaseHTTPRequestHandler):
|
|
|
214
214
|
|
|
215
215
|
if two_factor_token:
|
|
216
216
|
totp_token = click.prompt(
|
|
217
|
-
"Please enter your 2FA token", hide_input=True, type=str
|
|
217
|
+
"Please enter your 2FA token", hide_input=True, type=str, err=True
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
access_token, refresh_token = exchange_2fa_token(
|
cloudsmith_cli/data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.10.
|
|
1
|
+
1.10.4
|