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.
Files changed (37) hide show
  1. cloudsmith_cli/cli/command.py +66 -0
  2. cloudsmith_cli/cli/commands/auth.py +25 -12
  3. cloudsmith_cli/cli/commands/check.py +18 -4
  4. cloudsmith_cli/cli/commands/copy.py +8 -2
  5. cloudsmith_cli/cli/commands/delete.py +9 -3
  6. cloudsmith_cli/cli/commands/dependencies.py +1 -1
  7. cloudsmith_cli/cli/commands/download.py +89 -8
  8. cloudsmith_cli/cli/commands/entitlements.py +11 -7
  9. cloudsmith_cli/cli/commands/list_.py +2 -2
  10. cloudsmith_cli/cli/commands/login.py +21 -13
  11. cloudsmith_cli/cli/commands/metrics/entitlements.py +2 -2
  12. cloudsmith_cli/cli/commands/metrics/packages.py +1 -1
  13. cloudsmith_cli/cli/commands/move.py +10 -3
  14. cloudsmith_cli/cli/commands/policy/deny.py +8 -6
  15. cloudsmith_cli/cli/commands/policy/license.py +5 -3
  16. cloudsmith_cli/cli/commands/policy/vulnerability.py +6 -3
  17. cloudsmith_cli/cli/commands/push.py +146 -64
  18. cloudsmith_cli/cli/commands/quarantine.py +7 -4
  19. cloudsmith_cli/cli/commands/quota/history.py +1 -1
  20. cloudsmith_cli/cli/commands/quota/quota.py +1 -1
  21. cloudsmith_cli/cli/commands/repos.py +5 -3
  22. cloudsmith_cli/cli/commands/resync.py +7 -2
  23. cloudsmith_cli/cli/commands/status.py +21 -3
  24. cloudsmith_cli/cli/commands/tags.py +20 -5
  25. cloudsmith_cli/cli/commands/tokens.py +37 -25
  26. cloudsmith_cli/cli/commands/upstream.py +2 -3
  27. cloudsmith_cli/cli/commands/whoami.py +4 -5
  28. cloudsmith_cli/cli/exceptions.py +86 -49
  29. cloudsmith_cli/cli/utils.py +17 -2
  30. cloudsmith_cli/cli/webserver.py +1 -1
  31. cloudsmith_cli/data/VERSION +1 -1
  32. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/METADATA +1 -1
  33. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/RECORD +37 -37
  34. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/WHEEL +0 -0
  35. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/entry_points.txt +0 -0
  36. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/licenses/LICENSE +0 -0
  37. {cloudsmith_cli-1.10.2.dist-info → cloudsmith_cli-1.10.4.dist-info}/top_level.txt +0 -0
@@ -6,6 +6,46 @@ import click.exceptions
6
6
  from click_didyoumean import DYMGroup
7
7
 
8
8
 
9
+ def _is_json_output_requested(exception):
10
+ """Determine if JSON output was requested, checking context and argv."""
11
+ # Check context if available
12
+ ctx = getattr(exception, "ctx", None)
13
+ if ctx and ctx.params:
14
+ fmt = ctx.params.get("output")
15
+ if fmt in ("json", "pretty_json"):
16
+ return True
17
+
18
+ # Fallback: check sys.argv for output format flags
19
+ import sys
20
+
21
+ argv = sys.argv
22
+
23
+ if "--output-format=json" in argv or "--output-format=pretty_json" in argv:
24
+ return True
25
+
26
+ for idx, arg in enumerate(argv):
27
+ if arg in ("-F", "--output-format") and idx + 1 < len(argv):
28
+ if argv[idx + 1] in ("json", "pretty_json"):
29
+ return True
30
+
31
+ return False
32
+
33
+
34
+ def _format_click_exception_as_json(exception):
35
+ """Format a ClickException as a JSON error dict."""
36
+ return {
37
+ "detail": exception.format_message(),
38
+ "meta": {
39
+ "code": exception.exit_code,
40
+ "description": "Usage Error",
41
+ },
42
+ "help": {
43
+ "context": "Invalid usage",
44
+ "hint": "Check your command arguments/flags.",
45
+ },
46
+ }
47
+
48
+
9
49
  class AliasGroup(DYMGroup):
10
50
  """A command group with DYM and alias support."""
11
51
 
