|
7 | 7 | from cloudsmith_cli.cli import validators |
8 | 8 |
|
9 | 9 | from ..core.api.init import initialise_api as _initialise_api |
| 10 | +from ..core.config_resolver import ConfigResolver |
| 11 | +from ..core.credentials import CredentialContext, CredentialProviderChain |
10 | 12 | from ..core.mcp import server |
11 | 13 | from . import config, utils |
12 | 14 |
|
@@ -221,8 +223,17 @@ def wrapper(ctx, *args, **kwargs): |
221 | 223 | return wrapper |
222 | 224 |
|
223 | 225 |
|
224 | | -def initialise_api(f): |
225 | | - """Initialise the Cloudsmith API for use.""" |
| 226 | +def _pop_boolean_option(kwargs, name, invert=False): |
| 227 | + """Pop a boolean option from kwargs, optionally inverting it.""" |
| 228 | + value = kwargs.pop(name) |
| 229 | + value = value if value is not None else None |
| 230 | + if value is not None and invert: |
| 231 | + value = not value |
| 232 | + return value |
| 233 | + |
| 234 | + |
| 235 | +def resolve_credentials(f): |
| 236 | + """Resolve client configuration and credentials without initialising the API.""" |
226 | 237 |
|
227 | 238 | @click.option( |
228 | 239 | "--api-host", envvar="CLOUDSMITH_API_HOST", help="The API host to connect to." |
@@ -252,6 +263,75 @@ def initialise_api(f): |
252 | 263 | envvar="CLOUDSMITH_API_HEADERS", |
253 | 264 | help="A CSV list of extra headers (key=value) to send to the API.", |
254 | 265 | ) |
| 266 | + @click.pass_context |
| 267 | + @functools.wraps(f) |
| 268 | + def wrapper(ctx, *args, **kwargs): |
| 269 | + # pylint: disable=missing-docstring |
| 270 | + opts = config.get_or_create_options(ctx) |
| 271 | + |
| 272 | + cli_api_host = kwargs.pop("api_host") |
| 273 | + cli_api_proxy = kwargs.pop("api_proxy") |
| 274 | + cli_ssl_verify = _pop_boolean_option( |
| 275 | + kwargs, "without_api_ssl_verify", invert=True |
| 276 | + ) |
| 277 | + cli_api_user_agent = kwargs.pop("api_user_agent") |
| 278 | + cli_api_headers = kwargs.pop("api_headers") |
| 279 | + |
| 280 | + client_config = ConfigResolver().resolve( |
| 281 | + api_host=cli_api_host, |
| 282 | + proxy=cli_api_proxy, |
| 283 | + ssl_verify=cli_ssl_verify, |
| 284 | + user_agent=cli_api_user_agent, |
| 285 | + headers=cli_api_headers, |
| 286 | + debug=opts.debug, |
| 287 | + profile=ctx.meta.get("profile"), |
| 288 | + config_file=ctx.meta.get("config_file"), |
| 289 | + ) |
| 290 | + |
| 291 | + opts.api_host = client_config.api_host |
| 292 | + opts.api_proxy = client_config.proxy |
| 293 | + opts.api_ssl_verify = client_config.ssl_verify |
| 294 | + opts.api_user_agent = client_config.user_agent |
| 295 | + opts.api_headers = client_config.headers |
| 296 | + |
| 297 | + credential_context = CredentialContext( |
| 298 | + config=client_config, |
| 299 | + creds_file_path=ctx.meta.get("creds_file"), |
| 300 | + profile=ctx.meta.get("profile"), |
| 301 | + debug=opts.debug, |
| 302 | + cli_api_key=opts.api_key, |
| 303 | + ) |
| 304 | + |
| 305 | + chain = CredentialProviderChain() |
| 306 | + |
| 307 | + credential = chain.resolve(credential_context) |
| 308 | + |
| 309 | + if credential_context.keyring_refresh_failed: |
| 310 | + click.secho( |
| 311 | + "An error occurred when attempting to refresh your SSO access token. " |
| 312 | + "To refresh this session, run 'cloudsmith auth'", |
| 313 | + fg="yellow", |
| 314 | + err=True, |
| 315 | + ) |
| 316 | + if credential: |
| 317 | + click.secho( |
| 318 | + "Falling back to API key authentication.", |
| 319 | + fg="yellow", |
| 320 | + err=True, |
| 321 | + ) |
| 322 | + |
| 323 | + opts.client_config = client_config |
| 324 | + opts.credential = credential |
| 325 | + |
| 326 | + kwargs["opts"] = opts |
| 327 | + return ctx.invoke(f, *args, **kwargs) |
| 328 | + |
| 329 | + return wrapper |
| 330 | + |
| 331 | + |
| 332 | +def initialise_api(f): |
| 333 | + """Initialise the Cloudsmith API for use. Depends on resolve_credentials.""" |
| 334 | + |
255 | 335 | @click.option( |
256 | 336 | "-R", |
257 | 337 | "--without-rate-limit", |
@@ -294,49 +374,50 @@ def initialise_api(f): |
294 | 374 | @functools.wraps(f) |
295 | 375 | def wrapper(ctx, *args, **kwargs): |
296 | 376 | # pylint: disable=missing-docstring |
297 | | - def _set_boolean(name, invert=False): |
298 | | - value = kwargs.pop(name) |
299 | | - value = value if value is not None else None |
300 | | - if value is not None and invert: |
301 | | - value = not value |
302 | | - return value |
303 | | - |
304 | 377 | opts = config.get_or_create_options(ctx) |
305 | | - opts.api_host = kwargs.pop("api_host") |
306 | | - opts.api_proxy = kwargs.pop("api_proxy") |
307 | | - opts.api_ssl_verify = _set_boolean("without_api_ssl_verify", invert=True) |
308 | | - opts.api_user_agent = kwargs.pop("api_user_agent") |
309 | | - opts.api_headers = kwargs.pop("api_headers") |
310 | | - opts.rate_limit = _set_boolean("without_rate_limit", invert=True) |
| 378 | + |
| 379 | + opts.rate_limit = _pop_boolean_option(kwargs, "without_rate_limit", invert=True) |
311 | 380 | opts.rate_limit_warning = kwargs.pop("rate_limit_warning") |
312 | 381 | opts.error_retry_max = kwargs.pop("error_retry_max") |
313 | 382 | opts.error_retry_backoff = kwargs.pop("error_retry_backoff") |
314 | 383 | opts.error_retry_codes = kwargs.pop("error_retry_codes") |
315 | 384 | opts.error_retry_cb = report_retry |
316 | 385 |
|
| 386 | + client_config = opts.client_config |
| 387 | + credential = opts.credential |
| 388 | + |
| 389 | + resolved_key = None |
| 390 | + resolved_access_token = None |
| 391 | + if credential: |
| 392 | + if credential.auth_type == "bearer": |
| 393 | + resolved_access_token = credential.api_key |
| 394 | + else: |
| 395 | + resolved_key = credential.api_key |
| 396 | + |
317 | 397 | def call_print_rate_limit_info_with_opts(rate_info): |
318 | 398 | utils.print_rate_limit_info(opts, rate_info) |
319 | 399 |
|
320 | 400 | opts.api_config = _initialise_api( |
321 | | - debug=opts.debug, |
322 | | - host=opts.api_host, |
323 | | - key=opts.api_key, |
324 | | - proxy=opts.api_proxy, |
325 | | - ssl_verify=opts.api_ssl_verify, |
326 | | - user_agent=opts.api_user_agent, |
327 | | - headers=opts.api_headers, |
| 401 | + debug=client_config.debug, |
| 402 | + host=client_config.api_host, |
| 403 | + key=resolved_key, |
| 404 | + proxy=client_config.proxy, |
| 405 | + ssl_verify=client_config.ssl_verify, |
| 406 | + user_agent=client_config.user_agent, |
| 407 | + headers=client_config.headers, |
328 | 408 | rate_limit=opts.rate_limit, |
329 | 409 | rate_limit_callback=call_print_rate_limit_info_with_opts, |
330 | 410 | error_retry_max=opts.error_retry_max, |
331 | 411 | error_retry_backoff=opts.error_retry_backoff, |
332 | 412 | error_retry_codes=opts.error_retry_codes, |
333 | 413 | error_retry_cb=opts.error_retry_cb, |
| 414 | + access_token=resolved_access_token, |
334 | 415 | ) |
335 | 416 |
|
336 | 417 | kwargs["opts"] = opts |
337 | 418 | return ctx.invoke(f, *args, **kwargs) |
338 | 419 |
|
339 | | - return wrapper |
| 420 | + return resolve_credentials(wrapper) |
340 | 421 |
|
341 | 422 |
|
342 | 423 | def initialise_mcp(f): |
|
0 commit comments