github.com/swaros/contxt/module/runner@v0.0.0-20240305083542-3dbd4436ac40/functions.go (about)

     1  // Copyright (c) 2023 Thomas Ziegler <thomas.zglr@googlemail.com>. All rights reserved.
     2  //
     3  // # Licensed under the MIT License
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  package runner
    23  
    24  import (
    25  	"errors"
    26  	"fmt"
    27  	"os"
    28  	"runtime"
    29  	"sort"
    30  	"strconv"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/sirupsen/logrus"
    35  	"github.com/swaros/contxt/module/configure"
    36  	"github.com/swaros/contxt/module/ctxout"
    37  	"github.com/swaros/contxt/module/dirhandle"
    38  	"github.com/swaros/contxt/module/mimiclog"
    39  	"github.com/swaros/contxt/module/systools"
    40  	"github.com/swaros/contxt/module/tasks"
    41  	"github.com/swaros/contxt/module/yaclint"
    42  	"gopkg.in/yaml.v2"
    43  )
    44  
    45  type CmdExecutorImpl struct {
    46  	session *CmdSession
    47  }
    48  
    49  func NewCmd(session *CmdSession) *CmdExecutorImpl {
    50  	return &CmdExecutorImpl{
    51  		session: session,
    52  	}
    53  }
    54  
    55  func (c *CmdExecutorImpl) Combine4Print(msg ...interface{}) []interface{} {
    56  	var outInterfaces []interface{}
    57  	outInterfaces = append(outInterfaces, c.session.OutPutHdnl)
    58  	outInterfaces = append(outInterfaces, c.session.Printer)
    59  	outInterfaces = append(outInterfaces, msg...)
    60  	return outInterfaces
    61  }
    62  
    63  func (c *CmdExecutorImpl) MessageToString(msg ...interface{}) string {
    64  	msg = c.Combine4Print(msg...)
    65  	return ctxout.ToString(msg...)
    66  }
    67  
    68  func (c *CmdExecutorImpl) Print(msg ...interface{}) {
    69  	ctxout.Print(c.Combine4Print(msg...)...)
    70  }
    71  
    72  func (c *CmdExecutorImpl) Println(msg ...interface{}) {
    73  	ctxout.PrintLn(c.Combine4Print(msg...)...)
    74  }
    75  
    76  func (c *CmdExecutorImpl) doMagicParamOne(args string) {
    77  }
    78  
    79  func (c *CmdExecutorImpl) CallBackOldWs(oldws string) bool {
    80  	c.session.Log.Logger.Info("OLD workspace: ", oldws)
    81  	// get all paths first
    82  	configure.GetGlobalConfig().PathWorkerNoCd(func(_ string, path string) {
    83  
    84  		current := dirhandle.Pushd()
    85  		template, exists, _ := c.session.TemplateHndl.Load()
    86  		Fields := logrus.Fields{
    87  			"template: ": template,
    88  			"exists":     exists,
    89  			"path":       path,
    90  		}
    91  		c.session.Log.Logger.Debug("path parsing ", Fields)
    92  
    93  		if exists && template.Config.Autorun.Onleave != "" {
    94  			onleaveTarget := template.Config.Autorun.Onleave
    95  			Fields := logrus.Fields{
    96  				"target": onleaveTarget,
    97  			}
    98  			c.session.Log.Logger.Info("execute leave-action", Fields)
    99  			c.RunTargets(onleaveTarget, true)
   100  
   101  		}
   102  		current.Popd()
   103  
   104  	})
   105  	return true
   106  }
   107  
   108  func (c *CmdExecutorImpl) CallBackNewWs(newWs string) {
   109  	c.ResetVariables() // reset old variables while change the workspace. (req for shell mode)
   110  	c.session.Log.Logger.Info("NEW workspace: ", newWs)
   111  	configure.GetGlobalConfig().PathWorker(func(_ string, path string) { // iterate any path
   112  		template, exists, _ := c.session.TemplateHndl.Load()
   113  		Fields := logrus.Fields{
   114  			"template: ": template,
   115  			"exists":     exists,
   116  			"path":       path,
   117  		}
   118  		c.session.Log.Logger.Debug("path parsing", Fields)
   119  
   120  		// try to run onEnter func at any possible target in the workspace
   121  		if exists && template.Config.Autorun.Onenter != "" {
   122  			Fields := logrus.Fields{
   123  				"target": template.Config.Autorun.Onenter,
   124  			}
   125  			onEnterTarget := template.Config.Autorun.Onenter
   126  			c.session.Log.Logger.Info("execute enter-action", Fields)
   127  			c.RunTargets(onEnterTarget, true)
   128  		}
   129  
   130  	}, func(origin string) {
   131  		Fields := logrus.Fields{
   132  			"current-dir": origin,
   133  		}
   134  		c.session.Log.Logger.Debug("done calling autoruns on sub-dirs", Fields)
   135  	})
   136  }
   137  
   138  // set the default runtime variables depeding the predefined variables from
   139  // the main init, and the given variables depending the task and environment
   140  func (c *CmdExecutorImpl) SetStartupVariables(dataHndl *tasks.CombinedDh, template *configure.RunConfig) {
   141  	// first apply logger if poosible
   142  	mimiclog.ApplyLogger(c.session.Log.Logger, dataHndl)
   143  
   144  	c.session.Log.Logger.Debug("set startup variables")
   145  	// get the predifined variables from the MainInit function
   146  	// and set them to the datahandler
   147  	for k, v := range c.session.DefaultVariables {
   148  		dataHndl.SetPH(k, v)
   149  	}
   150  
   151  	currentDir, err := os.Getwd()
   152  	if err != nil {
   153  		c.session.Log.Logger.Error("error while getting current dir", err)
   154  	} else {
   155  		// we will override the current dir from the predefined ones, with the current dir
   156  		dataHndl.SetPH("CTX_PWD", currentDir)
   157  		dataHndl.SetPH("BASEPATH", currentDir)
   158  	}
   159  
   160  	// template depending variables
   161  	dataHndl.SetPH("CTX_PROJECT", template.Workspace.Project)
   162  	dataHndl.SetPH("CTX_ROLE", template.Workspace.Role)
   163  	dataHndl.SetPH("CTX_VERSION", template.Workspace.Version)
   164  
   165  	dataHndl.SetPH("CTX_WS", configure.GetGlobalConfig().UsedV2Config.CurrentSet)
   166  	keys := ""
   167  	configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) {
   168  		for _, ws2 := range cfg.Paths {
   169  			keys += setConfigVaribales(dataHndl, ws2, "WS") // there a space is added at the end already
   170  		}
   171  	})
   172  	c.session.Log.Logger.Debug("set startup variables for ws2", keys)
   173  	dataHndl.SetPH("CTX_WS_KEYS", keys)
   174  	// read the imports from the template and set them to the datahandler
   175  	c.handleImports(dataHndl, template)
   176  }
   177  
   178  // taking care about the imports.
   179  // these are the imports from the template, defined in config.imports and they are
   180  // used as variables map for the current run.
   181  // these imports will be set as map[string]string to the datahandler as long the are json or yaml files.
   182  // and can be used as placeholder in the tasks.
   183  // for example:
   184  //
   185  //	imports:
   186  //	  - imports.json
   187  //	  - imports.yaml
   188  //
   189  // this can be used in the tasks as ${imports.json:key1} or ${imports.yaml:key1}
   190  // but if an string is given, sperated by a space, the string will be used as key.
   191  // for example:
   192  //
   193  //	imports:
   194  //	  - imports.json jmap
   195  //	  - imports.yaml ymap
   196  //
   197  // this can be used in the tasks as ${jmap:key1} or ${ymap:key1}
   198  // any other file type will be loaded as string and assigned to the key with the whole content of the file.
   199  // for example:
   200  //
   201  //	imports:
   202  //	  - imports.txt
   203  //
   204  // this can be used in the tasks as ${imports.txt}
   205  // or by using the key by the import
   206  // for example:
   207  //
   208  //	imports:
   209  //	  - imports.txt txt
   210  //
   211  // this can be used in the tasks as ${txt}
   212  func (c *CmdExecutorImpl) handleImports(dataHndl *tasks.CombinedDh, template *configure.RunConfig) {
   213  	importHndlr := NewImportHandler(c.session.Log.Logger, dataHndl, c.session.TemplateHndl)
   214  	importHndlr.SetImports(template.Config.Imports)
   215  	if err := importHndlr.HandleImports(); err != nil {
   216  		c.Println(ctxout.ForeRed, "error while handling imports", ctxout.ForeYellow, err)
   217  		c.session.Log.Logger.Error("error while handling imports", err)
   218  		systools.Exit(systools.ErrorBySystem)
   219  	}
   220  }
   221  
   222  func setConfigVaribales(dataHndl *tasks.CombinedDh, wsInfo configure.WorkspaceInfoV2, varPrefix string) string {
   223  	pathStrInfo := ""
   224  	if wsInfo.Project != "" && wsInfo.Role != "" {
   225  		prefix := wsInfo.Project + "_" + wsInfo.Role
   226  		pathkey := varPrefix + "0_" + prefix
   227  		dataHndl.SetPH(pathkey, wsInfo.Path) // at least XXX0 without any version. this could be overwritten by other checkouts
   228  		pathStrInfo += pathkey + " "
   229  		if wsInfo.Version != "" {
   230  			// if version is set, we use them for avoid conflicts with different checkouts
   231  			if versionSan, err := systools.CheckForCleanString(wsInfo.Version); err == nil {
   232  				prefix += "_" + versionSan
   233  				// add it to ws1 as prefix for versionized keys
   234  				dataHndl.SetPH(varPrefix+"1_"+prefix, wsInfo.Path)
   235  			}
   236  		}
   237  	}
   238  	return pathStrInfo
   239  }
   240  
   241  // RunTargets run the given targets
   242  // force is used as flag for the first level targets, and is used
   243  // to runs shared targets once in front of the regular assigned targets
   244  func (c *CmdExecutorImpl) RunTargets(target string, force bool) error {
   245  	if template, exists, err := c.session.TemplateHndl.Load(); err != nil {
   246  		c.session.Log.Logger.Error("error while loading template", err)
   247  		return err
   248  	} else if !exists {
   249  		c.session.Log.Logger.Error("template not exists")
   250  		return errors.New("no contxt template found in current directory")
   251  	} else {
   252  
   253  		datahndl := tasks.NewCombinedDataHandler()
   254  		c.SetStartupVariables(datahndl, &template)
   255  		datahndl.SetPH("CTX_TARGET", target)
   256  		datahndl.SetPH("CTX_FORCE", strconv.FormatBool(force))
   257  
   258  		requireHndl := tasks.NewDefaultRequires(datahndl, c.session.Log.Logger)
   259  		executer := tasks.NewTaskListExec(
   260  			template,
   261  			datahndl,
   262  			requireHndl,
   263  			c.getOutHandler(),
   264  			tasks.ShellCmd,
   265  		)
   266  		executer.SetLogger(c.session.Log.Logger)
   267  		code := executer.RunTarget(target, force)
   268  		switch code {
   269  		case systools.ExitByNoTargetExists:
   270  			c.session.Log.Logger.Error("target not exists:", target)
   271  			return errors.New("target " + target + " not exists")
   272  		case systools.ExitAlreadyRunning:
   273  			c.session.Log.Logger.Info("target already running")
   274  			return nil
   275  		case systools.ExitCmdError:
   276  			c.session.Log.Logger.Error("error while running target ", target)
   277  			return errors.New("error while running target:" + target)
   278  		case systools.ExitByNothingToDo:
   279  			c.session.Log.Logger.Info("nothing to do ", target)
   280  			return nil
   281  		case systools.ExitOk:
   282  			c.session.Log.Logger.Info("target executed successfully")
   283  			return nil
   284  		default:
   285  			c.session.Log.Logger.Error("unexpected exit code:", code)
   286  			return errors.New("unexpected exit code:" + fmt.Sprintf("%d", code))
   287  		}
   288  	}
   289  
   290  }
   291  
   292  func (c *CmdExecutorImpl) GetTargets(incInvisible bool) []string {
   293  	if template, exists, err := c.session.TemplateHndl.Load(); err != nil {
   294  		c.session.Log.Logger.Error("error while loading template", err)
   295  	} else if !exists {
   296  		c.session.Log.Logger.Error("template not exists", err)
   297  	} else {
   298  		if res, have := TemplateTargetsAsMap(template, incInvisible); have {
   299  			return res
   300  		}
   301  	}
   302  	return nil
   303  }
   304  
   305  func (c *CmdExecutorImpl) ResetVariables() {
   306  }
   307  
   308  func (c *CmdExecutorImpl) MainInit() {
   309  	c.initDefaultVariables()
   310  }
   311  
   312  // initDefaultVariables init the default variables for the current session.
   313  // these are the varibales they should not change during the session.
   314  func (c *CmdExecutorImpl) initDefaultVariables() {
   315  	if currentPath, err := os.Getwd(); err != nil {
   316  		ctxout.CtxOut("Error while reading current directory", err)
   317  		systools.Exit(systools.ErrorBySystem)
   318  	} else {
   319  		c.setVariable("CTX_PWD", currentPath)
   320  		c.setVariable("CTX_PATH", currentPath)
   321  		c.setVariable("BASEPATH", currentPath)
   322  	}
   323  	c.setVariable("CTX_OS", runtime.GOOS)
   324  	c.setVariable("CTX_ARCH", runtime.GOARCH)
   325  	c.setVariable("CTX_USER", os.Getenv("USER"))
   326  	c.setVariable("CTX_HOST", getHostname())
   327  	c.setVariable("CTX_HOME", os.Getenv("HOME"))
   328  	c.setVariable("CTX_DATE", time.Now().Format("2006-01-02"))
   329  	c.setVariable("CTX_TIME", time.Now().Format("15:04:05"))
   330  	c.setVariable("CTX_DATETIME", time.Now().Format("2006-01-02 15:04:05"))
   331  
   332  	c.setVariable("CTX_VERSION", configure.GetVersion())
   333  	c.setVariable("CTX_BUILD_NO", configure.GetBuild())
   334  
   335  	c.handleWindowsInit() // it self is testing if we are on windows
   336  }
   337  
   338  func getHostname() string {
   339  	if hostname, err := os.Hostname(); err != nil {
   340  		return ""
   341  	} else {
   342  		return hostname
   343  	}
   344  }
   345  
   346  func (c *CmdExecutorImpl) handleWindowsInit() {
   347  	if runtime.GOOS == "windows" {
   348  		// we need to set the console to utf8, to be able to print utf8 chars
   349  		// in the console
   350  		if os.Getenv("CTX_COLOR") == "ON" { // then lets see if this should forced for beeing enabled by env-var
   351  			c.SetColor(true)
   352  		} else {
   353  			// if not forced already we try to figure out, by oure own, if the powershell is able to support ANSII
   354  			// this is since version 7 the case
   355  			pwrShellRunner := tasks.GetShellRunnerForOs("windows")
   356  			version, _ := pwrShellRunner.ExecSilentAndReturnLast(PWRSHELL_CMD_VERSION)
   357  			c.setVariable("CTX_PS_VERSION", version) // also setup varibale to have the PS version in place
   358  			if version >= "7" {
   359  				c.SetColor(true)
   360  			}
   361  		}
   362  	}
   363  }
   364  
   365  // updates the given variable in the current session.
   366  // this is just for keeping the variable in the session. but this
   367  // is not used as variables for the template.
   368  // this is just ment, to define already variables while setting up
   369  // the session and keep them in the session, until they get used by the template later.
   370  // see RunTargets for the usage of the variables.
   371  func (c *CmdExecutorImpl) setVariable(name string, value string) {
   372  	c.session.DefaultVariables[name] = value
   373  }
   374  
   375  func (c *CmdExecutorImpl) GetVariable(name string) string {
   376  	if val, have := c.session.DefaultVariables[name]; have {
   377  		return val
   378  	}
   379  	return ""
   380  }
   381  
   382  func (c *CmdExecutorImpl) GetVariables() map[string]string {
   383  	return c.session.DefaultVariables
   384  }
   385  
   386  func (c *CmdExecutorImpl) SetColor(onoff bool) {
   387  	behave := ctxout.GetBehavior()
   388  	behave.NoColored = onoff
   389  	ctxout.SetBehavior(behave)
   390  }
   391  
   392  func (c *CmdExecutorImpl) GetOuputHandler() (ctxout.StreamInterface, ctxout.PrintInterface) {
   393  	return c.session.OutPutHdnl, c.session.Printer
   394  }
   395  
   396  func (c *CmdExecutorImpl) GetWorkspaces() []string {
   397  	ws := configure.GetGlobalConfig().ListWorkSpaces()
   398  	sort.Strings(ws)
   399  	return ws
   400  }
   401  
   402  func (c *CmdExecutorImpl) FindWorkspaceInfoByTemplate(updateFn func(workspace string, cnt int, update bool, info configure.WorkspaceInfoV2)) (allCount int, updatedCount int) {
   403  	wsCount := 0
   404  	wsUpdated := 0
   405  	c.session.Log.Logger.Info("Start to find workspace info by template")
   406  
   407  	if currentPath, err := os.Getwd(); err != nil {
   408  		ctxout.CtxOut("Error while reading current directory", err)
   409  		systools.Exit(systools.ErrorBySystem)
   410  	} else {
   411  		haveUpdate := false
   412  		configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) {
   413  			wsCount++
   414  			for pathIndex, savedWorkspace := range cfg.Paths {
   415  				logFields := mimiclog.Fields{"path": savedWorkspace.Path, "project": savedWorkspace.Project, "role": savedWorkspace.Role}
   416  				c.session.Log.Logger.Debug("parsing workspace", logFields)
   417  				if err := os.Chdir(savedWorkspace.Path); err == nil && savedWorkspace.Project == "" && savedWorkspace.Role == "" {
   418  					template, found, err := c.session.TemplateHndl.Load()
   419  					if found && err == nil {
   420  						if template.Workspace.Project != "" && template.Workspace.Role != "" {
   421  							savedWorkspace.Project = template.Workspace.Project
   422  							savedWorkspace.Role = template.Workspace.Role
   423  							if template.Workspace.Version != "" {
   424  								savedWorkspace.Version = template.Workspace.Version
   425  							}
   426  							cfg.Paths[pathIndex] = savedWorkspace
   427  							logFields := mimiclog.Fields{"path": savedWorkspace.Path, "project": savedWorkspace.Project, "role": savedWorkspace.Role}
   428  							c.session.Log.Logger.Info("found template for workspace", logFields)
   429  							configure.GetGlobalConfig().UpdateCurrentConfig(cfg)
   430  							haveUpdate = true
   431  							wsUpdated++
   432  							if updateFn != nil {
   433  								c.session.Log.Logger.Debug("exeute update function")
   434  								updateFn(index, wsCount, true, savedWorkspace)
   435  							}
   436  						}
   437  					} else {
   438  						if updateFn != nil {
   439  							updateFn(index, wsCount, false, savedWorkspace)
   440  						}
   441  					}
   442  				}
   443  			}
   444  
   445  		})
   446  		if haveUpdate {
   447  			c.session.Log.Logger.Info("Update configuration")
   448  			if err := configure.GetGlobalConfig().SaveConfiguration(); err != nil {
   449  				c.session.Log.Logger.Error("Error while saving configuration", err)
   450  				ctxout.CtxOut("Error while saving configuration", err)
   451  				systools.Exit(systools.ErrorBySystem)
   452  			}
   453  		}
   454  		os.Chdir(currentPath)
   455  	}
   456  	ctxout.PrintLn("")
   457  	return wsCount, wsUpdated
   458  }
   459  
   460  func (c *CmdExecutorImpl) SetLogLevel(level string) error {
   461  	if level != "" {
   462  		lvl, err := logrus.ParseLevel(level)
   463  		if err != nil {
   464  			return err
   465  		}
   466  		c.session.Log.Logger.SetLevel(lvl)
   467  
   468  	}
   469  	return nil
   470  }
   471  
   472  func (c *CmdExecutorImpl) GetLogger() mimiclog.Logger {
   473  	return c.session.Log.Logger
   474  }
   475  
   476  func (c *CmdExecutorImpl) PrintPaths(plain bool, showFulltask bool) {
   477  	dir, err := os.Getwd()
   478  	logFields := mimiclog.Fields{"dir": dir, "err": err}
   479  	c.session.Log.Logger.Debug("print paths in workspace", logFields)
   480  
   481  	if err == nil {
   482  		if !plain {
   483  			c.Println(ctxout.ForeWhite, " current directory: ", ctxout.BoldTag, dir, ctxout.CleanTag)
   484  			c.Println(ctxout.ForeWhite, " current workspace: ", ctxout.BoldTag, configure.GetGlobalConfig().UsedV2Config.CurrentSet, ctxout.CleanTag)
   485  		}
   486  		pathColor := ctxout.ForeLightBlue
   487  		if !configure.GetGlobalConfig().PathMeightPartOfWs(dir) {
   488  			pathColor = ctxout.ForeLightMagenta
   489  		}
   490  		if !plain {
   491  			c.Println(" contains paths:")
   492  		}
   493  		//ctxout.Print(c.session.OutPutHdnl, "<table>")
   494  		walkErr := configure.GetGlobalConfig().PathWorker(func(index string, path string) {
   495  			template, exists, err := c.session.TemplateHndl.Load()
   496  			if err == nil {
   497  				add := ctxout.Dim + ctxout.ForeLightGrey
   498  				taskDrawMode := "ignore"
   499  				if showFulltask {
   500  					taskDrawMode = "wordwrap"
   501  				}
   502  				indexColor := ctxout.ForeLightBlue
   503  				indexStr := index
   504  				if path == configure.GetGlobalConfig().GetActivePath("") {
   505  					indexColor = ctxout.ForeLightCyan
   506  					indexStr = "> " + index
   507  					add = ctxout.ResetDim + ctxout.ForeLightGrey
   508  				}
   509  
   510  				if strings.Contains(dir, path) {
   511  					add = ctxout.ResetDim + ctxout.ForeCyan
   512  				}
   513  				if dir == path {
   514  					add = ctxout.ResetDim + ctxout.ForeGreen
   515  				}
   516  				outTasks := ""
   517  				if exists {
   518  					targets, _ := TemplateTargetsAsMap(template, true)
   519  					outTasks = strings.Join(targets, " ")
   520  				} else {
   521  					outTasks = ctxout.ForeDarkGrey + "no tasks"
   522  				}
   523  				c.Print(
   524  					"<row>",
   525  					indexColor,
   526  					"<tab size='5' fill=' ' draw='fixed' origin='2'>",
   527  					indexStr+" ",
   528  					"</tab>",
   529  					add,
   530  					"<tab size='65' draw='content' fill=' ' cut-add='///..' origin='1'>",
   531  					path, " ",
   532  					"</tab>",
   533  					ctxout.CleanTag,
   534  					"<tab size='29' fill=' ' prefix='<f:yellow>' suffix='</>'  overflow='"+taskDrawMode+"' draw='extend' cut-add='<f:light-blue> ..<f:yellow>.' origin='2'>",
   535  					outTasks,
   536  					"</tab>",
   537  					"</row>",
   538  				)
   539  
   540  			} else {
   541  				c.Print(ctxout.Message("       path: ", ctxout.Dim, " no ", ctxout.ForeYellow, index, " ", pathColor, path, ctxout.ForeRed, " error while loading template: ", err.Error()))
   542  			}
   543  		}, func(origin string) {})
   544  
   545  		if walkErr != nil {
   546  			c.session.Log.Logger.Error("Error while walking through paths", err)
   547  			c.Println(ctxout.ForeRed, "Error while walking through paths: ", ctxout.CleanTag, walkErr.Error(), ctxout.CleanTag)
   548  		}
   549  		//c.Println("")
   550  	}
   551  }
   552  
   553  func (c *CmdExecutorImpl) GetCurrentWorkSpace() string {
   554  	return configure.GetGlobalConfig().UsedV2Config.CurrentSet
   555  }
   556  
   557  func (c *CmdExecutorImpl) PrintWorkspaces() {
   558  	configure.GetGlobalConfig().ExecOnWorkSpaces(func(index string, cfg configure.ConfigurationV2) {
   559  		if index == configure.GetGlobalConfig().UsedV2Config.CurrentSet {
   560  			c.Println("\t[ ", ctxout.BoldTag, index, ctxout.CleanTag, " ]")
   561  		} else {
   562  			c.Println("\t  ", ctxout.ForeDarkGrey, index, ctxout.CleanTag)
   563  		}
   564  	})
   565  }
   566  
   567  func TemplateTargetsAsMap(template configure.RunConfig, showInvTarget bool) ([]string, bool) {
   568  	var targets []string
   569  	found := false
   570  
   571  	if len(template.Task) > 0 {
   572  		for _, tasks := range template.Task {
   573  			if !systools.SliceContains(targets, tasks.ID) && (!tasks.Options.Invisible || showInvTarget) {
   574  				found = true
   575  				targets = append(targets, strings.TrimSpace(tasks.ID))
   576  			}
   577  		}
   578  	}
   579  	sort.Strings(targets)
   580  	return targets, found
   581  }
   582  
   583  func (c *CmdExecutorImpl) Lint(showAll bool) error {
   584  	c.Println("linting...")
   585  	c.session.TemplateHndl.SetLinting(true)
   586  	if _, exists, err := c.session.TemplateHndl.Load(); err != nil {
   587  		c.Println(ctxout.ForeRed, "linting failed: ", ctxout.CleanTag, err.Error())
   588  		return err
   589  	} else {
   590  		if exists {
   591  			c.Println("...loading config ", ctxout.ForeGreen, "ok", ctxout.CleanTag)
   592  			linter, lErr := c.session.TemplateHndl.GetLinter()
   593  			if lErr != nil {
   594  				return lErr
   595  			}
   596  			if linter.HasWarning() {
   597  				if showAll {
   598  					c.Println(" ")
   599  					c.Println("  you see all the unset fields, which are not set in the config")
   600  					c.Println("  these are shown as", ctxout.ForeDarkGrey, " MissingEntry: level[5]", ctxout.CleanTag)
   601  					c.Println("  this do not mean, that you have to set them, but it is a hint, that you can set them")
   602  					c.Println("  and how they are named")
   603  					c.Println(" ")
   604  					c.Println(" ")
   605  					// we just print all warnings once per keypath
   606  					alreadyPrinted := make(map[string]bool)
   607  
   608  					linter.GetIssue(yaclint.IssueLevelWarn, func(token *yaclint.MatchToken) {
   609  						//c.Println(ctxout.ForeYellow, "linting warning: ", ctxout.CleanTag, token.ToIssueString())
   610  						propColor := ctxout.ForeYellow
   611  						if token.Added {
   612  							propColor = ctxout.ForeGreen
   613  						}
   614  
   615  						canPrint := true
   616  						if _, ok := alreadyPrinted[token.KeyPath]; ok {
   617  							canPrint = false
   618  						}
   619  						if canPrint {
   620  							valueStr := fmt.Sprintf(" %v ", token.Value)
   621  							c.Println(
   622  								ctxout.Row(
   623  									ctxout.TD(token.KeyPath, ctxout.Size(20), ctxout.Prop(ctxout.AttrPrefix, propColor)),
   624  									ctxout.TD(valueStr, ctxout.Size(20), ctxout.Prop(ctxout.AttrPrefix, ctxout.ForeYellow)),
   625  									ctxout.TD(token.ToIssueString(), ctxout.Size(40), ctxout.Prop(ctxout.AttrPrefix, ctxout.ForeDarkGrey)),
   626  									ctxout.TD(token.Type, ctxout.Size(20), ctxout.Prop(ctxout.AttrPrefix, ctxout.ForeLightCyan)),
   627  								),
   628  							)
   629  							alreadyPrinted[token.KeyPath] = true
   630  						}
   631  
   632  					})
   633  				} else {
   634  					if linter.HasError() {
   635  						c.Println(ctxout.ForeRed, "...linting errors: ", ctxout.CleanTag, len(linter.Errors()))
   636  						c.Println(" ")
   637  						linter.GetIssue(yaclint.IssueLevelError, func(token *yaclint.MatchToken) {
   638  							c.Println(
   639  								ctxout.ForeRed,
   640  								"linting error: ", ctxout.ForeYellow, token.ToIssueString(),
   641  								ctxout.CleanTag, " check entry: ",
   642  								ctxout.ForeBlue, token.KeyPath, ctxout.CleanTag, ":", ctxout.ForeLightBlue, token.Value, ctxout.CleanTag)
   643  						})
   644  						c.Println(" ")
   645  					} else {
   646  						c.Println(ctxout.ForeLightGreen, "...linter findings : ", ctxout.CleanTag, len(linter.Warnings()))
   647  						c.Println(" ")
   648  						c.Println(ctxout.ForeLightBlue,
   649  							"linter findings are usual and expected, because there are fields not set, the ",
   650  							ctxout.BoldTag, "could", ctxout.CleanTag, ctxout.ForeLightBlue, " be set, but it is not necessary.")
   651  						c.Println(ctxout.ForeLightBlue, "if you like to see all of this findings, use the flag show-issues")
   652  						c.Println(" ")
   653  					}
   654  				}
   655  			}
   656  		} else {
   657  			c.Println(ctxout.ForeRed, "linting failed: ", ctxout.CleanTag, "no template found")
   658  			return errors.New("no template found")
   659  		}
   660  	}
   661  	return nil
   662  
   663  }
   664  
   665  func (c *CmdExecutorImpl) InteractiveScreen() {
   666  
   667  	if !systools.IsStdOutTerminal() {
   668  		c.Print("no terminal detected")
   669  		systools.Exit(systools.ErrorInitApp)
   670  		return
   671  	}
   672  	shellRunner(c).runAsShell()
   673  }
   674  func (c *CmdExecutorImpl) ShellWithComands(cmds []string, timeout int) {
   675  	if err := shellRunner(c).runWithCmds(cmds, timeout); err != nil {
   676  		c.Println(ctxout.ForeRed, "error while running shell", ctxout.CleanTag, err.Error())
   677  	}
   678  }
   679  
   680  // PrintShared print all shared paths in a simple list
   681  func (c *CmdExecutorImpl) PrintShared() {
   682  	sharedRun := NewSharedHelper()
   683  	sharedDirs, _ := sharedRun.ListUseCases(false)
   684  	for _, sharedPath := range sharedDirs {
   685  		c.Println(sharedPath)
   686  	}
   687  }
   688  
   689  // displays the current version of contxt template as a yaml string
   690  func (c *CmdExecutorImpl) PrintTemplate() {
   691  	if template, exists, err := c.session.TemplateHndl.Load(); err != nil {
   692  		c.Println(ctxout.ForeRed, "yaml export failed: ", ctxout.CleanTag, err.Error())
   693  		c.Print(ctxout.ForeRed, "error while loading template: ", ctxout.CleanTag, err.Error())
   694  	} else {
   695  		if exists {
   696  			c.Println("...loading config ", ctxout.ForeGreen, "ok", ctxout.CleanTag)
   697  			// map the template to a yaml string
   698  			if yamlStr, err := yaml.Marshal(template); err != nil {
   699  				c.Println(ctxout.ForeRed, "yaml export failed: ", ctxout.CleanTag, err.Error())
   700  			} else {
   701  				c.Println(string(yamlStr))
   702  			}
   703  		} else {
   704  			c.Println(ctxout.ForeRed, "yaml export failed: ", ctxout.CleanTag, "no template found")
   705  		}
   706  	}
   707  }
   708  
   709  // Set the given variable to the current session Default Variables.
   710  // this will end up by using them as variables for the template, and are reset for any run.
   711  // this is also an different Behavior to V1 where the variables are set for the wohle runtime, and if
   712  // they changed by a task, they are changed for the whole runtime.
   713  // this is not happen anymore, and the variables are just set for the current run.
   714  func (c *CmdExecutorImpl) SetPreValue(name string, value string) {
   715  	c.session.DefaultVariables[name] = value
   716  }