diff --git a/Gopkg.lock b/Gopkg.lock index fa6fa5fa..42d0658e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,11 +3,11 @@ [[projects]] branch = "master" - digest = "1:147fe67eb5dcb4f1a182992bf2a9b00ca4de3f4b8193bda0c89a9fdbb9db6ad6" + digest = "1:f3960e064201714a3507bf96183be246b3d941568af01dc5cff2a388ac4c7515" name = "github.com/Masterminds/semver" packages = ["."] pruneopts = "NUT" - revision = "3c560837130448941620d7694991d3ec440aefc0" + revision = "4ca3c04fd4fe2a472df0d7121b1b9462f2214c43" [[projects]] branch = "master" @@ -50,12 +50,12 @@ version = "v1.0.0" [[projects]] - branch = "master" digest = "1:65300ccc4bcb38b107b868155c303312978981e56bca707c81efec57575b5e06" name = "github.com/imdario/mergo" packages = ["."] pruneopts = "NUT" revision = "9316a62528ac99aaecb4e47eadd6dc8aa6533d58" + version = "v0.3.5" [[projects]] branch = "master" @@ -118,7 +118,7 @@ "ssh/terminal", ] pruneopts = "NUT" - revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602" + revision = "a2144134853fc9a27a7b1e3eb4f19f1a76df13c9" [[projects]] branch = "master" @@ -126,7 +126,7 @@ name = "golang.org/x/net" packages = ["context"] pruneopts = "NUT" - revision = "d0887baf81f4598189d4e12a37c6da86f0bba4d0" + revision = "a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1" [[projects]] branch = "master" @@ -157,14 +157,15 @@ [[projects]] branch = "master" - digest = "1:36dbf10f4bf8c7568b5d4a5fe1afb57cd8e0451a6566b28f5bc87fc01f65339d" + digest = "1:01be9a02fb8bcae03383a7422d0bd48813fd49834a50be5ef933e430604b3423" name = "mvdan.cc/sh" packages = [ "interp", + "shell", "syntax", ] pruneopts = "NUT" - revision = "76fa0d67a25bb78ecf63fa717191661589dcdadd" + revision = "54e5852f101469e5ff9b03902ce0d4ff2ef09809" [solve-meta] analyzer-name = "dep" @@ -172,7 +173,6 @@ input-imports = [ "github.com/Masterminds/semver", "github.com/Masterminds/sprig", - "github.com/imdario/mergo", "github.com/mattn/go-zglob", "github.com/mitchellh/go-homedir", "github.com/radovskyb/watcher", @@ -181,6 +181,7 @@ "golang.org/x/sync/errgroup", "gopkg.in/yaml.v2", "mvdan.cc/sh/interp", + "mvdan.cc/sh/shell", "mvdan.cc/sh/syntax", ] solver-name = "gps-cdcl" diff --git a/Gopkg.toml b/Gopkg.toml index 19c4f9c1..ead17dec 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -7,10 +7,6 @@ branch = "master" name = "github.com/Masterminds/sprig" -[[constraint]] - branch = "master" - name = "github.com/imdario/mergo" - [[constraint]] branch = "master" name = "github.com/mattn/go-zglob" diff --git a/vendor/github.com/Masterminds/semver/doc.go b/vendor/github.com/Masterminds/semver/doc.go index e00f65eb..6a6c24c6 100644 --- a/vendor/github.com/Masterminds/semver/doc.go +++ b/vendor/github.com/Masterminds/semver/doc.go @@ -47,7 +47,7 @@ parts of the package. // Handle constraint not being parseable. } - v, _ := semver.NewVersion("1.3") + v, err := semver.NewVersion("1.3") if err != nil { // Handle version not being parseable. } diff --git a/vendor/github.com/Masterminds/semver/version.go b/vendor/github.com/Masterminds/semver/version.go index 9d22ea63..ab3a368e 100644 --- a/vendor/github.com/Masterminds/semver/version.go +++ b/vendor/github.com/Masterminds/semver/version.go @@ -106,7 +106,7 @@ func MustParse(v string) *Version { // Note, if the original version contained a leading v this version will not. // See the Original() method to retrieve the original value. Semantic Versions // don't contain a leading v per the spec. Instead it's optional on -// impelementation. +// implementation. func (v *Version) String() string { var buf bytes.Buffer diff --git a/vendor/mvdan.cc/sh/interp/builtin.go b/vendor/mvdan.cc/sh/interp/builtin.go index 09ede3b3..8335fa65 100644 --- a/vendor/mvdan.cc/sh/interp/builtin.go +++ b/vendor/mvdan.cc/sh/interp/builtin.go @@ -290,7 +290,7 @@ func (r *Runner) builtinCode(pos syntax.Pos, name string, args []string) int { if parseErr { return 2 } - return oneIf(r.bashTest(expr) == "") + return oneIf(r.bashTest(expr, true) == "") case "exec": // TODO: Consider syscall.Exec, i.e. actually replacing // the process. It's in theory what a shell should do, diff --git a/vendor/mvdan.cc/sh/interp/doc.go b/vendor/mvdan.cc/sh/interp/doc.go index a9a5723d..320a9106 100644 --- a/vendor/mvdan.cc/sh/interp/doc.go +++ b/vendor/mvdan.cc/sh/interp/doc.go @@ -7,4 +7,4 @@ // // This package is a work in progress and EXPERIMENTAL; its API is not // subject to the 1.x backwards compatibility guarantee. -package interp // import "mvdan.cc/sh/interp" +package interp diff --git a/vendor/mvdan.cc/sh/interp/expand.go b/vendor/mvdan.cc/sh/interp/expand.go index 017b5311..e1d50fe2 100644 --- a/vendor/mvdan.cc/sh/interp/expand.go +++ b/vendor/mvdan.cc/sh/interp/expand.go @@ -247,7 +247,7 @@ func (r *Runner) wordField(wps []syntax.WordPart, ql quoteLevel) []fieldPart { case '\n': // remove \\\n i++ continue - case '\\', '$', '`': // special chars + case '"', '\\', '$', '`': // special chars continue } } diff --git a/vendor/mvdan.cc/sh/interp/interp.go b/vendor/mvdan.cc/sh/interp/interp.go index 4919f222..8fb73acf 100644 --- a/vendor/mvdan.cc/sh/interp/interp.go +++ b/vendor/mvdan.cc/sh/interp/interp.go @@ -602,7 +602,7 @@ func (r *Runner) cmd(cm syntax.Command) { } case *syntax.TestClause: r.exit = 0 - if r.bashTest(x.X) == "" && r.exit == 0 { + if r.bashTest(x.X, false) == "" && r.exit == 0 { // to preserve exit code 2 for regex // errors, etc r.exit = 1 diff --git a/vendor/mvdan.cc/sh/interp/test.go b/vendor/mvdan.cc/sh/interp/test.go index 13b26ac2..423a3a5d 100644 --- a/vendor/mvdan.cc/sh/interp/test.go +++ b/vendor/mvdan.cc/sh/interp/test.go @@ -15,29 +15,36 @@ import ( ) // non-empty string is true, empty string is false -func (r *Runner) bashTest(expr syntax.TestExpr) string { +func (r *Runner) bashTest(expr syntax.TestExpr, classic bool) string { switch x := expr.(type) { case *syntax.Word: return r.loneWord(x) case *syntax.ParenTest: - return r.bashTest(x.X) + return r.bashTest(x.X, classic) case *syntax.BinaryTest: switch x.Op { case syntax.TsMatch, syntax.TsNoMatch: str := r.loneWord(x.X.(*syntax.Word)) yw := x.Y.(*syntax.Word) - pat := r.lonePattern(yw) - if match(pat, str) == (x.Op == syntax.TsMatch) { - return "1" + if classic { // test, [ + lit := r.loneWord(yw) + if (str == lit) == (x.Op == syntax.TsMatch) { + return "1" + } + } else { // [[ + pat := r.lonePattern(yw) + if match(pat, str) == (x.Op == syntax.TsMatch) { + return "1" + } } return "" } - if r.binTest(x.Op, r.bashTest(x.X), r.bashTest(x.Y)) { + if r.binTest(x.Op, r.bashTest(x.X, classic), r.bashTest(x.Y, classic)) { return "1" } return "" case *syntax.UnaryTest: - if r.unTest(x.Op, r.bashTest(x.X)) { + if r.unTest(x.Op, r.bashTest(x.X, classic)) { return "1" } return "" diff --git a/vendor/mvdan.cc/sh/shell/doc.go b/vendor/mvdan.cc/sh/shell/doc.go new file mode 100644 index 00000000..554e4b03 --- /dev/null +++ b/vendor/mvdan.cc/sh/shell/doc.go @@ -0,0 +1,9 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +// Package shell contains high-level features that use the syntax and +// interp packages under the hood. +// +// This package is a work in progress and EXPERIMENTAL; its API is not +// subject to the 1.x backwards compatibility guarantee. +package shell diff --git a/vendor/mvdan.cc/sh/shell/expand.go b/vendor/mvdan.cc/sh/shell/expand.go new file mode 100644 index 00000000..6fbf883e --- /dev/null +++ b/vendor/mvdan.cc/sh/shell/expand.go @@ -0,0 +1,47 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package shell + +import ( + "context" + "strings" + + "mvdan.cc/sh/interp" + "mvdan.cc/sh/syntax" +) + +// Expand performs shell expansion on s, using env to resolve variables. +// The expansion will apply to parameter expansions like $var and +// ${#var}, but also to arithmetic expansions like $((var + 3)), and +// command substitutions like $(echo foo). +// +// If env is nil, the current environment variables are used. +// +// Any side effects or modifications to the system are forbidden when +// interpreting the program. This is enforced via whitelists when +// executing programs and opening paths. The interpreter also has a timeout of +// two seconds. +func Expand(s string, env func(string) string) (string, error) { + p := syntax.NewParser() + src := "< +// See LICENSE for licensing information + +package shell + +import ( + "context" + "fmt" + "io" + "os" + "time" + + "mvdan.cc/sh/interp" + "mvdan.cc/sh/syntax" +) + +// SourceFile sources a shell file from disk and returns the variables +// declared in it. +// +// A default parser is used; to set custom options, use SourceNode +// instead. +func SourceFile(path string) (map[string]interp.Variable, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("could not open: %v", err) + } + defer f.Close() + p := syntax.NewParser() + file, err := p.Parse(f, path) + if err != nil { + return nil, fmt.Errorf("could not parse: %v", err) + } + return SourceNode(file) +} + +// purePrograms holds a list of common programs that do not have side +// effects, or otherwise cannot modify or harm the system that runs +// them. +var purePrograms = []string{ + // string handling + "sed", "grep", "tr", "cut", "cat", "head", "tail", "seq", "yes", + "wc", + // paths + "ls", "pwd", "basename", "realpath", + // others + "env", "sleep", "uniq", "sort", +} + +var pureRunnerTimeout = 2 * time.Second + +func pureRunner() *interp.Runner { + r := &interp.Runner{} + // forbid executing programs that might cause trouble + r.Exec = func(ctx interp.Ctxt, path string, args []string) error { + for _, name := range purePrograms { + if args[0] == name { + return interp.DefaultExec(ctx, path, args) + } + } + return fmt.Errorf("program not in whitelist: %s", args[0]) + } + // forbid opening any real files + r.Open = interp.OpenDevImpls(func(ctx interp.Ctxt, path string, flags int, mode os.FileMode) (io.ReadWriteCloser, error) { + return nil, fmt.Errorf("cannot open path: %s", ctx.UnixPath(path)) + }) + return r +} + +// SourceNode sources a shell program from a node and returns the +// variables declared in it. +// +// Any side effects or modifications to the system are forbidden when +// interpreting the program. This is enforced via whitelists when +// executing programs and opening paths. The interpreter also has a timeout of +// two seconds. +func SourceNode(node syntax.Node) (map[string]interp.Variable, error) { + r := pureRunner() + r.Reset() + ctx, cancel := context.WithTimeout(context.Background(), pureRunnerTimeout) + defer cancel() + r.Context = ctx + if err := r.Run(node); err != nil { + return nil, fmt.Errorf("could not run: %v", err) + } + // delete the internal shell vars that the user is not + // interested in + delete(r.Vars, "PWD") + delete(r.Vars, "HOME") + delete(r.Vars, "PATH") + delete(r.Vars, "IFS") + delete(r.Vars, "OPTIND") + return r.Vars, nil +} diff --git a/vendor/mvdan.cc/sh/syntax/doc.go b/vendor/mvdan.cc/sh/syntax/doc.go index 803e65e8..eff8c2fe 100644 --- a/vendor/mvdan.cc/sh/syntax/doc.go +++ b/vendor/mvdan.cc/sh/syntax/doc.go @@ -3,4 +3,4 @@ // Package syntax implements parsing and formatting of shell programs. // It supports both POSIX Shell and Bash. -package syntax // import "mvdan.cc/sh/syntax" +package syntax diff --git a/vendor/mvdan.cc/sh/syntax/printer.go b/vendor/mvdan.cc/sh/syntax/printer.go index a767fbe7..9658d2df 100644 --- a/vendor/mvdan.cc/sh/syntax/printer.go +++ b/vendor/mvdan.cc/sh/syntax/printer.go @@ -788,6 +788,7 @@ func (p *Printer) elemJoin(elems []*ArrayElem, last []Comment) { } func (p *Printer) stmt(s *Stmt) { + p.wroteSemi = false if s.Negated { p.spacedString("!", s.Pos()) } @@ -817,7 +818,6 @@ func (p *Printer) stmt(s *Stmt) { p.pendingHdocs = append(p.pendingHdocs, r) } } - p.wroteSemi = false switch { case s.Semicolon.IsValid() && s.Semicolon.Line() > p.line: p.bslashNewl()