diff --git a/cmd/bashbrew/cmd-cat.go b/cmd/bashbrew/cmd-cat.go index 8edb581c..ee825994 100644 --- a/cmd/bashbrew/cmd-cat.go +++ b/cmd/bashbrew/cmd-cat.go @@ -48,7 +48,7 @@ func cmdCat(c *cli.Context) error { } var i int - tmpl, err := template.New(templateName).Funcs(templatelib.FuncMap).Funcs(template.FuncMap{ + tmpl, err := template.New(templateName).Funcs(templatelib.FuncMap()).Funcs(template.FuncMap{ "i": func() int { return i }, diff --git a/go.mod b/go.mod index a03278ee..a8132737 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/docker-library/bashbrew go 1.13 require ( + github.com/Masterminds/sprig/v3 v3.2.0 github.com/containerd/containerd v1.4.0 github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/go-git/go-git/v5 v5.1.0 - github.com/imdario/mergo v0.3.11 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 github.com/pkg/errors v0.9.1 // indirect diff --git a/go.sum b/go.sum index a227e1f2..209bee10 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.0 h1:P1ekkbuU73Ui/wS0nK1HOM37hh4xdfZo485UPf8rc+Y= +github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= @@ -45,6 +51,10 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -61,8 +71,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -79,15 +93,20 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= @@ -97,6 +116,7 @@ golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/pkg/templatelib/lib.go b/pkg/templatelib/lib.go index 9e87531e..7f891445 100644 --- a/pkg/templatelib/lib.go +++ b/pkg/templatelib/lib.go @@ -1,12 +1,13 @@ package templatelib import ( - "encoding/json" "fmt" "os" "reflect" "strings" "text/template" + + "github.com/Masterminds/sprig/v3" ) func swapStringsFuncBoolArgsOrder(a func(string, string) bool) func(string, string) bool { @@ -68,55 +69,33 @@ func stringsModifierActionFactory(a func(string, string) string) func([]string, } } -var FuncMap = template.FuncMap{ - // {{- $isGitHub := hasPrefix "https://github.com/" $url -}} - // {{- $isHtml := hasSuffix ".html" $url -}} - "hasPrefix": swapStringsFuncBoolArgsOrder(strings.HasPrefix), - "hasSuffix": swapStringsFuncBoolArgsOrder(strings.HasSuffix), - - // {{- $hugeIfTrue := .SomeValue | ternary "HUGE" "not so huge" -}} - // if .SomeValue is truthy, $hugeIfTrue will be "HUGE" - // (otherwise, "not so huge") - "ternary": func(truthy interface{}, falsey interface{}, val interface{}) interface{} { - if t, ok := template.IsTrue(val); !ok { - panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, val)) - } else if t { - return truthy - } else { - return falsey +func FuncMap() template.FuncMap { + funcMap := sprig.TxtFuncMap() + + // https://github.com/Masterminds/sprig/pull/276 + funcMap["ternary"] = func(vt interface{}, vf interface{}, v interface{}) interface{} { + if truth, ok := template.IsTrue(v); !ok { + panic(fmt.Sprintf(`template.IsTrue(%+v) says things are NOT OK`, v)) + } else if truth { + return vt } - }, + return vf + } - // First Tag: {{- .Tags | first -}} - // Last Tag: {{- .Tags | last -}} - "first": thingsActionFactory("first", true, func(args []interface{}, arg interface{}) interface{} { return arg }), - "last": thingsActionFactory("last", false, func(args []interface{}, arg interface{}) interface{} { return arg }), + // Everybody: {{- join ", " .Names -}} + // Concat: {{- join "/" "https://github.com" "jsmith" "some-repo" -}} + funcMap["join"] = stringsActionFactory("join", true, strings.Join) + // (this differs slightly from the Sprig "join" in that it accepts either a list of strings or multiple arguments - Sprig instead has an explicit "list" function which can create a list of strings *from* a list of arguments so that multiple-signature usability like this is not necessary) // JSON data dump: {{ json . }} // (especially nice for taking data and piping it to "jq") // (ie "some-tool inspect --format '{{ json . }}' some-things | jq .") - "json": func(v interface{}) (string, error) { - j, err := json.Marshal(v) - return string(j), err - }, - - // Everybody: {{- join ", " .Names -}} - // Concat: {{- join "/" "https://github.com" "jsmith" "some-repo" -}} - "join": stringsActionFactory("join", true, strings.Join), - - // {{- $mungedUrl := $url | replace "git://" "https://" | trimSuffixes ".git" -}} - // turns: git://github.com/jsmith/some-repo.git - // into: https://github.com/jsmith/some-repo - "trimPrefixes": stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)), - "trimSuffixes": stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)), - "replace": stringsActionFactory("replace", false, func(strs []string, str string) string { - return strings.NewReplacer(strs...).Replace(str) - }), + funcMap["json"] = funcMap["toJson"] // {{- getenv "PATH" -}} // {{- getenv "HOME" "no HOME set" -}} // {{- getenv "HOME" "is set" "is NOT set (or is empty)" -}} - "getenv": thingsActionFactory("getenv", true, func(args []interface{}, arg interface{}) interface{} { + funcMap["getenv"] = thingsActionFactory("getenv", true, func(args []interface{}, arg interface{}) interface{} { var ( val = os.Getenv(arg.(string)) setVal interface{} = val @@ -134,5 +113,13 @@ var FuncMap = template.FuncMap{ } else { return unsetVal } - }), + }) + + // {{- $mungedUrl := $url | replace "git://" "https://" | trimSuffixes ".git" -}} + // turns: git://github.com/jsmith/some-repo.git + // into: https://github.com/jsmith/some-repo + funcMap["trimPrefixes"] = stringsActionFactory("trimPrefixes", false, stringsModifierActionFactory(strings.TrimPrefix)) + funcMap["trimSuffixes"] = stringsActionFactory("trimSuffixes", false, stringsModifierActionFactory(strings.TrimSuffix)) + + return funcMap } diff --git a/pkg/templatelib/lib_example_test.go b/pkg/templatelib/lib_example_test.go index 53a4495a..e0d409a5 100644 --- a/pkg/templatelib/lib_example_test.go +++ b/pkg/templatelib/lib_example_test.go @@ -8,7 +8,7 @@ import ( ) func Example_prefixSuffix() { - tmpl, err := template.New("github-or-html").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("github-or-html").Funcs(templatelib.FuncMap()).Parse(` {{- . -}} {{- if hasPrefix "https://github.com/" . -}} @@ -53,7 +53,7 @@ func Example_prefixSuffix() { } func Example_ternary() { - tmpl, err := template.New("huge-if-true").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("huge-if-true").Funcs(templatelib.FuncMap()).Parse(` {{- range $a := . -}} {{ printf "%#v: %s\n" $a (ternary "HUGE" "not so huge" $a) }} {{- end -}} @@ -91,7 +91,7 @@ func Example_ternary() { } func Example_firstLast() { - tmpl, err := template.New("first-and-last").Funcs(templatelib.FuncMap).Parse(`First: {{ . | first }}, Last: {{ . | last }}`) + tmpl, err := template.New("first-and-last").Funcs(templatelib.FuncMap()).Parse(`First: {{ . | first }}, Last: {{ . | last }}`) err = tmpl.Execute(os.Stdout, []interface{}{ "a", @@ -107,7 +107,7 @@ func Example_firstLast() { } func Example_json() { - tmpl, err := template.New("json").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("json").Funcs(templatelib.FuncMap()).Parse(` {{- json . -}} `) @@ -125,7 +125,7 @@ func Example_json() { } func Example_join() { - tmpl, err := template.New("join").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("join").Funcs(templatelib.FuncMap()).Parse(` Array: {{ . | join ", " }}{{ "\n" -}} Args: {{ join ", " "a" "b" "c" -}} `) @@ -145,7 +145,7 @@ func Example_join() { } func Example_trimReplaceGitToHttps() { - tmpl, err := template.New("git-to-https").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("git-to-https").Funcs(templatelib.FuncMap()).Parse(` {{- range . -}} {{- . | replace "git://" "https://" | trimSuffixes ".git" }}{{ "\n" -}} {{- end -}} @@ -167,7 +167,7 @@ func Example_trimReplaceGitToHttps() { } func Example_trimReplaceGitToGo() { - tmpl, err := template.New("git-to-go").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("git-to-go").Funcs(templatelib.FuncMap()).Parse(` {{- range . -}} {{- . | trimPrefixes "git://" "http://" "https://" "ssh://" | trimSuffixes ".git" }}{{ "\n" -}} {{- end -}} @@ -193,7 +193,7 @@ func Example_trimReplaceGitToGo() { } func Example_getenv() { - tmpl, err := template.New("getenv").Funcs(templatelib.FuncMap).Parse(` + tmpl, err := template.New("getenv").Funcs(templatelib.FuncMap()).Parse(` The FOO environment variable {{ getenv "FOO" "is set" "is not set" }}. {{- "\n" -}} BAR: {{ getenv "BAR" "not set" }} {{- "\n" -}} BAZ: {{ getenv "BAZ" "not set" }} {{- "\n" -}} diff --git a/pkg/templatelib/lib_test.go b/pkg/templatelib/lib_test.go index 09af0624..37812bb2 100644 --- a/pkg/templatelib/lib_test.go +++ b/pkg/templatelib/lib_test.go @@ -12,7 +12,7 @@ import ( func TestTernaryPanic(t *testing.T) { // one of the only places template.IsTrue will return "false" for the "ok" value is an UnsafePointer (hence this test) - tmpl, err := template.New("unsafe-pointer").Funcs(templatelib.FuncMap).Parse(`{{ ternary "true" "false" . }}`) + tmpl, err := template.New("unsafe-pointer").Funcs(templatelib.FuncMap()).Parse(`{{ ternary "true" "false" . }}`) if err != nil { t.Errorf("Unexpected error: %v", err) } @@ -27,7 +27,7 @@ func TestTernaryPanic(t *testing.T) { } func TestJoinPanic(t *testing.T) { - tmpl, err := template.New("join-no-arg").Funcs(templatelib.FuncMap).Parse(`{{ join }}`) + tmpl, err := template.New("join-no-arg").Funcs(templatelib.FuncMap()).Parse(`{{ join }}`) if err != nil { t.Errorf("Unexpected error: %v", err) }