Skip to content

Commit 08876e1

Browse files
committed
move to warnings object
1 parent 3e1a697 commit 08876e1

File tree

5 files changed

+136
-54
lines changed

5 files changed

+136
-54
lines changed

cloudsmith_cli/cli/commands/main.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import click
44

5+
from cloudsmith_cli.cli.warnings import get_or_create_warnings
6+
57
from ...core.api.version import get_version as get_api_version
68
from ...core.utils import get_github_website, get_help_website
79
from ...core.version import get_version as get_cli_version
@@ -61,3 +63,13 @@ def main(ctx, opts, version):
6163
print_version()
6264
elif ctx.invoked_subcommand is None:
6365
click.echo(ctx.get_help())
66+
67+
68+
@main.result_callback()
69+
@click.pass_context
70+
def result_callback(ctx, opts, **kwargs):
71+
"""Callback for main function. Required for saving warnings til the end."""
72+
73+
warnings = get_or_create_warnings(ctx)
74+
if warnings:
75+
click.echo(warnings.report())

cloudsmith_cli/cli/commands/repos.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,6 @@ def get(ctx, opts, owner_repo, page, page_size):
101101

102102
click.echo("Getting list of repositories ... ", nl=False, err=use_stderr)
103103

104-
repo = None
105-
owner = None
106-
107104
if isinstance(owner_repo, list):
108105
if len(owner_repo) == 1:
109106
owner = owner_repo[0]

cloudsmith_cli/cli/config.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import click
88
from click_configfile import ConfigFileReader, Param, SectionSchema, matches_section
99

10+
from cloudsmith_cli.cli.warnings import ConfigLoadWarning, ProfileNotFoundWarning
11+
1012
from ..core.utils import get_data_path, read_file
1113
from . import utils, validators
1214

@@ -162,8 +164,9 @@ def config_already_warned(cls):
162164
return False
163165

164166
@classmethod
165-
def load_config(cls, opts, path=None, profile=None, no_warn=False):
167+
def load_config(cls, opts, path=None, warnings=None, profile=None):
166168
"""Load a configuration file into an options object."""
169+
167170
if path and os.path.exists(path):
168171
if os.path.isdir(path):
169172
cls.config_searchpath.insert(0, path)
@@ -174,36 +177,22 @@ def load_config(cls, opts, path=None, profile=None, no_warn=False):
174177
values = config.get("default", {})
175178
cls._load_values_into_opts(opts, values)
176179

177-
warn = not no_warn and not cls.config_already_warned()
178-
179-
if profile and profile != "default" and warn:
180+
if profile and profile != "default":
180181
try:
181182
values = config["profile:%s" % profile]
182183
cls._load_values_into_opts(opts, values)
183184
except KeyError:
184-
if warn:
185-
click.secho(
186-
f"Warning: profile {profile} not found in config files {cls.config_files}",
187-
fg="yellow",
188-
)
185+
warning = ProfileNotFoundWarning(path=path, profile=profile)
186+
warnings.append(warning)
189187

