2121 */ 
2222
2323import  com .fasterxml .jackson .databind .ObjectMapper ;
24+ import  com .fasterxml .jackson .databind .exc .UnrecognizedPropertyException ;
2425import  com .fasterxml .jackson .dataformat .yaml .YAMLFactory ;
2526import  com .fasterxml .jackson .dataformat .yaml .YAMLGenerator ;
2627import  com .google .inject .Injector ;
28+ import  com .walmartlabs .concord .cli .CliConfig .CliConfigContext ;
2729import  com .walmartlabs .concord .cli .runner .*;
2830import  com .walmartlabs .concord .common .ConfigurationUtils ;
2931import  com .walmartlabs .concord .common .FileVisitor ;
5052import  com .walmartlabs .concord .runtime .v2 .sdk .*;
5153import  com .walmartlabs .concord .sdk .Constants ;
5254import  com .walmartlabs .concord .sdk .MapUtils ;
55+ import  org .slf4j .Logger ;
56+ import  org .slf4j .LoggerFactory ;
5357import  picocli .CommandLine .Command ;
5458import  picocli .CommandLine .Model .CommandSpec ;
5559import  picocli .CommandLine .Option ;
7175@ Command (name  = "run" , description  = "Run the current directory as a Concord process" )
7276public  class  Run  implements  Callable <Integer > {
7377
78+     private  static  final  Logger  log  = LoggerFactory .getLogger (Run .class );
7479    @ Spec 
7580    private  CommandSpec  spec ;
7681
7782    @ Option (names  = {"-h" , "--help" }, usageHelp  = true , description  = "display the command's help message" )
7883    boolean  helpRequested  = false ;
7984
85+     @ Option (names  = {"--context" }, description  = "Configuration context to use" )
86+     String  context  = "default" ;
87+ 
8088    @ Option (names  = {"-e" , "--extra-vars" }, description  = "additional process variables" )
8189    Map <String , Object > extraVars  = new  LinkedHashMap <>();
8290
@@ -93,13 +101,13 @@ public class Run implements Callable<Integer> {
93101    Path  repoCacheDir  = Paths .get (System .getProperty ("user.home" )).resolve (".concord" ).resolve ("repoCache" );
94102
95103    @ Option (names  = {"--secret-dir" }, description  = "secret store dir" )
96-     Path  secretStoreDir  =  Paths . get ( System . getProperty ( "user.home" )). resolve ( ".concord" ). resolve ( "secrets" ) ;
104+     Path  secretStoreDir ;
97105
98106    @ Option (names  = {"--vault-dir" }, description  = "vault dir" )
99-     Path  vaultDir  =  Paths . get ( System . getProperty ( "user.home" )). resolve ( ".concord" ). resolve ( "vaults" ) ;
107+     Path  vaultDir ;
100108
101109    @ Option (names  = {"--vault-id" }, description  = "vault id" )
102-     String  vaultId  =  "default" ;
110+     String  vaultId ;
103111
104112    @ Option (names  = {"--imports-source" }, description  = "default imports source" )
105113    String  importsSource  = "https://github.com" ;
@@ -139,6 +147,8 @@ public class Run implements Callable<Integer> {
139147    public  Integer  call () throws  Exception  {
140148        Verbosity  verbosity  = new  Verbosity (this .verbosity );
141149
150+         CliConfigContext  cliConfigContext  = loadCliConfig (verbosity , context );
151+ 
142152        sourceDir  = sourceDir .normalize ().toAbsolutePath ();
143153        Path  targetDir ;
144154
@@ -274,7 +284,7 @@ public Integer call() throws Exception {
274284                runnerCfg ,
275285                () -> cfg ,
276286                new  ProcessDependenciesModule (targetDir , runnerCfg .dependencies (), cfg .debug ()),
277-                 new  CliServicesModule (secretStoreDir , targetDir , defaultTaskVars ,  new   VaultProvider ( vaultDir ,  vaultId ) , dependencyManager , verbosity ))
287+                 new  CliServicesModule (cliConfigContext , targetDir , defaultTaskVars , dependencyManager , verbosity ))
278288                .create ();
279289
280290        // Just to notify listeners 
@@ -410,6 +420,64 @@ private DependencyManagerConfiguration getDependencyManagerConfiguration() {
410420        return  DependencyManagerConfiguration .of (depsCacheDir );
411421    }
412422
423+     private  CliConfigContext  loadCliConfig (Verbosity  verbosity , String  context ) {
424+         CliConfig .Overrides  overrides  = new  CliConfig .Overrides (secretStoreDir , vaultDir , vaultId );
425+ 
426+         Path  baseDir  = Paths .get (System .getProperty ("user.home" ), ".concord" );
427+         Path  cfgFile  = baseDir .resolve ("cli.yaml" );
428+         if  (!Files .exists (cfgFile )) {
429+             cfgFile  = baseDir .resolve ("cli.yml" );
430+         }
431+         if  (!Files .exists (cfgFile )) {
432+             CliConfig  cfg  = CliConfig .create ();
433+             return  assertCliConfigContext (cfg , context ).withOverrides (overrides );
434+         }
435+ 
436+         if  (verbosity .verbose ()) {
437+             log .info ("Using CLI configuration file: {} (\" {}\"  context)" , cfgFile , context );
438+         }
439+ 
440+         try  {
441+             CliConfig  cfg  = CliConfig .load (cfgFile );
442+             return  assertCliConfigContext (cfg , context ).withOverrides (overrides );
443+         } catch  (Exception  e ) {
444+             handleCliConfigErrorAndBail (cfgFile .toAbsolutePath ().toString (), e );
445+             return  null ;
446+         }
447+     }
448+ 
449+     private  static  void  handleCliConfigErrorAndBail (String  cfgPath , Throwable  e ) {
450+         // unwrap runtime exceptions 
451+         if  (e  instanceof  RuntimeException  ex ) {
452+             if  (ex .getCause () instanceof  IllegalArgumentException ) {
453+                 e  = ex .getCause ();
454+             }
455+         }
456+ 
457+         // handle YAML errors 
458+         if  (e  instanceof  IllegalArgumentException ) {
459+             if  (e .getCause () instanceof  UnrecognizedPropertyException  ex ) {
460+                 System .out .println (ansi ().fgRed ().a ("Invalid format of the CLI configuration file " ).a (cfgPath ).a (". " ).a (ex .getMessage ()));
461+                 System .exit (1 );
462+             }
463+             System .out .println (ansi ().fgRed ().a ("Invalid format of the CLI configuration file " ).a (cfgPath ).a (". " ).a (e .getMessage ()));
464+             System .exit (1 );
465+         }
466+ 
467+         // all other errors 
468+         System .out .println (ansi ().fgRed ().a ("Failed to read the CLI configuration file " ).a (cfgPath ).a (". " ).a (e .getMessage ()));
469+         System .exit (1 );
470+     }
471+ 
472+     private  static  CliConfigContext  assertCliConfigContext (CliConfig  config , String  context ) {
473+         CliConfigContext  result  = config .contexts ().get (context );
474+         if  (result  == null ) {
475+             System .out .println (ansi ().fgRed ().a ("Configuration context not found: " ).a (context ).a (". Check the CLI configuration file." ));
476+             System .exit (1 );
477+         }
478+         return  result ;
479+     }
480+ 
413481    private  static  void  dumpArguments (Map <String , Object > args ) {
414482        ObjectMapper  om  = new  ObjectMapper (new  YAMLFactory ().disable (YAMLGenerator .Feature .WRITE_DOC_START_MARKER ));
415483        try  {
@@ -446,7 +514,7 @@ public void visit(Path sourceFile, Path dstFile) {
446514            }
447515
448516            if  (currentCount  == notifyOnCount ) {
449-                 System .out .println (ansi ().fgBrightBlack ().a ("Copying files into the  target directory..." ));
517+                 System .out .println (ansi ().fgBrightBlack ().a ("Copying files into ./ target/  directory..." ));
450518                currentCount  = -1 ;
451519                return ;
452520            }
0 commit comments