@@ -92,3 +132,29 @@ class AliasGroup(DYMGroup):
92
132
  def format_commands(self, ctx, formatter):
93
133
  ctx.showing_help = True
94
134
  return super().format_commands(ctx, formatter)
135
+
136
+ def main(self, *args, **kwargs):
137
+ """Override main to intercept exceptions and format as JSON if requested."""
138
+ import sys
139
+
140
+ original_standalone_mode = kwargs.get("standalone_mode", True)
141
+ kwargs["standalone_mode"] = False
142
+
143
+ try:
144
+ return super().main(*args, **kwargs)
145
+ except click.exceptions.Abort:
146
+ if not original_standalone_mode:
147
+ raise
148
+ click.echo("Aborted!", err=True)
149
+ sys.exit(1)
150
+ except click.exceptions.ClickException as e:
151
+ if _is_json_output_requested(e):
152
+ import json
153
+
154
+ click.echo(json.dumps(_format_click_exception_as_json(e), indent=4))
155
+ sys.exit(e.exit_code)
156
+
157
+ if not original_standalone_mode:
158
+ raise
159
+ e.show()
160
+ sys.exit(e.exit_code)
@@ -4,7 +4,7 @@ import webbrowser
4
4
 
5
5
  import click
6
6
 
7
- from .. import decorators, validators
7
+ from .. import decorators, utils, validators
8
8
  from ..exceptions import handle_api_exceptions
9
9
  from ..saml import create_configured_session, get_idp_url
10
10
  from ..webserver import AuthenticationWebRequestHandler, AuthenticationWebServer
@@ -22,14 +22,15 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
22
22
  api_host = opts.api_config.host
23
23
 
24
24
  idp_url = get_idp_url(api_host, owner, session=session)
25
- if not json:
26
- click.echo(
27
- f"Opening your organization's SAML IDP URL in your browser: {click.style(idp_url, bold=True)}"
28
- )
29
- click.echo()
25
+
26
+ click.echo(
27
+ f"Opening your organization's SAML IDP URL in your browser: {click.style(idp_url, bold=True)}",
28
+ err=json,
29
+ )
30
+ click.echo(err=json)
30
31
  webbrowser.open(idp_url)
31
- if not json:
32
- click.echo("Starting webserver to begin authentication ... ")
32
+
33
+ click.echo("Starting webserver to begin authentication ... ", err=json)
33
34
 
