Coverage for harbor_cli/commands/cli/cli_config.py: 28%
68 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-09 12:09 +0100
1from __future__ import annotations
3from pathlib import Path
4from typing import Any
5from typing import Optional
7import typer
8from pydantic import Extra
10from ...config import HarborCLIConfig
11from ...logs import logger
12from ...output.console import console
13from ...output.console import err_console
14from ...output.console import exit_err
15from ...output.console import success
16from ...output.render import render_result
17from ...output.table.anysequence import AnySequence
18from ...state import state
20# Create a command group
21app = typer.Typer(
22 name="cli-config",
23 help="Manage CLI configuration.",
24 no_args_is_help=True,
25)
28def render_config(config: HarborCLIConfig, as_toml: bool) -> None:
29 if as_toml:
30 console.print(config.toml(expose_secrets=False), markup=False)
31 else:
32 render_result(config)
35@app.command("get")
36def get_cli_config(
37 ctx: typer.Context,
38 as_toml: bool = typer.Option(
39 True,
40 "--toml/--no-toml",
41 help="Show the current configuration in TOML format after setting the value. Overrides --format.",
42 ),
43) -> None:
44 """Show the current CLI configuration."""
45 render_config(state.config, as_toml)
46 logger.info(f"Source: {state.config.config_file}")
49@app.command("keys")
50def get_cli_config_keys(ctx: typer.Context) -> None:
51 """Show the current CLI configuration."""
53 def get_fields(field: dict[str, Any], current: str) -> list[str]:
54 fields = []
55 if isinstance(field, dict):
56 for sub_key, sub_value in field.items():
57 f = get_fields(sub_value, f"{current}.{sub_key}")
58 fields.extend(f)
59 else:
60 fields.append(current)
61 return fields
63 ff = []
64 d = state.config.dict()
65 for key, value in d.items():
66 if isinstance(value, dict):
67 ff.extend(get_fields(value, key))
68 else:
69 ff.append(key) # no subkeys
71 render_result(AnySequence(values=ff, title="Config Keys"))
74@app.command("set", no_args_is_help=True)
75def set_cli_config(
76 ctx: typer.Context,
77 key: str = typer.Argument(
78 ...,
79 help="Key to set. Subkeys can be specified using dot notation. e.g. [green]'harbor.url'[/]",
80 ),
81 value: str = typer.Argument(..., help="Value to set."),
82 path: Path = typer.Option(None, "--path", help="Path to save configuration file."),
83 session: bool = typer.Option(
84 False,
85 "--session",
86 help="Set the value in the current session only. The value will not be saved to disk. Only useful in REPL mode.",
87 ),
88 show_config: bool = typer.Option(
89 True,
90 "--show/--no-show",
91 help="Show the current configuration after setting the value.",
92 ),
93 as_toml: bool = typer.Option(
94 True,
95 "--toml/--no-toml",
96 help="Render config as TOML if [green]--show[/] is set. Overrides [green]--format[/].",
97 ),
98) -> None:
99 """Set a key in the CLI configuration."""
100 attrs = []
101 if "." in key:
102 attrs = key.split(".")
104 try:
105 # temporarily forbid extra fields, so typos raise an error
106 state.config.__config__.extra = Extra.forbid
107 if attrs:
108 obj = getattr(state.config, attrs[0])
109 for attr in attrs[1:-1]:
110 obj = getattr(obj, attr)
111 setattr(obj, attrs[-1], value)
112 else:
113 setattr(state.config, key, value)
114 except (
115 ValueError, # pydantic raises ValueError for unknown fields
116 AttributeError,
117 ):
118 err_console.print(f"Invalid key: [red]{key}[/]")
119 finally:
120 state.config.__config__.extra = Extra.forbid
122 if not session:
123 state.config.save(path=path)
125 if show_config:
126 render_config(state.config, as_toml)
129@app.command("write")
130def write_session_config(
131 ctx: typer.Context,
132 path: Optional[Path] = typer.Option(
133 None,
134 "--path",
135 help="Path to save configuration file. Overrides the config's current path.",
136 ),
137) -> None:
138 """Write the current [bold]session[/] configuration to disk. Used to save
139 changes made with [green]harbor cli-config set --session[/] in REPL mode."""
140 save_path = path or state.config.config_file
141 if save_path is None:
142 exit_err(
143 "No path specified and no path found in current configuration. Use [green]--path[/] to specify a path."
144 )
145 state.config.save(path=save_path)
146 success(f"Saved configuration to [green]{save_path}[/]")
149# TODO: reload config