20
20
)
21
21
from macaron .config .defaults import create_defaults , load_defaults
22
22
from macaron .config .global_config import global_config
23
+ from macaron .console import RichConsoleHandler , access_handler
23
24
from macaron .errors import ConfigurationError
24
25
from macaron .output_reporter .reporter import HTMLReporter , JSONReporter , PolicyReporter
25
26
from macaron .policy_engine .policy_engine import run_policy_engine , show_prelude
@@ -63,7 +64,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
63
64
if analyzer_single_args .provenance_expectation is not None :
64
65
if not os .path .exists (analyzer_single_args .provenance_expectation ):
65
66
logger .critical (
66
- 'The provenance expectation file "%s" does not exist.' , analyzer_single_args .provenance_expectation
67
+ 'The provenance expectation file "%s" does not exist.' ,
68
+ analyzer_single_args .provenance_expectation ,
67
69
)
68
70
sys .exit (os .EX_OSFILE )
69
71
global_config .load_expectation_files (analyzer_single_args .provenance_expectation )
@@ -72,7 +74,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
72
74
if analyzer_single_args .python_venv is not None :
73
75
if not os .path .exists (analyzer_single_args .python_venv ):
74
76
logger .critical (
75
- 'The Python virtual environment path "%s" does not exist.' , analyzer_single_args .python_venv
77
+ 'The Python virtual environment path "%s" does not exist.' ,
78
+ analyzer_single_args .python_venv ,
76
79
)
77
80
sys .exit (os .EX_OSFILE )
78
81
global_config .load_python_venv (analyzer_single_args .python_venv )
@@ -95,7 +98,10 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
95
98
else :
96
99
user_provided_local_maven_repo = analyzer_single_args .local_maven_repo
97
100
if not os .path .isdir (user_provided_local_maven_repo ):
98
- logger .error ("The user provided local Maven repo at %s is not valid." , user_provided_local_maven_repo )
101
+ logger .error (
102
+ "The user provided local Maven repo at %s is not valid." ,
103
+ user_provided_local_maven_repo ,
104
+ )
99
105
sys .exit (os .EX_USAGE )
100
106
101
107
global_config .local_maven_repo = user_provided_local_maven_repo
@@ -111,7 +117,8 @@ def analyze_slsa_levels_single(analyzer_single_args: argparse.Namespace) -> None
111
117
lstrip_blocks = True ,
112
118
)
113
119
html_reporter = HTMLReporter (
114
- env = custom_jinja_env , target_template = os .path .basename (analyzer_single_args .template_path )
120
+ env = custom_jinja_env ,
121
+ target_template = os .path .basename (analyzer_single_args .template_path ),
115
122
)
116
123
if not html_reporter .template :
117
124
logger .error ("Exiting because the custom template cannot be found." )
@@ -207,8 +214,11 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int:
207
214
208
215
result = run_policy_engine (verify_policy_args .database , policy_content )
209
216
vsa = generate_vsa (policy_content = policy_content , policy_result = result )
217
+ # Retrieve the console handler previously configured via the access_handler.
218
+ rich_handler = access_handler .get_handler ()
210
219
if vsa is not None :
211
220
vsa_filepath = os .path .join (global_config .output_path , "vsa.intoto.jsonl" )
221
+ rich_handler .update_vsa (os .path .relpath (vsa_filepath , os .getcwd ()))
212
222
logger .info (
213
223
"Generating the Verification Summary Attestation (VSA) to %s." ,
214
224
os .path .relpath (vsa_filepath , os .getcwd ()),
@@ -222,8 +232,12 @@ def verify_policy(verify_policy_args: argparse.Namespace) -> int:
222
232
file .write (json .dumps (vsa ))
223
233
except OSError as err :
224
234
logger .error (
225
- "Could not generate the VSA to %s. Error: %s" , os .path .relpath (vsa_filepath , os .getcwd ()), err
235
+ "Could not generate the VSA to %s. Error: %s" ,
236
+ os .path .relpath (vsa_filepath , os .getcwd ()),
237
+ err ,
226
238
)
239
+ else :
240
+ rich_handler .update_vsa ("No VSA generated." )
227
241
228
242
policy_reporter = PolicyReporter ()
229
243
policy_reporter .generate (global_config .output_path , result )
@@ -290,16 +304,23 @@ def find_source(find_args: argparse.Namespace) -> int:
290
304
291
305
def perform_action (action_args : argparse .Namespace ) -> None :
292
306
"""Perform the indicated action of Macaron."""
307
+ rich_handler = access_handler .get_handler ()
293
308
match action_args .action :
294
309
case "dump-defaults" :
310
+ if not action_args .disable_rich_output :
311
+ rich_handler .start ("dump-defaults" )
295
312
# Create the defaults.ini file in the output dir and exit.
296
313
create_defaults (action_args .output_dir , os .getcwd ())
297
314
sys .exit (os .EX_OK )
298
315
299
316
case "verify-policy" :
317
+ if not action_args .disable_rich_output :
318
+ rich_handler .start ("verify-policy" )
300
319
sys .exit (verify_policy (action_args ))
301
320
302
321
case "analyze" :
322
+ if not action_args .disable_rich_output :
323
+ rich_handler .start ("analyze" )
303
324
if not global_config .gh_token :
304
325
logger .error ("GitHub access token not set." )
305
326
sys .exit (os .EX_USAGE )
@@ -317,6 +338,8 @@ def perform_action(action_args: argparse.Namespace) -> None:
317
338
analyze_slsa_levels_single (action_args )
318
339
319
340
case "find-source" :
341
+ if not action_args .disable_rich_output :
342
+ rich_handler .start ("find-source" )
320
343
try :
321
344
for git_service in GIT_SERVICES :
322
345
git_service .load_defaults ()
@@ -329,6 +352,8 @@ def perform_action(action_args: argparse.Namespace) -> None:
329
352
find_source (action_args )
330
353
331
354
case "gen-build-spec" :
355
+ if not action_args .disable_rich_output :
356
+ rich_handler .start ("gen-build-spec" )
332
357
sys .exit (gen_build_spec (action_args ))
333
358
334
359
case _:
@@ -393,6 +418,13 @@ def main(argv: list[str] | None = None) -> None:
393
418
action = "store_true" ,
394
419
)
395
420
421
+ main_parser .add_argument (
422
+ "--disable-rich-output" ,
423
+ default = False ,
424
+ help = "Disable Rich UI output" ,
425
+ action = "store_true" ,
426
+ )
427
+
396
428
main_parser .add_argument (
397
429
"-o" ,
398
430
"--output-dir" ,
@@ -531,7 +563,10 @@ def main(argv: list[str] | None = None) -> None:
531
563
)
532
564
533
565
# Dump the default values.
534
- sub_parser .add_parser (name = "dump-defaults" , description = "Dumps the defaults.ini file to the output directory." )
566
+ sub_parser .add_parser (
567
+ name = "dump-defaults" ,
568
+ description = "Dumps the defaults.ini file to the output directory." ,
569
+ )
535
570
536
571
# Verify the Datalog policy.
537
572
vp_parser = sub_parser .add_parser (name = "verify-policy" )
@@ -593,65 +628,98 @@ def main(argv: list[str] | None = None) -> None:
593
628
main_parser .print_help ()
594
629
sys .exit (os .EX_USAGE )
595
630
596
- if args .verbose :
597
- log_level = logging .DEBUG
598
- log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] [%(levelname)s] %(message)s"
599
- else :
600
- log_level = logging .INFO
601
- log_format = "%(asctime)s [%(levelname)s] %(message)s"
602
-
603
631
# Set global logging config. We need the stream handler for the initial
604
632
# output directory checking log messages.
605
- st_handler = logging .StreamHandler (sys .stdout )
606
- logging .basicConfig (format = log_format , handlers = [st_handler ], force = True , level = log_level )
633
+ st_handler : logging .StreamHandler = logging .StreamHandler (sys .stdout )
634
+ rich_handler : RichConsoleHandler = access_handler .set_handler (args .verbose )
635
+ if args .disable_rich_output :
636
+ if args .verbose :
637
+ log_level = logging .DEBUG
638
+ log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] [%(levelname)s] %(message)s"
639
+ else :
640
+ log_level = logging .INFO
641
+ log_format = "%(asctime)s [%(levelname)s] %(message)s"
642
+ st_handler = logging .StreamHandler (sys .stdout )
643
+ logging .basicConfig (format = log_format , handlers = [st_handler ], force = True , level = log_level )
644
+ else :
645
+ if args .verbose :
646
+ log_level = logging .DEBUG
647
+ log_format = "%(asctime)s [%(name)s:%(funcName)s:%(lineno)d] %(message)s"
648
+ else :
649
+ log_level = logging .INFO
650
+ log_format = "%(asctime)s %(message)s"
651
+ rich_handler = access_handler .set_handler (args .verbose )
652
+ logging .basicConfig (format = log_format , handlers = [rich_handler ], force = True , level = log_level )
607
653
608
- # Set the output directory.
609
- if not args .output_dir :
610
- logger .error ("The output path cannot be empty. Exiting ..." )
611
- sys .exit (os .EX_USAGE )
654
+ try :
655
+ # Set the output directory.
656
+ if not args .output_dir :
657
+ logger .error ("The output path cannot be empty. Exiting ..." )
658
+ sys .exit (os .EX_USAGE )
612
659
613
- if os .path .isfile (args .output_dir ):
614
- logger .error ("The output directory already exists. Exiting ..." )
615
- sys .exit (os .EX_USAGE )
660
+ if os .path .isfile (args .output_dir ):
661
+ logger .error ("The output directory already exists. Exiting ..." )
662
+ sys .exit (os .EX_USAGE )
616
663
617
- if os .path .isdir (args .output_dir ):
618
- logger .info ("Setting the output directory to %s" , os .path .relpath (args .output_dir , os .getcwd ()))
619
- else :
620
- logger .info ("No directory at %s. Creating one ..." , os .path .relpath (args .output_dir , os .getcwd ()))
621
- os .makedirs (args .output_dir )
622
-
623
- # Add file handler to the root logger. Remove stream handler from the
624
- # root logger to prevent dependencies printing logs to stdout.
625
- debug_log_path = os .path .join (args .output_dir , "debug.log" )
626
- log_file_handler = logging .FileHandler (debug_log_path , "w" )
627
- log_file_handler .setFormatter (logging .Formatter (log_format ))
628
- logging .getLogger ().removeHandler (st_handler )
629
- logging .getLogger ().addHandler (log_file_handler )
630
-
631
- # Add StreamHandler to the Macaron logger only.
632
- mcn_logger = logging .getLogger ("macaron" )
633
- mcn_logger .addHandler (st_handler )
634
-
635
- logger .info ("The logs will be stored in debug.log" )
636
-
637
- # Set Macaron's global configuration.
638
- # The path to provenance expectation files will be updated if
639
- # set through analyze sub-command.
640
- global_config .load (
641
- macaron_path = macaron .MACARON_PATH ,
642
- output_path = args .output_dir ,
643
- build_log_path = os .path .join (args .output_dir , "build_log" ),
644
- debug_level = log_level ,
645
- local_repos_path = args .local_repos_path ,
646
- resources_path = os .path .join (macaron .MACARON_PATH , "resources" ),
647
- )
664
+ if os .path .isdir (args .output_dir ):
665
+ logger .info (
666
+ "Setting the output directory to %s" ,
667
+ os .path .relpath (args .output_dir , os .getcwd ()),
668
+ )
669
+ else :
670
+ logger .info (
671
+ "No directory at %s. Creating one ..." ,
672
+ os .path .relpath (args .output_dir , os .getcwd ()),
673
+ )
674
+ os .makedirs (args .output_dir )
675
+
676
+ # Add file handler to the root logger. Remove stream handler from the
677
+ # root logger to prevent dependencies printing logs to stdout.
678
+ debug_log_path = os .path .join (args .output_dir , "debug.log" )
679
+ log_file_handler = logging .FileHandler (debug_log_path , "w" )
680
+ log_file_handler .setFormatter (logging .Formatter (log_format ))
681
+ if args .disable_rich_output :
682
+ logging .getLogger ().removeHandler (st_handler )
683
+ else :
684
+ logging .getLogger ().removeHandler (rich_handler )
685
+ logging .getLogger ().addHandler (log_file_handler )
686
+
687
+ # Add StreamHandler to the Macaron logger only.
688
+ mcn_logger = logging .getLogger ("macaron" )
689
+ if args .disable_rich_output :
690
+ mcn_logger .addHandler (st_handler )
691
+ else :
692
+ mcn_logger .addHandler (rich_handler )
693
+
694
+ logger .info ("The logs will be stored in debug.log" )
695
+
696
+ # Set Macaron's global configuration.
697
+ # The path to provenance expectation files will be updated if
698
+ # set through analyze sub-command.
699
+ global_config .load (
700
+ macaron_path = macaron .MACARON_PATH ,
701
+ output_path = args .output_dir ,
702
+ build_log_path = os .path .join (args .output_dir , "build_log" ),
703
+ debug_level = log_level ,
704
+ local_repos_path = args .local_repos_path ,
705
+ resources_path = os .path .join (macaron .MACARON_PATH , "resources" ),
706
+ )
648
707
649
- # Load the default values from defaults.ini files.
650
- if not load_defaults (args .defaults_path ):
651
- logger .error ("Exiting because the defaults configuration could not be loaded." )
652
- sys .exit (os .EX_NOINPUT )
708
+ # Load the default values from defaults.ini files.
709
+ if not load_defaults (args .defaults_path ):
710
+ logger .error ("Exiting because the defaults configuration could not be loaded." )
711
+ sys .exit (os .EX_NOINPUT )
653
712
654
- perform_action (args )
713
+ perform_action (args )
714
+ except KeyboardInterrupt :
715
+ if not args .disable_rich_output :
716
+ rich_handler .error ("Macaron failed: Interrupted by user" )
717
+ sys .exit (os .EX_SOFTWARE )
718
+ finally :
719
+ if args .disable_rich_output :
720
+ st_handler .close ()
721
+ else :
722
+ rich_handler .close ()
655
723
656
724
657
725
def _get_token_from_dict_or_env (token : str , token_dict : dict [str , str ]) -> str :
0 commit comments