190188
existing_config_paths = {
191189
path: os.path.exists(path) for path in cls.config_files
192190
}
193-
if not any(existing_config_paths.values()) and warn:
194-
click.secho(
195-
"Warning: No config files found in search paths. Tried the following:",
196-
fg="yellow",
197-
)
198-
for tested_path, exists in existing_config_paths.items():
199-
if exists:
200-
click.secho(f"{tested_path} - file exists", fg="green")
201-
else:
202-
click.secho(f"{tested_path} - file does not exist", fg="yellow")
203-
click.secho(
204-
"You may need to run `cloudsmith login` to authenticate and create a config file.",
205-
fg="yellow",
191+
if not any(list(existing_config_paths.values())):
192+
config_load_warning = ConfigLoadWarning(
193+
paths=existing_config_paths,
206194
)
195+
warnings.append(config_load_warning)
207196

208197
return values
209198

@@ -248,7 +237,7 @@ class CredentialsReader(ConfigReader):
248237
config_section_schemas = [CredentialsSchema.Default, CredentialsSchema.Profile]
249238

250239
@classmethod
251-
def load_config(cls, opts, path=None, profile=None, no_warn=False):
240+
def load_config(cls, opts, path=None, warnings=None, profile=None):
252241
"""
253242
Load a credentials configuration file into an options object.
254243
We overload the load_config command in CredentialsReader as
@@ -292,15 +281,17 @@ def get_creds_reader():
292281
"""Get the credentials config reader class."""
293282
return CredentialsReader
294283

295-
def load_config_file(self, path, profile=None, no_warn=False):
284+
def load_config_file(self, path, warnings=None, profile=None):
296285
"""Load the standard config file."""
286+
print("load_config_file")
297287
config_cls = self.get_config_reader()
298-
return config_cls.load_config(self, path, profile=profile, no_warn=no_warn)
288+
return config_cls.load_config(self, path, warnings=warnings, profile=profile)
299289

300-
def load_creds_file(self, path, profile=None, no_warn=False):
290+
def load_creds_file(self, path, warnings=None, profile=None):
301291
"""Load the credentials config file."""
292+
print("load_creds_file")
302293
config_cls = self.get_creds_reader()
303-
return config_cls.load_config(self, path, profile=profile, no_warn=no_warn)
294+
return config_cls.load_config(self, path, warnings=warnings, profile=profile)
304295

305296
@property
306297
def api_config(self):

cloudsmith_cli/cli/decorators.py

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import click
66

7+
from cloudsmith_cli.cli.warnings import ApiAuthenticationWarning, get_or_create_warnings
8+
79
from ..core.api.init import initialise_api as _initialise_api
810
from ..core.api.user import get_user_brief
911
from . import config, utils, validators
@@ -87,25 +89,17 @@ def common_cli_config_options(f):
8789
envvar="CLOUDSMITH_PROFILE",
8890
help="The name of the profile to use for configuration.",
8991
)
90-
@click.option(
91-
"--no-warn",
92-
is_flag=True,
93-
default=None,
94-
help="Don't warn on misconfiguration issues",
95-
)
9692
@click.pass_context
9793
@functools.wraps(f)
9894
def wrapper(ctx, *args, **kwargs):
9995
# pylint: disable=missing-docstring
10096
opts = config.get_or_create_options(ctx)
97+
warnings = get_or_create_warnings(ctx)
10198
profile = kwargs.pop("profile")
10299
config_file = kwargs.pop("config_file")
103100
creds_file = kwargs.pop("credentials_file")
104-
no_warn = kwargs.pop("no_warn")
105-
if no_warn:
106-
opts.no_warn = no_warn
107-
opts.load_config_file(path=config_file, profile=profile, no_warn=opts.no_warn)
108-
opts.load_creds_file(path=creds_file, profile=profile, no_warn=opts.no_warn)
101+
opts.load_config_file(path=config_file, profile=profile, warnings=warnings)
102+
opts.load_creds_file(path=creds_file, profile=profile, warnings=warnings)
109103
kwargs["opts"] = opts
110104
return ctx.invoke(f, *args, **kwargs)
111105

@@ -316,20 +310,11 @@ def call_print_rate_limit_info_with_opts(rate_info):
316310
)
317311

318312
cloudsmith_host = kwargs["opts"].opts["api_config"].host
319-
no_warn = opts.no_warn
320313
is_auth, _, _, _ = get_user_brief()
321-
if not is_auth and not no_warn:
322-
click.secho(
323-
"Warning: You are not authenticated with the API. "
324-
"Please verify your config files, API key and "
325-
"run `cloudsmith login` if necessary to authenticate.",
326-
fg="yellow",
327-
)
328-
click.secho(
329-
f"You're currently attempting to connect to Cloudsmith instance {cloudsmith_host}",
330-
fg="yellow",
331-
)
332-
opts.no_warn = True
314+
if not is_auth:
315+
warnings = get_or_create_warnings(ctx)
316+
auth_warning = ApiAuthenticationWarning(cloudsmith_host)
317+
warnings.append(auth_warning)
333318

334319
kwargs["opts"] = opts
335320
return ctx.invoke(f, *args, **kwargs)

cloudsmith_cli/cli/warnings.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from abc import ABC
2+
from typing import Dict, List
3+
4+
5+
class CliWarning(ABC):
6+
"""
7+
Abstract base class for all Cloudsmith CLI warnings.
8+
"""
9+
10+
def __repr__(self):
11+
return self.__str__()
12+
13+
def __str__(self):
14+
return f"{self.__class__.__name__}"
15+
16+
17+
class ConfigLoadWarning(CliWarning):
18+
"""
19+
Warning for issues loading the configuration file.
20+
"""
21+
22+
def __init__(self, paths: Dict[str, bool]):
23+
self.paths = paths
24+
self.message = "Failed to load config files. Tried the following paths: \n"
25+
for path, exists in paths.items():
26+
self.message += f" - {path} - exists: {exists})\n"
27+
self.message += "You may need to run `cloudsmith login` to authenticate and create a config file."
28+
29+
def __str__(self):
30+
return f"{self.__class__.__name__} - {self.paths}"
31+
32+
33+
class ProfileNotFoundWarning(CliWarning):
34+
"""
35+
Warning for issues loading the configuration file.
36+
"""
37+
38+
def __init__(self, path, profile):
39+
self.path = path
40+
self.profile = profile
41+
self.message = f"Failed to load config file: {path} for profile: {profile}"
42+
43+
def __str__(self):
44+
return f"{self.__class__.__name__} - {self.path} - {self.profile}"
45+
46+
47+
class ApiAuthenticationWarning(CliWarning):
48+
"""
49+
Warning for issues with API authentication.
50+
"""
51+
52+
def __init__(self, cloudsmith_host):
53+
self.cloudsmith_host = cloudsmith_host
54+
self.message = "\n".join(
55+
[
56+
"Failed to authenticate with Cloudsmith API",
57+
"Please check your credentials and try again",
58+
f"Host: {cloudsmith_host}",
59+
]
60+
)
61+
62+
def __str__(self):
63+
return f"{self.__class__.__name__} - {self.cloudsmith_host}"
64+
65+
66+
class CliWarnings(list):
67+
"""
68+
A class to manage warnings in the CLI.
69+
"""
70+
71+
def __init__(self):
72+
super().__init__()
73+
self.warnings: List[CliWarning] = []
74+
75+
def append(self, warning: CliWarning):
76+
self.warnings.append(warning)
77+
78+
def __dedupe__(self) -> List[CliWarning]:
79+
return list(set(self.warnings))
80+
81+
def report(self) -> List[CliWarning]:
82+
return self.__dedupe__()
83+
84+
def __str__(self) -> str:
85+
return ",".join([str(x) for x in self.warnings])
86+
87+
def __repr__(self) -> str:
88+
return ",".join([str(x) for x in self.warnings])
89+
90+
def __len__(self) -> int:
91+
return len(self.warnings)
92+
93+
94+
def get_or_create_warnings(ctx):
95+
"""Get or create the options object."""
96+
97+
return ctx.ensure_object(CliWarnings)

0 commit comments

Comments
 (0)