6
6
7
7
import click
8
8
import typer
9
+ from rich .console import Console
10
+ from rich .table import Table
9
11
from typer .core import TyperGroup
10
12
11
- from ops2deb import __version__ , builder , formatter , generator , logger , updater
13
+ from ops2deb import __version__ , generator , logger , updater
14
+ from ops2deb .apt import list_repository_packages
15
+ from ops2deb .builder import build_source_packages , find_and_build_source_packages
16
+ from ops2deb .delta import StateDelta , compute_state_delta
12
17
from ops2deb .exceptions import Ops2debError
13
18
from ops2deb .fetcher import DEFAULT_CACHE_DIRECTORY , Fetcher
14
- from ops2deb .parser import load_configuration
19
+ from ops2deb .formatter import format_all
20
+ from ops2deb .parser import Resources , load_resources
15
21
16
22
17
23
class DefaultCommandGroup (TyperGroup ):
@@ -51,12 +57,6 @@ def validate_exit_code(exit_code: int) -> int:
51
57
return exit_code
52
58
53
59
54
- def error (exception : Exception , exit_code : int ) -> NoReturn :
55
- logger .error (str (exception ))
56
- logger .debug (traceback .format_exc ())
57
- sys .exit (exit_code )
58
-
59
-
60
60
option_verbose : bool = typer .Option (
61
61
False ,
62
62
"--verbose" ,
@@ -75,7 +75,7 @@ def error(exception: Exception, exit_code: int) -> NoReturn:
75
75
callback = validate_exit_code ,
76
76
)
77
77
78
- option_search_glob : str = typer .Option (
78
+ option_configurations_search_pattern : str = typer .Option (
79
79
"./**/ops2deb.yml" ,
80
80
"--config" ,
81
81
"-c" ,
@@ -127,47 +127,72 @@ def error(exception: Exception, exit_code: int) -> NoReturn:
127
127
app = typer .Typer (cls = DefaultCommandGroup )
128
128
129
129
130
+ def error (exception : Exception , exit_code : int ) -> NoReturn :
131
+ logger .error (str (exception ))
132
+ logger .debug (traceback .format_exc ())
133
+ sys .exit (exit_code )
134
+
135
+
136
+ def print_loaded_resources (resources : Resources ) -> None :
137
+ logger .title (
138
+ f"Loaded { len (resources .configuration_files )} configuration file(s) and "
139
+ f"{ len (resources .blueprints )} blueprint(s)"
140
+ )
141
+
142
+
143
+ def print_state_delta_as_rich_table (state_delta : StateDelta ) -> None :
144
+ table = Table (box = None , pad_edge = False , show_header = False )
145
+ for package in state_delta .removed :
146
+ table .add_row ("[red]-[/]" , package .name , package .version , package .architecture )
147
+ for package in state_delta .added :
148
+ table .add_row ("[green]+[/]" , package .name , package .version , package .architecture )
149
+ console = Console ()
150
+ console .print (table )
151
+
152
+
130
153
@app .command (help = "Generate and build source packages." )
131
154
def default (
132
155
verbose : bool = option_verbose ,
133
156
exit_code : int = option_exit_code ,
134
- search_glob : str = option_search_glob ,
157
+ configurations_search_pattern : str = option_configurations_search_pattern ,
135
158
output_directory : Path = option_output_directory ,
136
159
cache_directory : Path = option_cache_directory ,
137
160
debian_repository : Optional [str ] = option_debian_repository ,
138
161
only : Optional [List [str ]] = option_only ,
139
162
workers_count : int = option_workers_count ,
140
163
) -> None :
141
164
try :
142
- configuration = load_configuration (search_glob )
165
+ resources = load_resources (configurations_search_pattern )
166
+ print_loaded_resources (resources )
143
167
fetcher = Fetcher (cache_directory )
144
168
packages = generator .generate (
145
- configuration ,
169
+ resources ,
146
170
fetcher ,
147
171
output_directory ,
148
172
debian_repository ,
149
173
only or None ,
150
174
)
151
- builder . build ([p .package_directory for p in packages ], workers_count )
175
+ build_source_packages ([p .package_directory for p in packages ], workers_count )
152
176
except Ops2debError as e :
153
177
error (e , exit_code )
154
178
155
179
156
- @app .command (help = "Generate debian source packages from configuration file ." )
180
+ @app .command (help = "Generate debian source packages from configuration files ." )
157
181
def generate (
158
182
verbose : bool = option_verbose ,
159
183
exit_code : int = option_exit_code ,
160
- search_glob : str = option_search_glob ,
184
+ configurations_search_pattern : str = option_configurations_search_pattern ,
161
185
output_directory : Path = option_output_directory ,
162
186
cache_directory : Path = option_cache_directory ,
163
187
debian_repository : Optional [str ] = option_debian_repository ,
164
188
only : Optional [List [str ]] = option_only ,
165
189
) -> None :
166
190
try :
167
- configuration = load_configuration (search_glob )
191
+ resources = load_resources (configurations_search_pattern )
192
+ print_loaded_resources (resources )
168
193
fetcher = Fetcher (cache_directory )
169
194
generator .generate (
170
- configuration ,
195
+ resources ,
171
196
fetcher ,
172
197
output_directory ,
173
198
debian_repository ,
@@ -185,7 +210,7 @@ def build(
185
210
workers_count : int = option_workers_count ,
186
211
) -> None :
187
212
try :
188
- builder . build_all (output_directory , workers_count )
213
+ find_and_build_source_packages (output_directory , workers_count )
189
214
except Ops2debError as e :
190
215
error (e , exit_code )
191
216
@@ -195,11 +220,11 @@ def purge(cache_directory: Path = option_cache_directory) -> None:
195
220
shutil .rmtree (cache_directory , ignore_errors = True )
196
221
197
222
198
- @app .command (help = "Look for new application releases and edit configuration file ." )
223
+ @app .command (help = "Look for new application releases and edit configuration files ." )
199
224
def update (
200
225
verbose : bool = option_verbose ,
201
226
exit_code : int = option_exit_code ,
202
- search_glob : str = option_search_glob ,
227
+ configurations_search_pattern : str = option_configurations_search_pattern ,
203
228
cache_directory : Path = option_cache_directory ,
204
229
dry_run : bool = typer .Option (
205
230
False , "--dry-run" , "-d" , help = "Don't edit config file."
@@ -223,10 +248,11 @@ def update(
223
248
),
224
249
) -> None :
225
250
try :
226
- configuration = load_configuration (search_glob )
251
+ resources = load_resources (configurations_search_pattern )
252
+ print_loaded_resources (resources )
227
253
fetcher = Fetcher (cache_directory )
228
254
updater .update (
229
- configuration ,
255
+ resources ,
230
256
fetcher ,
231
257
dry_run ,
232
258
output_path ,
@@ -238,26 +264,28 @@ def update(
238
264
error (e , exit_code )
239
265
240
266
241
- @app .command (help = "Validate configuration file ." )
267
+ @app .command (help = "Validate configuration files ." )
242
268
def validate (
243
269
verbose : bool = option_verbose ,
244
270
exit_code : int = option_exit_code ,
245
- search_glob : str = option_search_glob ,
271
+ configurations_search_pattern : str = option_configurations_search_pattern ,
246
272
) -> None :
247
273
try :
248
- load_configuration ( search_glob )
274
+ load_resources ( configurations_search_pattern )
249
275
except Ops2debError as e :
250
276
error (e , exit_code )
251
277
252
278
253
- @app .command (help = "Format configuration file ." )
279
+ @app .command (help = "Format configuration files ." )
254
280
def format (
255
281
verbose : bool = option_verbose ,
256
282
exit_code : int = option_exit_code ,
257
- search_glob : str = option_search_glob ,
283
+ configurations_search_pattern : str = option_configurations_search_pattern ,
258
284
) -> None :
259
285
try :
260
- formatter .format_all (search_glob )
286
+ resources = load_resources (configurations_search_pattern )
287
+ print_loaded_resources (resources )
288
+ format_all (resources )
261
289
except Ops2debError as e :
262
290
error (e , exit_code )
263
291
@@ -267,24 +295,50 @@ def version() -> None:
267
295
logger .info (__version__ )
268
296
269
297
270
- @app .command (help = "Update lockfile ." )
298
+ @app .command (help = "Update lock files ." )
271
299
def lock (
272
300
verbose : bool = option_verbose ,
273
301
exit_code : int = option_exit_code ,
274
- search_glob : str = option_search_glob ,
302
+ configurations_search_pattern : str = option_configurations_search_pattern ,
275
303
cache_directory : Path = option_cache_directory ,
276
304
) -> None :
277
305
try :
278
306
fetcher = Fetcher (cache_directory )
279
- configuration = load_configuration (search_glob )
280
- for blueprint in configuration .blueprints :
281
- lock_file = configuration .get_blueprint_lock (blueprint )
307
+ resources = load_resources (configurations_search_pattern )
308
+ print_loaded_resources (resources )
309
+ for blueprint in resources .blueprints :
310
+ lock_file = resources .get_blueprint_lock (blueprint )
282
311
for url in blueprint .render_fetch_urls ():
283
312
if url not in lock_file :
284
313
fetcher .add_task (url , data = lock_file )
285
314
for result in fetcher .run_tasks ()[0 ]:
286
315
result .task_data .add ([result ])
287
- configuration .save ()
316
+ resources .save ()
317
+ except Ops2debError as e :
318
+ error (e , exit_code )
319
+
320
+
321
+ @app .command (help = "Output state drift configuration files and debian repository" )
322
+ def delta (
323
+ verbose : bool = option_verbose ,
324
+ exit_code : int = option_exit_code ,
325
+ debian_repository : Optional [str ] = option_debian_repository ,
326
+ configurations_search_pattern : str = option_configurations_search_pattern ,
327
+ output_as_json : bool = typer .Option (
328
+ False , "--json" , help = "Output state delta as a JSON"
329
+ ),
330
+ ) -> None :
331
+ if debian_repository is None :
332
+ logger .error ("Missing command line option --repository" )
333
+ sys .exit (exit_code )
334
+ try :
335
+ resources = load_resources (configurations_search_pattern )
336
+ packages = list_repository_packages (debian_repository )
337
+ state_delta = compute_state_delta (packages , resources .blueprints )
338
+ if output_as_json :
339
+ print (state_delta .json (sort_keys = True , indent = 2 ))
340
+ else :
341
+ print_state_delta_as_rich_table (state_delta )
288
342
except Ops2debError as e :
289
343
error (e , exit_code )
290
344
0 commit comments