34
35
  auth_server = AuthenticationWebServer(
35
36
  (AUTH_SERVER_HOST, AUTH_SERVER_PORT),
@@ -86,13 +87,25 @@ def _perform_saml_authentication(opts, owner, enable_token_creation=False, json=
86
87
  @click.pass_context
87
88
  def authenticate(ctx, opts, owner, token, force, save_config, json):
88
89
  """Authenticate to Cloudsmith using the org's SAML setup."""
89
- owner = owner[0].strip("'[]'")
90
+ json = json or utils.should_use_stderr(opts)
91
+ # If using json output, we redirect info messages to stderr
92
+ use_stderr = json
90
93
 
91
- if not json:
92
- click.echo(
93
- f"Beginning authentication for the {click.style(owner, bold=True)} org ... "
94
+ if json and not utils.should_use_stderr(opts):
95
+ click.secho(
96
+ "DEPRECATION WARNING: The `--json` flag is deprecated and will be removed in a future release. "
97
+ "Please use `--output-format json` instead.",
98
+ fg="yellow",
99
+ err=True,
94
100
  )
95
101
 
102
+ owner = owner[0].strip("'[]'")
103
+
104
+ click.echo(
105
+ f"Beginning authentication for the {click.style(owner, bold=True)} org ... ",
106
+ err=use_stderr,
107
+ )
108
+
96
109
  context_message = "Failed to authenticate via SSO!"
97
110
  with handle_api_exceptions(ctx, opts=opts, context_msg=context_message):
98
111
  _perform_saml_authentication(
@@ -29,14 +29,18 @@ def check(ctx, opts): # pylint: disable=unused-argument
29
29
  @click.pass_context
30
30
  def rates(ctx, opts):
31
31
  """Check current API rate limits."""
32
- click.echo("Retrieving rate limits ... ", nl=False)
32
+ use_stderr = utils.should_use_stderr(opts)
33
+ click.echo("Retrieving rate limits ... ", nl=False, err=use_stderr)
33
34
 
34
35
  context_msg = "Failed to retrieve status!"
35
36
  with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
36
37
  with maybe_spinner(opts):
37
38
  resources_limits = get_rate_limits()
38
39
 
39
- click.secho("OK", fg="green")
40
+ click.secho("OK", fg="green", err=use_stderr)
41
+
42
+ if utils.maybe_print_as_json(opts, resources_limits):
43
+ return
40
44
 
41
45
  headers = ["Resource", "Throttled", "Remaining", "Interval (Seconds)", "Reset"]
42
46
 
@@ -77,17 +81,27 @@ def rates(ctx, opts):
77
81
  @click.pass_context
78
82
  def service(ctx, opts):
79
83
  """Check the status of the Cloudsmith service."""
80
- click.echo("Retrieving service status ... ", nl=False)
84
+ use_stderr = utils.should_use_stderr(opts)
85
+ click.echo("Retrieving service status ... ", nl=False, err=use_stderr)
81
86
 
82
87
  context_msg = "Failed to retrieve status!"
83
88
  with handle_api_exceptions(ctx, opts=opts, context_msg=context_msg):
84
89
  with maybe_spinner(opts):
85
90
  status, version = get_status(with_version=True)
86
91
 
87
- click.secho("OK", fg="green")
92
+ click.secho("OK", fg="green", err=use_stderr)
88
93
 
89
94
  config = cloudsmith_api.Configuration()
90
95
 
96
+ data = {
97
+ "endpoint": config.host,
98
+ "status": status,
99
+ "version": version,
100
+ }
101
+
102
+ if utils.maybe_print_as_json(opts, data):
103
+ return
104
+
91
105
  click.echo()
92
106
  click.echo(f"The service endpoint is: {click.style(config.host, bold=True)}")
93
107
  click.echo(f"The service status is: {click.style(status, bold=True)}")
@@ -3,7 +3,7 @@
3
3
  import click
4
4
 
5
5
  from ...core.api.packages import copy_package
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
@@ -56,6 +56,8 @@ def copy(
56
56
  """
57
57
  owner, source, slug = owner_repo_package
58
58
 
59
+ use_stderr = utils.should_use_stderr(opts)
60
+
59
61
  click.echo(
60
62
  "Copying %(slug)s package from %(source)s to %(dest)s ... "
61
63
  % {
@@ -64,6 +66,7 @@ def copy(
64
66
  "dest": click.style(destination, bold=True),
65
67
  },
66
68
  nl=False,
69
+ err=use_stderr,
67
70
  )
68
71
 
69
72
  context_msg = "Failed to copy package!"
@@ -75,9 +78,10 @@ def copy(
75
78
  owner=owner, repo=source, identifier=slug, destination=destination
76
79
  )
77
80
 
78
- click.secho("OK", fg="green")
81
+ click.secho("OK", fg="green", err=use_stderr)
79
82
 
80
83
  if no_wait_for_sync:
84
+ utils.maybe_print_status_json(opts, {"slug": new_slug, "status": "OK"})
81
85
  return
82
86
 
83
87
  wait_for_package_sync(
@@ -90,3 +94,5 @@ def copy(
90
94
  skip_errors=skip_errors,
91
95
  attempts=sync_attempts,
92
96
  )
97
+
98
+ utils.maybe_print_status_json(opts, {"slug": new_slug, "status": "OK"})
@@ -45,12 +45,16 @@ def delete(ctx, opts, owner_repo_package, yes):
45
45
  "package": click.style(slug, bold=True),
46
46
  }
47
47
 
48
+ use_stderr = utils.should_use_stderr(opts)
49
+
48
50
  prompt = "delete the %(package)s from %(owner)s/%(repo)s" % delete_args
49
- if not utils.confirm_operation(prompt, assume_yes=yes):
51
+ if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
50
52
  return
51
53
 
52
54
  click.echo(
53
- "Deleting %(package)s from %(owner)s/%(repo)s ... " % delete_args, nl=False
55
+ "Deleting %(package)s from %(owner)s/%(repo)s ... " % delete_args,
56
+ nl=False,
57
+ err=use_stderr,
54
58
  )
55
59
 
56
60
  context_msg = "Failed to delete the package!"
@@ -58,4 +62,6 @@ def delete(ctx, opts, owner_repo_package, yes):
58
62
  with maybe_spinner(opts):
59
63
  delete_package(owner=owner, repo=repo, identifier=slug)
60
64
 
61
- click.secho("OK", fg="green")
65
+ click.secho("OK", fg="green", err=use_stderr)
66
+
67
+ utils.maybe_print_status_json(opts, {"slug": slug, "status": "OK"})
@@ -50,7 +50,7 @@ def list_dependencies(ctx, opts, owner_repo_package):
50
50
  owner, repo, identifier = owner_repo_package
51
51
 
52
52
  # Use stderr for messages if the output is something else (e.g. # JSON)
53
- use_stderr = opts.output != "pretty"
53
+ use_stderr = utils.should_use_stderr(opts)
54
54
 
55
55
  click.echo(
56
56
  "Getting direct (non-transitive) dependencies of %(package)s in "
@@ -12,7 +12,7 @@ from ...core.download import (
12
12
  resolve_package,
13
13
  stream_download,
14
14
  )
15
- from .. import decorators, validators
15
+ from .. import decorators, utils, validators
16
16
  from ..exceptions import handle_api_exceptions
17
17
  from ..utils import maybe_spinner
18
18
  from .main import main
@@ -124,7 +124,7 @@ def download( # noqa: C901
124
124
  owner, repo = owner_repo
125
125
 
126
126
  # Use stderr for messages if output is JSON
127
- use_stderr = opts.output != "pretty"
127
+ use_stderr = utils.should_use_stderr(opts)
128
128
 
129
129
  if not use_stderr:
130
130
  click.echo(
@@ -213,11 +213,13 @@ def download( # noqa: C901
213
213
  return
214
214
 
215
215
  # Download all files
216
- click.echo(f"\nDownloading {len(files_to_download)} files to: {output_dir}")
217
- click.echo()
216
+ if not use_stderr:
217
+ click.echo(f"\nDownloading {len(files_to_download)} files to: {output_dir}")
218
+ click.echo()
218
219
 
219
220
  success_count = 0
220
221
  failed_files = []
222
+ downloaded_files = []
221
223
 
222
224
  for idx, file_info in enumerate(files_to_download, 1):
223
225
  filename = file_info["filename"]
@@ -232,6 +234,12 @@ def download( # noqa: C901
232
234
  f"[{idx}/{len(files_to_download)}] [{tag}] {filename}{primary_marker} ...",
233
235
  nl=False,
234
236
  )
237
+ else:
238
+ click.echo(
239
+ f"[{idx}/{len(files_to_download)}] [{tag}] {filename}{primary_marker} ...",
240
+ nl=False,
241
+ err=True,
242
+ )
235
243
 
236
244
  try:
237
245
  context_msg = f"Failed to download {filename}!"
@@ -246,11 +254,56 @@ def download( # noqa: C901
246
254
  )
247
255
  if not use_stderr:
248
256
  click.secho(" OK", fg="green")
257
+ else:
258
+ click.echo(" OK", err=True)
249
259
  success_count += 1
260
+ downloaded_files.append(
261
+ {
262
+ "filename": filename,
263
+ "path": output_path,
264
+ "tag": tag,
265
+ "is_primary": file_info.get("is_primary", False),
266
+ "size": file_info.get("size", 0),
267
+ "status": "OK",
268
+ }
269
+ )
250
270
  except Exception as e: # pylint: disable=broad-except
251
271
  if not use_stderr:
252
272
  click.secho(" FAILED", fg="red")
273
+ else:
274
+ click.echo(" FAILED", err=True)
253
275
  failed_files.append((filename, str(e)))
276
+ downloaded_files.append(
277
+ {
278
+ "filename": filename,
279
+ "path": output_path,
280
+ "tag": tag,
281
+ "is_primary": file_info.get("is_primary", False),
282
+ "size": file_info.get("size", 0),
283
+ "status": "FAILED",
284
+ "error": str(e),
285
+ }
286
+ )
287
+
288
+ # Build JSON output for all-files download
289
+ json_output = {
290
+ "package": {
291
+ "name": package.get("name"),
292
+ "version": package.get("version"),
293
+ "format": package.get("format"),
294
+ "slug": package.get("slug"),
295
+ },
296
+ "output_directory": output_dir,
297
+ "files": downloaded_files,
298
+ "summary": {
299
+ "total": len(files_to_download),
300
+ "success": success_count,
301
+ "failed": len(failed_files),
302
+ },
303
+ }
304
+
305
+ if utils.maybe_print_as_json(opts, json_output):
306
+ return
254
307
 
255
308
  click.echo()
256
309
  if success_count == len(files_to_download):
@@ -321,12 +374,40 @@ def download( # noqa: C901
321
374
  session=session,
322
375
  headers=auth_headers,
323
376
  overwrite=overwrite,
324
- quiet=opts.output != "pretty",
377
+ quiet=utils.should_use_stderr(opts),
325
378
  )
326
379
 
327
- if opts.output == "pretty":
328
- click.echo()
329
- click.secho("Download completed successfully!", fg="green")
380
+ # Build JSON output for single-file download
381
+ json_output = {
382
+ "package": {
383
+ "name": package.get("name"),
384
+ "version": package.get("version"),
385
+ "format": package.get("format"),
386
+ "slug": package.get("slug"),
387
+ },
388
+ "output_directory": os.path.dirname(outfile),
389
+ "files": [
390
+ {
391
+ "filename": os.path.basename(outfile),
392
+ "path": outfile,
393
+ "tag": "file",
394
+ "is_primary": True,
395
+ "size": package.get("size", 0),
396
+ "status": "OK",
397
+ }
398
+ ],
399
+ "summary": {
400
+ "total": 1,
401
+ "success": 1,
402
+ "failed": 0,
403
+ },
404
+ }
405
+
406
+ if utils.maybe_print_as_json(opts, json_output):
407
+ return
408
+
409
+ click.echo()
410
+ click.secho("Download completed successfully!", fg="green")
330
411
 
331
412
 
332
413
  def _get_extension_for_format(pkg_format: str) -> str:
@@ -89,7 +89,7 @@ def list_entitlements(ctx, opts, owner_repo, page, page_size, show_tokens, page_
89
89
  owner, repo = owner_repo
90
90
 
91
91
  # Use stderr for messages if the output is something else (e.g. # JSON)
92
- use_stderr = opts.output != "pretty"
92
+ use_stderr = utils.should_use_stderr(opts)
93
93
 
94
94
  click.echo(
95
95
  "Getting list of entitlements for the %(repository)s "
@@ -323,7 +323,7 @@ def create(ctx, opts, owner_repo, show_tokens, name, token):
323
323
  owner, repo = owner_repo
324
324
 
325
325
  # Use stderr for messages if the output is something else (e.g. # JSON)
326
- use_stderr = opts.output != "pretty"
326
+ use_stderr = utils.should_use_stderr(opts)
327
327
 
328
328
  click.secho(
329
329
  "Creating %(name)s entitlement for the %(repository)s "
@@ -391,13 +391,17 @@ def delete(ctx, opts, owner_repo_identifier, yes):
391
391
  "delete the %(identifier)s entitlement from the %(repository)s "
392
392
  "repository" % delete_args
393
393
  )
394
- if not utils.confirm_operation(prompt, assume_yes=yes):
394
+
395
+ use_stderr = utils.should_use_stderr(opts)
396
+
397
+ if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
395
398
  return
396
399
 
397
400
  click.secho(
398
401
  "Deleting %(identifier)s entitlement from the %(repository)s "
399
402
  "repository ... " % delete_args,
400
403
  nl=False,
404
+ err=use_stderr,
401
405
  )
402
406
 
403
407
  context_msg = "Failed to delete the entitlement!"
@@ -454,7 +458,7 @@ def update(ctx, opts, owner_repo_identifier, show_tokens, name, token):
454
458
  owner, repo, identifier = owner_repo_identifier
455
459
 
456
460
  # Use stderr for messages if the output is something else (e.g. # JSON)
457
- use_stderr = opts.output != "pretty"
461
+ use_stderr = utils.should_use_stderr(opts)
458
462
 
459
463
  click.secho(
460
464
  "Updating %(identifier)s entitlement for the %(repository)s "
@@ -527,7 +531,7 @@ def refresh(ctx, opts, owner_repo_identifier, show_tokens, yes):
527
531
  }
528
532
 
529
533
  # Use stderr for messages if the output is something else (e.g. # JSON)
530
- use_stderr = opts.output != "pretty"
534
+ use_stderr = utils.should_use_stderr(opts)
531
535
 
532
536
  prompt = (
533
537
  "refresh the %(identifier)s entitlement for the %(repository)s "
@@ -603,7 +607,7 @@ def sync(ctx, opts, owner_repo, show_tokens, source, yes):
603
607
  }
604
608
 
605
609
  # Use stderr for messages if the output is something else (e.g. # JSON)
606
- use_stderr = opts.output != "pretty"
610
+ use_stderr = utils.should_use_stderr(opts)
607
611
 
608
612
  if not yes:
609
613
  click.secho(
@@ -768,7 +772,7 @@ def restrict(
768
772
  owner, repo, identifier = owner_repo_identifier
769
773
 
770
774
  # Use stderr for messages if the output is something else (e.g. # JSON)
771
- use_stderr = opts.output != "pretty"
775
+ use_stderr = utils.should_use_stderr(opts)
772
776
 
773
777
  click.secho(
774
778
  "Updating %(identifier)s entitlement for the %(repository)s "
@@ -53,7 +53,7 @@ def dependencies_(*args, **kwargs): # pylint: disable=missing-docstring
53
53
  def distros(ctx, opts, package_format):
54
54
  """List available distributions."""
55
55
  # Use stderr for messages if the output is something else (e.g. # JSON)
56
- use_stderr = opts.output != "pretty"
56
+ use_stderr = utils.should_use_stderr(opts)
57
57
 
58
58
  if not use_stderr:
59
59
  click.echo("Getting list of distributions ... ", nl=False, err=use_stderr)
@@ -210,7 +210,7 @@ def packages(ctx, opts, owner_repo, page, page_size, query, sort, page_all):
210
210
  owner, repo = owner_repo
211
211
 
212
212
  # Use stderr for messages if the output is something else (e.g. # JSON)
213
- use_stderr = opts.output != "pretty"
213
+ use_stderr = utils.should_use_stderr(opts)
214
214
 
215
215
  if not use_stderr:
216
216
  click.echo("Getting list of packages ... ", nl=False, err=use_stderr)
@@ -6,7 +6,7 @@ import cloudsmith_api
6
6
  from ...core.api.exceptions import TwoFactorRequiredException
7
7
  from ...core.api.user import get_user_token
8
8
  from ...core.config import create_config_files, new_config_messaging
9
- from .. import decorators
9
+ from .. import decorators, utils
10
10
  from ..exceptions import handle_api_exceptions
11
11
  from ..utils import maybe_spinner
12
12
  from .main import main
@@ -37,10 +37,12 @@ def validate_login(ctx, param, value):
37
37
  @click.pass_context
38
38
  def login(ctx, opts, login, password): # pylint: disable=redefined-outer-name
39
39
  """Retrieve your API authentication token/key via login."""
40
+ use_stderr = utils.should_use_stderr(opts)
40
41
  click.echo(
41
42
  "Retrieving API token for %(login)s ... "
42
43
  % {"login": click.style(login, bold=True)},
43
44
  nl=False,
45
+ err=use_stderr,
44
46
  )
45
47
 
46
48
  context_msg = "Failed to retrieve the API token!"
@@ -49,14 +51,17 @@ def login(ctx, opts, login, password): # pylint: disable=redefined-outer-name
49
51
  with maybe_spinner(opts):
50
52
  api_key = get_user_token(login=login, password=password)
51
53
  except TwoFactorRequiredException as e:
52
- click.echo("\r\033[K", nl=False)
53
- click.echo("Two-factor authentication is required.")
54
+ click.echo("\r\033[K", nl=False, err=use_stderr)
55
+ click.echo("Two-factor authentication is required.", err=use_stderr)
54
56
 
55
- totp_token = click.prompt("Enter your two-factor authentication code", type=str)
57
+ totp_token = click.prompt(
58
+ "Enter your two-factor authentication code", type=str, err=use_stderr
59
+ )
56
60
  click.echo(
57
61
  "Verifying two-factor code for %(login)s ... "
58
62
  % {"login": click.style(login, bold=True)},
59
63
  nl=False,
64
+ err=use_stderr,
60
65
  )
61
66
 
62
67
  try:
@@ -69,23 +74,26 @@ def login(ctx, opts, login, password): # pylint: disable=redefined-outer-name
69
74
  two_factor_token=e.two_factor_token,
70
75
  )
71
76
  except cloudsmith_api.rest.ApiException:
72
- click.echo("\r\033[K", nl=False)
77
+ click.echo("\r\033[K", nl=False, err=use_stderr)
73
78
  click.secho(
74
- "Authentication failed: The entered TOTP token is not valid.", fg="red"
79
+ "Authentication failed: The entered TOTP token is not valid.",
80
+ fg="red",
81
+ err=use_stderr,
75
82
  )
76
83
  ctx.exit(1)
77
84
 
78
85
  except cloudsmith_api.rest.ApiException as e:
79
- click.echo("\r\033[K", nl=False)
80
- click.secho(f"Authentication failed: {str(e)}", fg="red")
86
+ click.echo("\r\033[K", nl=False, err=use_stderr)
87
+ click.secho(f"Authentication failed: {str(e)}", fg="red", err=use_stderr)
81
88
  ctx.exit(1)
82
89
 
83
- click.secho("OK", fg="green")
90
+ click.secho("OK", fg="green", err=use_stderr)
84
91
 
85
- click.echo(
86
- "Your API key/token is: %(token)s"
87
- % {"token": click.style(api_key, fg="magenta")}
88
- )
92
+ if not utils.maybe_print_as_json(opts, {"token": api_key, "login": login}):
93
+ click.echo(
94
+ "Your API key/token is: %(token)s"
95
+ % {"token": click.style(api_key, fg="magenta")}
96
+ )
89
97
 
90
98
  create, has_errors = create_config_files(ctx, opts, api_key=api_key)
91
99
  new_config_messaging(has_errors, opts, create, api_key=api_key)
@@ -109,8 +109,8 @@ def usage(ctx, opts, owner_repo, tokens, start, finish):
109
109
  If REPO isn't specified, all repositories will be included from the
110
110
  OWNER namespace.
111
111
  """
112
- # Use stderr for messages if the output is something else (e.g. # JSON)
113
- use_stderr = opts.output != "pretty"
112
+
113
+ use_stderr = utils.should_use_stderr(opts)
114
114
 
115
115
  click.echo("Getting usage metrics ... ", nl=False, err=use_stderr)
116
116
 
@@ -106,7 +106,7 @@ def usage(ctx, opts, owner_repo, packages, start, finish):
106
106
  metrics for that namespace/repository combination.
107
107
  """
108
108
  # Use stderr for messages if the output is something else (e.g. # JSON)
109
- use_stderr = opts.output != "pretty"
109
+ use_stderr = utils.should_use_stderr(opts)
110
110
 
111
111
  click.echo("Getting usage metrics ... ", nl=False, err=use_stderr)
112
112
 
@@ -70,12 +70,16 @@ def move(
70
70
  "dest": click.style(destination, bold=True),
71
71
  }
72
72
 
73
+ use_stderr = utils.should_use_stderr(opts)
74
+
73
75
  prompt = "move the %(slug)s from %(source)s to %(dest)s" % move_args
74
- if not utils.confirm_operation(prompt, assume_yes=yes):
76
+ if not utils.confirm_operation(prompt, assume_yes=yes, err=use_stderr):
75
77
  return
76
78
 
77
79
  click.echo(
78
- "Moving %(slug)s package from %(source)s to %(dest)s ... " % move_args, nl=False
80
+ "Moving %(slug)s package from %(source)s to %(dest)s ... " % move_args,
81
+ nl=False,
82
+ err=use_stderr,
79
83
  )
80
84
 
81
85
  context_msg = "Failed to move package!"
@@ -87,9 +91,10 @@ def move(
87
91
  owner=owner, repo=source, identifier=slug, destination=destination
88
92
  )
89
93
 
90
- click.secho("OK", fg="green")
94
+ click.secho("OK", fg="green", err=use_stderr)
91
95
 
92
96
  if no_wait_for_sync:
97
+ utils.maybe_print_status_json(opts, {"slug": new_slug, "status": "OK"})
93
98
  return
94
99
 
95
100
  wait_for_package_sync(
@@ -102,3 +107,5 @@ def move(
102
107
  skip_errors=skip_errors,
103
108
  attempts=sync_attempts,
104
109
  )
110
+
111
+ utils.maybe_print_status_json(opts, {"slug": new_slug, "status": "OK"})