66 from revenueholdings_license import require_license
77except ImportError :
88 import warnings
9- warnings .warn ("revenueholdings-license not installed; license checks skipped" , stacklevel = 2 )
9+
10+ warnings .warn (
11+ "revenueholdings-license not installed; license checks skipped" , stacklevel = 2
12+ )
13+
1014 def require_license (product : str ) -> None : # type: ignore[misc]
1115 pass
1216
17+
1318from configdrift import __version__
1419from configdrift .diff import (
1520 Severity ,
@@ -61,11 +66,30 @@ class OutputFormat(str, Enum):
6166_DEFAULT_TARGET = "target"
6267_DEFAULT_OUTPUT : OutputFormat = OutputFormat .TABLE
6368_DEFAULT_STRICT = False
64- _FILES_ARG = typer .Argument (..., help = "Config files to compare (2+ files; first file is baseline)." )
65- _BASELINE_OPT = typer .Option (_DEFAULT_BASELINE , "--baseline" , "-b" , help = "Baseline environment label (default: 'dev')." )
66- _TARGET_OPT = typer .Option (_DEFAULT_TARGET , "--target" , "-t" , help = "Target environment label (default: 'target')." )
67- _OUTPUT_OPT = typer .Option (_DEFAULT_OUTPUT , "--output" , "-o" , help = "Output format: table, json, or silent (exit code only)." )
68- _STRICT_OPT = typer .Option (_DEFAULT_STRICT , "--strict" , help = "Exit 1 on ANY drift, not just breaking changes." )
69+ _FILES_ARG = typer .Argument (
70+ ..., help = "Config files to compare (2+ files; first file is baseline)."
71+ )
72+ _BASELINE_OPT = typer .Option (
73+ _DEFAULT_BASELINE ,
74+ "--baseline" ,
75+ "-b" ,
76+ help = "Baseline environment label (default: 'dev')." ,
77+ )
78+ _TARGET_OPT = typer .Option (
79+ _DEFAULT_TARGET ,
80+ "--target" ,
81+ "-t" ,
82+ help = "Target environment label (default: 'target')." ,
83+ )
84+ _OUTPUT_OPT = typer .Option (
85+ _DEFAULT_OUTPUT ,
86+ "--output" ,
87+ "-o" ,
88+ help = "Output format: table, json, or silent (exit code only)." ,
89+ )
90+ _STRICT_OPT = typer .Option (
91+ _DEFAULT_STRICT , "--strict" , help = "Exit 1 on ANY drift, not just breaking changes."
92+ )
6993
7094
7195@app .command ()
@@ -82,7 +106,11 @@ def check(
82106 raise typer .Exit (code = 1 )
83107
84108 env_configs : dict [str , dict [str , Any ]] = {}
85- env_labels = [baseline , target ] if len (files ) == 2 else [f"file_{ i + 1 } " for i in range (len (files ))]
109+ env_labels = (
110+ [baseline , target ]
111+ if len (files ) == 2
112+ else [f"file_{ i + 1 } " for i in range (len (files ))]
113+ )
86114
87115 for label , filepath in zip (env_labels , files , strict = False ):
88116 try :
@@ -106,7 +134,11 @@ def check(
106134 _output_table (results , baseline_env )
107135
108136 # Exit codes for CI gating
109- has_drift = any (r .count > 0 for r in results .values ()) if strict else any (r .has_breaking for r in results .values ())
137+ has_drift = (
138+ any (r .count > 0 for r in results .values ())
139+ if strict
140+ else any (r .has_breaking for r in results .values ())
141+ )
110142 if has_drift :
111143 raise typer .Exit (code = 1 )
112144
@@ -124,12 +156,16 @@ def _output_table(results: dict[str, Any], baseline_env: str) -> None:
124156 table .add_column ("Severity" , style = "magenta" )
125157
126158 for change in diff_result .changes :
127- symbol = {"added" : "+" , "removed" : "-" , "changed" : "~" }[change .change_type .value ]
159+ symbol = {"added" : "+" , "removed" : "-" , "changed" : "~" }[
160+ change .change_type .value
161+ ]
128162 old_str = str (change .old_value ) if change .old_value is not None else ""
129163 new_str = str (change .new_value ) if change .new_value is not None else ""
130164 sev_style = (
131- "red" if change .severity == Severity .BREAKING
132- else "yellow" if change .severity == Severity .WARNING
165+ "red"
166+ if change .severity == Severity .BREAKING
167+ else "yellow"
168+ if change .severity == Severity .WARNING
133169 else "white"
134170 )
135171 table .add_row (
@@ -149,6 +185,7 @@ def _output_table(results: dict[str, Any], baseline_env: str) -> None:
149185
150186def _output_json (results : dict [str , Any ]) -> None :
151187 import json
188+
152189 output = {}
153190 for env_name , diff_result in results .items ():
154191 output [env_name ] = {
@@ -171,17 +208,27 @@ def _output_json(results: dict[str, Any]) -> None:
171208@app .command ()
172209def scan (
173210 dirs : list [str ] | None = typer .Argument ( # noqa: B008
174- None , help = "Directories containing config files. Each dir is treated as an environment." ,
211+ None ,
212+ help = "Directories containing config files. Each dir is treated as an environment." ,
175213 ),
176- baseline : str = typer .Option ("dev" , "--baseline" , "-b" , help = "Baseline directory name for comparison." ), # noqa: B008
177- config : str | None = typer .Option (None , "--config" , "-c" , help = "Path to .configdrift.yaml config file." ), # noqa: B008
178- output : OutputFormat = typer .Option (OutputFormat .TABLE , "--output" , "-o" , help = "Output format." ), # noqa: B008
179- strict : bool = typer .Option (False , "--strict" , help = "Exit 1 on ANY drift, not just breaking changes." ), # noqa: B008
214+ baseline : str = typer .Option (
215+ "dev" , "--baseline" , "-b" , help = "Baseline directory name for comparison."
216+ ), # noqa: B008
217+ config : str | None = typer .Option (
218+ None , "--config" , "-c" , help = "Path to .configdrift.yaml config file."
219+ ), # noqa: B008
220+ output : OutputFormat = typer .Option (
221+ OutputFormat .TABLE , "--output" , "-o" , help = "Output format."
222+ ), # noqa: B008
223+ strict : bool = typer .Option (
224+ False , "--strict" , help = "Exit 1 on ANY drift, not just breaking changes."
225+ ), # noqa: B008
180226):
181227 """Scan directories of config files and compare environments."""
182228 if config :
183229 # Load config file for directory → env mapping (raw, not flattened)
184230 import yaml as _yaml
231+
185232 with open (config , encoding = "utf-8" ) as _f :
186233 cfg_data = _yaml .safe_load (_f ) or {}
187234 dir_mapping = cfg_data .get ("environments" , {})
@@ -192,7 +239,9 @@ def scan(
192239 env_name = Path (d ).stem
193240 dir_mapping [env_name ] = d
194241 else :
195- console .print ("[red]ERROR: Provide either --config or directories as arguments.[/red]" )
242+ console .print (
243+ "[red]ERROR: Provide either --config or directories as arguments.[/red]"
244+ )
196245 raise typer .Exit (code = 1 )
197246
198247 if baseline not in dir_mapping :
@@ -204,7 +253,9 @@ def scan(
204253 env_configs [env_name ] = {}
205254 p = Path (dir_path )
206255 if not p .is_dir ():
207- console .print (f"[yellow]Warning: '{ dir_path } ' is not a directory, skipping.[/yellow]" )
256+ console .print (
257+ f"[yellow]Warning: '{ dir_path } ' is not a directory, skipping.[/yellow]"
258+ )
208259 continue
209260 # Load all supported config files in the directory and merge
210261 for ext in ("*.yaml" , "*.yml" , "*.json" , "*.toml" , "*.env" ):
@@ -228,7 +279,11 @@ def scan(
228279 else :
229280 _output_table (results , baseline )
230281
231- has_drift = any (r .count > 0 for r in results .values ()) if strict else any (r .has_breaking for r in results .values ())
282+ has_drift = (
283+ any (r .count > 0 for r in results .values ())
284+ if strict
285+ else any (r .has_breaking for r in results .values ())
286+ )
232287 if has_drift :
233288 raise typer .Exit (code = 1 )
234289
0 commit comments