-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement process wrapper & add executor tests (#27)
* Setup executor tests & update parser tests * Add process implementation and mock * Add executor tests to cover watching * Add short timeout for ci * Refactor Parser (#28) * Parser interface, dependencies udpated * Get options moved to internal * Internal/util.go refactor and tests Co-authored-by: Dugi <[email protected]> * Remove useless tests Co-authored-by: Sam <[email protected]>
- Loading branch information
Showing
16 changed files
with
511 additions
and
240 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ on: | |
|
||
jobs: | ||
ci: | ||
timeout-minutes: 2 | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var osCommandRegexp = regexp.MustCompile(`\$\((.+)\)`) | ||
var osEnvRegexp = regexp.MustCompile(`\$\{(.+)\}`) | ||
|
||
// Parses the interpolated system commands, ie. "Hello $(echo 'World')" and returns it. | ||
// Returns the command wrapper in $() and without the wrapper. | ||
func parseSystemCmd(re *regexp.Regexp, str string) (string, string) { | ||
match := re.FindAllStringSubmatch(str, -1) | ||
|
||
if len(match) > 0 && len(match[0]) > 0 { | ||
return match[0][0], match[0][1] | ||
} | ||
|
||
return "", "" | ||
} | ||
|
||
// prase system commands and store results to env | ||
func SetEnvVariables(vars map[string]string) (map[string]string, error) { | ||
retVars := make(map[string]string) | ||
for k, v := range vars { | ||
_, cmd := parseSystemCmd(osCommandRegexp, v) | ||
|
||
if cmd == "" { | ||
retVars[k] = v | ||
_ = os.Setenv(k, v) | ||
continue | ||
} | ||
|
||
splitCmd, err := ParseCommandLine(os.ExpandEnv(cmd)) | ||
if err != nil { | ||
return retVars, err | ||
} | ||
|
||
out, err := exec.Command(splitCmd[0], splitCmd[1:]...).Output() | ||
if err != nil { | ||
return retVars, err | ||
} | ||
|
||
outStr := strings.TrimSpace(string(out)) | ||
retVars[k] = outStr | ||
_ = os.Setenv(k, outStr) | ||
} | ||
|
||
return retVars, nil | ||
} | ||
|
||
// Parses the command string into an array of [command, args, args]... | ||
func ParseCommandLine(command string) ([]string, error) { | ||
var args []string | ||
state := "start" | ||
current := "" | ||
quote := "\"" | ||
escapeNext := true | ||
|
||
for i := 0; i < len(command); i++ { | ||
c := command[i] | ||
|
||
if state == "quotes" { | ||
if string(c) != quote { | ||
current += string(c) | ||
} else { | ||
args = append(args, current) | ||
current = "" | ||
state = "start" | ||
} | ||
continue | ||
} | ||
|
||
if escapeNext { | ||
current += string(c) | ||
escapeNext = false | ||
continue | ||
} | ||
|
||
if c == '\\' { | ||
escapeNext = true | ||
continue | ||
} | ||
|
||
if c == '"' || c == '\'' { | ||
state = "quotes" | ||
quote = string(c) | ||
continue | ||
} | ||
|
||
if state == "arg" { | ||
if c == ' ' || c == '\t' { | ||
args = append(args, current) | ||
current = "" | ||
state = "start" | ||
} else { | ||
current += string(c) | ||
} | ||
continue | ||
} | ||
|
||
if c != ' ' && c != '\t' { | ||
state = "arg" | ||
current += string(c) | ||
} | ||
} | ||
|
||
if state == "quotes" { | ||
return []string{}, fmt.Errorf("unclosed quote in command: %s", command) | ||
} | ||
|
||
if current != "" { | ||
args = append(args, current) | ||
} | ||
|
||
return args, nil | ||
} | ||
|
||
// Replace the placeholders with actual environment variable values in string pointer. | ||
// Given that a string pointer must be provided, the replacement happens in place. | ||
func ReplaceEnvironmentVariables(re *regexp.Regexp, str *string) { | ||
resolved := *str | ||
raw, env := parseSystemCmd(re, resolved) | ||
|
||
if raw != "" && env != "" { | ||
*str = strings.Replace(resolved, raw, os.Getenv(env), -1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package cli | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestParseSystemCmd(t *testing.T) { | ||
|
||
cmds := []string{ | ||
"$(echo 'Hello Thor')", | ||
"hello world", | ||
} | ||
|
||
want := [][]string{ | ||
{"$(echo 'Hello Thor')", "echo 'Hello Thor'"}, | ||
{"", ""}, | ||
} | ||
|
||
for i, cmd := range cmds { | ||
got0, got1 := parseSystemCmd(osCommandRegexp, cmd) | ||
assert.Equal(t, want[i][0], got0, "expected "+want[i][0]+", got ", got0) | ||
assert.Equal(t, want[i][1], got1, "expected "+want[i][1]+", got ", got1) | ||
} | ||
|
||
} | ||
|
||
func TestSetEnvVariables(t *testing.T) { | ||
|
||
values := map[string]string{ | ||
"THOR": "Lord of thunder", | ||
"THOR_CMD": "$(echo 'Hello Thor')", | ||
} | ||
|
||
want := map[string]string{ | ||
"THOR": "Lord of thunder", | ||
"THOR_CMD": "Hello Thor", | ||
} | ||
|
||
got, _ := SetEnvVariables(values) | ||
assert.Equal(t, want["THOR"], os.Getenv("THOR")) | ||
assert.Equal(t, want["THOR_CMD"], os.Getenv("THOR_CMD")) | ||
|
||
for k := range got { | ||
assert.Equal(t, want[k], got[k]) | ||
} | ||
} | ||
|
||
func TestParseCommandLine(t *testing.T) { | ||
t.Skip() | ||
} | ||
|
||
func TestReplaceEnvironmentVariables(t *testing.T) { | ||
values := map[string]string{ | ||
"THOR": "Lord of thunder", | ||
"LOKI": "Lord of deception", | ||
} | ||
|
||
for k, v := range values { | ||
t.Setenv(k, v) | ||
} | ||
|
||
str := "I am ${THOR}" | ||
want := "I am Lord of thunder" | ||
|
||
ReplaceEnvironmentVariables(osEnvRegexp, &str) | ||
|
||
assert.Equal(t, want, str, "wrong env value is injected") | ||
} |
Oops, something went wrong.