3030 // debug indicates if the `debug` flag has been set to enable configure the logging for the debug scope.
3131 debug bool
3232 // explicitConfigFilePath stores the path to the application configuration file when the `config` flag is specified.
33+ // It takes precedence over the default application configuration paths.
3334 explicitConfigFilePath string
35+ // snowblockBaseDirs stores the comma-separated paths to the snowblock directories when the `basedirs` flag is
36+ // specified.
37+ // It takes precedence over the path of the application configuration file(s).
38+ snowblockBaseDirs []string
3439)
3540
3641// rootCmd is the root command of the application.
@@ -48,7 +53,7 @@ var rootCmd = &cobra.Command{
4853// Run is the main application function that adds all child commands to the root command and sets flags appropriately.
4954// This is called by `main.main()` and only needs to be run once for the root command.
5055func Run () {
51- // Disable verbose errors to provide custom formatted CLI output via application-wide printer .
56+ // Disable cobra's verbose errors and replace with custom formatted CLI logger from the `pkg/prt` package .
5257 rootCmd .SilenceErrors = true
5358
5459 // Run the application with the given commands, flags and arguments and exit on any (downstream) error.
@@ -60,35 +65,39 @@ func Run() {
6065
6166func init () {
6267 // Specify the functions to be run before each command gets executed.
63- cobra .OnInitialize (initDebugScope , initConfig , initPrinter )
68+ cobra .OnInitialize (initDebugScope , initConfig , initPrinter , mergeConfigValues )
6469
6570 // Define global application flags.
66- rootCmd .PersistentFlags ().StringVar (& explicitConfigFilePath , "config" , "" , "set the configuration file" )
71+ rootCmd .PersistentFlags ().StringVar (& explicitConfigFilePath , "config" , "" , "path to the configuration file" )
6772 rootCmd .PersistentFlags ().BoolVar (& debug , "debug" , false , "enable debug information output" )
73+ rootCmd .PersistentFlags ().StringSliceVarP (
74+ & snowblockBaseDirs , "basedirs" , "b" , config .AppConfig .Snowblocks .Paths ,
75+ "comma-separated paths to snowblock base directories" )
6876
6977 // Set the app version information for the automatically generated `version` flag.
7078 rootCmd .Version = color .CyanString (config .Version )
7179 rootCmd .SetVersionTemplate (`{{printf "%s\n" .Version}}` )
7280
7381 // Create and register all subcommands.
7482 rootCmd .AddCommand (info .NewInfoCmd ())
83+ rootCmd .AddCommand (bootstrap .NewBootstrapCmd ())
7584}
7685
7786// initConfig searches and loads either the default application configuration file paths or the explicit file at the
7887// given path specified through the global `config` flag.
7988func initConfig () {
8089 if explicitConfigFilePath != "" {
81- if err := builder .Load (file .NewFile (explicitConfigFilePath )).Into (& config .AppConfig ); err != nil {
82- prt .Errorf ("while loading custom application configuration file:\n %v" , err )
90+ if err := builder .Load (file .NewFile (explicitConfigFilePath )).Into (& config .AppConfig , false ); err != nil {
91+ prt .Errorf ("Failed to load application configuration file: %v" , err )
8392 os .Exit (1 )
8493 }
8594 } else {
8695 b := builder .Load (config .AppConfigPaths ... )
8796 if len (b .Files ) == 0 {
88- prt .Debugf ("No configuration files found, using default application configuration ." )
97+ prt .Debugf ("No configuration files found, using application defaults ." )
8998 }
90- if err := b .Into (& config .AppConfig ); err != nil {
91- prt .Errorf ("while loading application configuration files: \n %v" , err )
99+ if err := b .Into (& config .AppConfig , true ); err != nil {
100+ prt .Errorf ("Failed to load application configuration file(s): %v" , err )
92101 os .Exit (1 )
93102 }
94103 }
@@ -103,13 +112,36 @@ func initDebugScope() {
103112
104113// setPrinterVerbosityLevel configures the global CLI printer like the verbosity level.
105114func initPrinter () {
106- lvl , err := prt .ParseVerbosityLevel (strings .ToUpper (config .AppConfig .LogLevel ))
107- if err != nil {
108- prt .Debugf ("Error while parsing log level from configuration: %v" , err )
109- prt .Debugf ("Using default INFO level as fallback" )
110- prt .SetVerbosityLevel (prt .InfoVerbosity )
111- } else {
112- prt .Debugf ("Using configured logger level: %s" , strings .ToUpper (config .AppConfig .LogLevel ))
113- prt .SetVerbosityLevel (lvl )
115+ // The `debug` flag always takes precedence and overrides the application configuration value.
116+ if ! debug {
117+ lvl , err := prt .ParseVerbosityLevel (strings .ToUpper (config .AppConfig .LogLevel ))
118+ if err != nil {
119+ prt .Debugf ("Error while parsing log level from configuration: %v" , err )
120+ prt .Debugf ("Using default INFO level as fallback" )
121+ prt .SetVerbosityLevel (prt .InfoVerbosity )
122+ } else {
123+ prt .Debugf ("Using configured logger level: %s" , strings .ToUpper (config .AppConfig .LogLevel ))
124+ prt .SetVerbosityLevel (lvl )
125+ }
126+ }
127+ }
128+
129+ // mergeConfigValues merges the specified (global) flags and merges the corresponding values with the default
130+ // application configuration values.
131+ // Since flags have the highest precedence their value will override application defaults as well as value from
132+ // loaded application configuration files.
133+ func mergeConfigValues () {
134+ // Use configured or individual snowblock base directories if specified, otherwise fall back to the default directory.
135+ if len (config .AppConfig .Snowblocks .BaseDirs ) == 0 {
136+ config .AppConfig .Snowblocks .BaseDirs = append (
137+ config .AppConfig .Snowblocks .BaseDirs , config .DefaultSnowblocksBaseDirectoryName )
138+ }
139+ if len (snowblockBaseDirs ) > 0 {
140+ config .AppConfig .Snowblocks .BaseDirs = snowblockBaseDirs
141+ }
142+
143+ // If the logging level has not been specified fall back to the default level.
144+ if config .AppConfig .LogLevel == "" {
145+ config .AppConfig .LogLevel = config .DefaultLoggingLevel
114146 }
115147}
0 commit comments