diff --git a/coyote.yml b/coyote.yml index a3526dc..479cb93 100644 --- a/coyote.yml +++ b/coyote.yml @@ -1,17 +1,25 @@ - name: coyote title: Sample YML +- name: coyote + # global vars + vars: + TEST: echo + - name: test + # local vars (have precedence) + vars: + TEST: ls entries: - name: ls command command: | - ls + %TEST% -lR . - name: echo command command: echo hello - name: error unless you are root - command: ls /root + command: "%TEST% /root" - name: moar tests @@ -34,8 +42,8 @@ - name: test env vars entries: - - name: echo a var - command: bash -c 'echo "$TEST"' + - name: "%TEST% a var" + command: bash -c '%TEST% "$TEST"' env: [ TEST="hello world" ] - name: test text search diff --git a/main.go b/main.go index 640e726..48dda48 100644 --- a/main.go +++ b/main.go @@ -1,4 +1,4 @@ -// Copyright 2016-2017 Landoop LTD +// Copyright 2016-2018 Landoop LTD // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -49,10 +49,12 @@ var ( ) var ( - logger *log.Logger - uniqStrings = make(map[string]string) - uniqRegexp = regexp.MustCompile("%UNIQUE_[0-9A-Za-z_-]+%") - t *template.Template + logger *log.Logger + uniqStrings = make(map[string]string) + uniqRegexp = regexp.MustCompile("%UNIQUE_[0-9A-Za-z_-]+%") + t *template.Template + acceptableVarName = regexp.MustCompile("^[a-zA-Z0-9_]+$") + globalVars = make(map[string]string) ) type configFilesArrayFlag []string @@ -123,6 +125,23 @@ func main() { logger.Fatalf("Error reading configuration file: %v", err) } + // Search for Coyote Groups which Contain Global Configuration + for _, v := range entriesGroups { + // Reserved name coyote is used to set the title and global vars + if v.Name == "coyote" { + if v.Title != "" { + *title = v.Title + } + if len(v.Vars) != 0 { + var err error + globalVars, err = checkVarNames(v.Vars) + if err != nil { + log.Fatalln(err) + } + } + } + } + // For groups in configuration for _, v := range entriesGroups { var results []Result @@ -138,10 +157,16 @@ func main() { // Reserved name coyote is used to set the title (and maybe other global vars in the future). if v.Name == "coyote" { - *title = v.Title continue } + // Check for Local Variables + localVars, err := checkVarNames(v.Vars) + if err != nil { + log.Fatalln(err) + } + // Replace any variables in title + v.Title = replaceVars(v.Title, localVars, globalVars) // Skip test if asked if strings.ToLower(v.Skip) == "true" { logger.Printf("Skipping processing group: [ %s ]\n", v.Name) @@ -166,8 +191,12 @@ func main() { } // If unique strings are asked, replace the placeholders + // Also replace local and global vars v.Command = replaceUnique(v.Command) + v.Command = replaceVars(v.Command, localVars, globalVars) v.Stdin = replaceUnique(v.Stdin) + v.Stdin = replaceVars(v.Stdin, localVars, globalVars) + v.Name = replaceVars(v.Name, localVars, globalVars) var replaceInArrays = [][]string{ v.EnvVars, v.StderrExpect, @@ -177,7 +206,7 @@ func main() { } for _, v2 := range replaceInArrays { for k3, v3 := range v2 { - v2[k3] = replaceUnique(v3) + v2[k3] = replaceVars(replaceUnique(v3), localVars, globalVars) } } @@ -477,3 +506,37 @@ func assignMultiUseUniques(matches []string) { } } } + +// checkVarNames verifies that variable names are withing acceptable criteria +// and returns a new map where keys are enclosed within ampersands +// so we can check for %VARNAME% entries. +func checkVarNames(vars map[string]string) (map[string]string, error) { + r := make(map[string]string) + for k, v := range vars { + test := acceptableVarName.MatchString(k) + if test != true { + return r, errors.New("Variable name '" + k + "' contains illegal characters. Only alphanumerics and underscore are permitted for var names.") + } + test = uniqRegexp.MatchString("%" + k + "%") + if test == true { + return r, errors.New("Variable name '" + k + "' matches UNIQUE keyword for autogenerated values.") + } + if strings.Compare(k, "UNIQUE") == 0 { + return r, errors.New("Variable name 'UNIQUE' is reserved.") + } + r["%"+k+"%"] = v + } + return r, nil +} + +// replaceVars searches and replaces local and global variables in a string +// localVars have precedence over globalVars +func replaceVars(text string, localVars map[string]string, globalVars map[string]string) string { + for k, v := range localVars { + text = strings.Replace(text, k, v, -1) + } + for k, v := range globalVars { + text = strings.Replace(text, k, v, -1) + } + return text +} diff --git a/structs.go b/structs.go index b6db283..0e04278 100644 --- a/structs.go +++ b/structs.go @@ -1,4 +1,4 @@ -// Copyright 2016-2017 Landoop LTD +// Copyright 2016-2018 Landoop LTD // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -36,12 +36,13 @@ type Entry struct { } type EntryGroup struct { - Name string `yaml:"name"` - Entries []Entry `yaml:"entries"` - Title string `yaml:"title,omitempty"` - Skip string `yaml:"skip"` - NoSkip string `yaml:"noskip,omitempty"` - Type string `yaml:"type,omitempty"` + Name string `yaml:"name"` + Entries []Entry `yaml:"entries"` + Title string `yaml:"title,omitempty"` + Skip string `yaml:"skip"` + NoSkip string `yaml:"noskip,omitempty"` + Type string `yaml:"type,omitempty"` + Vars map[string]string `yaml:"vars,omitempty"` } type Result struct { diff --git a/version.go b/version.go index d040b29..bc260d7 100644 --- a/version.go +++ b/version.go @@ -2,7 +2,7 @@ package main // auto generated by github.com/andmarios/go-versiongen const ( - vgVersion = "v1.2" - vgHash = "bfa4cdb7609a3520381af4eda45a0366ea36c70a" - vgClean = true + vgVersion = "v1.3+" + vgHash = "4f1a1ea76788abd1232d0fac204a85ef32cce204" + vgClean = false )