From 0386beed34a57e9f8ef9c035dccad854b2de85ab Mon Sep 17 00:00:00 2001 From: sinlov Date: Fri, 1 Mar 2024 01:42:18 +0800 Subject: [PATCH] feat: add wd_template package for support Handlebars.js for golang use github.com/aymerick/raymond v2.0.2 and open by wd_template.RegisterSettings(wd_template.DefaultHelpers) fe #28 --- cmd/woodpecker-tools/main.go | 3 + go.mod | 12 ++++ go.sum | 56 +++++++++++++++ wd_template/defaultHelpers.go | 112 ++++++++++++++++++++++++++++++ wd_template/helpers.go | 83 ++++++++++++++++++++++ wd_template/helpers_test.go | 84 ++++++++++++++++++++++ wd_template/template.go | 73 +++++++++++++++++++ wd_template_test/init_test.go | 7 ++ wd_template_test/template_test.go | 65 +++++++++++++++++ 9 files changed, 495 insertions(+) create mode 100644 wd_template/defaultHelpers.go create mode 100644 wd_template/helpers.go create mode 100644 wd_template/helpers_test.go create mode 100644 wd_template/template.go create mode 100644 wd_template_test/init_test.go create mode 100644 wd_template_test/template_test.go diff --git a/cmd/woodpecker-tools/main.go b/cmd/woodpecker-tools/main.go index bc43e6d..f48d457 100644 --- a/cmd/woodpecker-tools/main.go +++ b/cmd/woodpecker-tools/main.go @@ -15,6 +15,9 @@ func main() { wd_log.SetLogLineDeep(wd_log.DefaultExtLogLineMaxDeep) pkgJson.InitPkgJsonContent(resource.PackageJson) + // init wd_template + //wd_template.RegisterSettings(wd_template.DefaultHelpers) + app := cli.NewCliApp() // app run as urfave diff --git a/go.mod b/go.mod index 821be70..5b536f7 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.19 require ( github.com/Masterminds/semver/v3 v3.2.1 + github.com/Masterminds/sprig/v3 v3.2.3 + github.com/aymerick/raymond v2.0.2+incompatible github.com/gookit/color v1.5.4 github.com/joho/godotenv v1.5.1 github.com/sebdah/goldie/v2 v2.5.3 @@ -13,13 +15,23 @@ require ( ) require ( + github.com/Masterminds/goutils v1.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/spf13/cast v1.3.1 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/crypto v0.3.0 // indirect golang.org/x/sys v0.10.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9151e3b..6f4f3d9 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,31 @@ +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0= +github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +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/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -18,10 +35,16 @@ github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624 github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +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/sinlov-go/unittest-kit v1.0.0 h1:38d82yUsE2UvCNoI1/YJBONimdXhniar1W3wCiqkMZ0= github.com/sinlov-go/unittest-kit v1.0.0/go.mod h1:2Xkvyl6VpSXAZ6lyrb4q52Dk4oAD+Yio5KQsd1ZhxOc= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= @@ -30,9 +53,42 @@ github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHg github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/wd_template/defaultHelpers.go b/wd_template/defaultHelpers.go new file mode 100644 index 0000000..4bf5beb --- /dev/null +++ b/wd_template/defaultHelpers.go @@ -0,0 +1,112 @@ +package wd_template + +import ( + "fmt" + "github.com/aymerick/raymond" + _ "github.com/aymerick/raymond" + "math" + "net/url" + "regexp" + "strings" + "time" + "unicode" + "unicode/utf8" +) + +var ( + DefaultHelpers = map[string]interface{}{ + "duration": toDuration, + "datetime": toDatetime, + "success": isSuccess, + "failure": isFailure, + "truncate": truncate, + "urlencode": urlencode, + "since": since, + "uppercasefirst": uppercaseFirst, + "uppercase": strings.ToUpper, + "lowercase": strings.ToLower, + "regexReplace": regexReplace, + } +) + +func toDuration(started, finished int64) string { + return fmt.Sprint(time.Duration(finished-started) * time.Second) +} + +func toDatetime(timestamp int64, layout, zone string) string { + if len(zone) == 0 { + return time.Unix(timestamp, 0).Format(layout) + } + + loc, err := time.LoadLocation(zone) + + if err != nil { + return time.Unix(timestamp, 0).Local().Format(layout) + } + + return time.Unix(timestamp, 0).In(loc).Format(layout) +} + +func isSuccess(conditional bool, options *raymond.Options) string { + if !conditional { + return options.Inverse() + } + + switch options.ParamStr(0) { + case "success": + return options.Fn() + default: + return options.Inverse() + } +} + +func isFailure(conditional bool, options *raymond.Options) string { + if !conditional { + return options.Inverse() + } + + switch options.ParamStr(0) { + case "failure", "error", "killed": + return options.Fn() + default: + return options.Inverse() + } +} + +func truncate(s string, len int) string { + if utf8.RuneCountInString(s) <= int(math.Abs(float64(len))) { + return s + } + + runes := []rune(s) + + if len < 0 { + len = -len + return string(runes[len:]) + } + + return string(runes[:len]) +} + +func urlencode(options *raymond.Options) string { + return url.QueryEscape(options.Fn()) +} + +func since(start int64) string { + now := time.Unix(time.Now().Unix(), 0) + return fmt.Sprint(now.Sub(time.Unix(start, 0))) +} + +func uppercaseFirst(s string) string { + a := []rune(s) + + a[0] = unicode.ToUpper(a[0]) + s = string(a) + + return s +} + +func regexReplace(pattern string, input string, replacement string) string { + re := regexp.MustCompile(pattern) + return re.ReplaceAllString(input, replacement) +} diff --git a/wd_template/helpers.go b/wd_template/helpers.go new file mode 100644 index 0000000..e7359d8 --- /dev/null +++ b/wd_template/helpers.go @@ -0,0 +1,83 @@ +package wd_template + +import ( + "github.com/Masterminds/sprig/v3" + "github.com/aymerick/raymond" + "reflect" +) + +// RegisterSettings +// most of this can use wd_template.RegisterSettings(DefaultFunctions) +func RegisterSettings(funcSettings map[string]interface{}) { + if len(funcSettings) == 0 { + funcSettings = map[string]interface{}{} + } + for name, function := range sprig.GenericFuncMap() { + if invalidHelper(name) { + continue + } + val := reflect.ValueOf(function) + funcType := val.Type() + if funcType.NumOut() != 1 { + continue + } + + funcSettings[name] = function + } + + raymond.RegisterHelpers(funcSettings) +} + +func invalidHelper(name string) bool { + invalids := []string{ + "buildCustomCert", + "decryptAES", + "derivePassword", + "encryptAES", + "fail", + "genCA", + "genPrivateKey", + "genSelfSignedCert", + "genSignedCert", + "hello", + "mustAppend", + "mustCompact", + "mustDateModify", + "mustDeepCopy", + "mustFirst", + "mustHas", + "mustInitial", + "mustLast", + "mustMerge", + "mustMergeOverwrite", + "mustPrepend", + "mustPush", + "mustRegexFind", + "mustRegexFindAll", + "mustRegexMatch", + "mustRegexReplaceAll", + "mustRegexReplaceAllLiteral", + "mustRegexSplit", + "mustRest", + "mustReverse", + "mustSlice", + "mustToDate", + "mustToJson", + "mustToPrettyJson", + "mustToRawJson", + "mustUniq", + "mustWithout", + "must_date_modify", + "semver", + "semverCompare", + "trimall", + } + + for _, invalid := range invalids { + if name == invalid { + return true + } + } + + return false +} diff --git a/wd_template/helpers_test.go b/wd_template/helpers_test.go new file mode 100644 index 0000000..053600d --- /dev/null +++ b/wd_template/helpers_test.go @@ -0,0 +1,84 @@ +package wd_template + +import ( + "testing" + "time" +) + +func TestToDuration(t *testing.T) { + from := time.Date(2017, time.November, 15, 23, 0, 0, 0, time.UTC).Unix() + + vals := map[int64]string{ + time.Date(2018, time.November, 15, 23, 0, 0, 0, time.UTC).Unix(): "8760h0m0s", + time.Date(2017, time.November, 16, 23, 0, 0, 0, time.UTC).Unix(): "24h0m0s", + time.Date(2017, time.November, 15, 23, 30, 0, 0, time.UTC).Unix(): "30m0s", + time.Date(2017, time.November, 15, 23, 10, 15, 0, time.UTC).Unix(): "10m15s", + time.Date(2017, time.October, 15, 23, 0, 0, 0, time.UTC).Unix(): "-744h0m0s", + } + + for input, want := range vals { + if got := toDuration(from, input); got != want { + t.Errorf("Want transform %d-%d to %s, got %s", from, input, want, got) + } + } +} + +func TestTruncate(t *testing.T) { + vals := map[string]string{ + "foobarz": "fooba", + "foöäüüu": "foöäü", + "üpsßßßk": "üpsßß", + "1234567": "12345", + "!'§$%&/": "!'§$%", + } + + for input, want := range vals { + if got := truncate(input, 5); got != want { + t.Errorf("Want transform %s to %s, got %s", input, want, got) + } + } +} + +func TestNegativeTruncate(t *testing.T) { + vals := map[string]string{ + "foobarz": "rz", + "foöäüüu": "üu", + "üpsßßßk": "ßk", + "1234567": "67", + "!'§$%&/": "&/", + } + + for input, want := range vals { + if got := truncate(input, -5); got != want { + t.Errorf("Want transform %s to %s, got %s", input, want, got) + } + } +} + +func TestSince(t *testing.T) { + t.Skip() +} + +func TestUppercaseFirst(t *testing.T) { + vals := map[string]string{ + "hello": "Hello", + "ßqwert": "ßqwert", + "üps": "Üps", + "12345": "12345", + "Foobar": "Foobar", + } + + for input, want := range vals { + if got := uppercaseFirst(input); got != want { + t.Errorf("Want transform %s to %s, got %s", input, want, got) + } + } +} + +func TestRegexReplace(t *testing.T) { + expected := "hello-my-String-123" + actual := regexReplace("(.*?)\\/(.*)", "hello/my-String-123", "$1-$2") + if actual != "hello-my-String-123" { + t.Errorf("error, expected %s, got %s", expected, actual) + } +} diff --git a/wd_template/template.go b/wd_template/template.go new file mode 100644 index 0000000..03cfc2c --- /dev/null +++ b/wd_template/template.go @@ -0,0 +1,73 @@ +package wd_template + +import ( + "fmt" + "github.com/aymerick/raymond" + "io" + "net/http" + "net/url" + "os" + "strings" +) + +const ( + RenderStatusShow = "success" + RenderStatusHide = "failure" +) + +// Render parses and executes a template, returning the results in string +// format. Trailing or leading spaces or new-lines are not getting truncated. It +// is able to read templates from remote paths, local files or directly from the +// string. +// please use wd_template.RegisterSettings(DefaultFunctions) once to use. +func Render(template string, payload interface{}) (s string, errOut error) { + u, err := url.Parse(template) + + if err == nil { + switch u.Scheme { + case "http", "https": + res, errGet := http.Get(template) + + if errGet != nil { + errOut = fmt.Errorf("failed to fetch: %w", errGet) + return s, errOut + } + + defer func(Body io.ReadCloser) { + errBodyRead := Body.Close() + if errBodyRead != nil { + errOut = fmt.Errorf("failed to close body: %w", errBodyRead) + } + }(res.Body) + + out, errRead := io.ReadAll(res.Body) + + if errRead != nil { + errOut = fmt.Errorf("failed to read: %w", errRead) + return s, errOut + } + + template = string(out) + case "file": + out, errReadFile := os.ReadFile(u.Path) + + if errReadFile != nil { + errOut = fmt.Errorf("failed to read: %w", errReadFile) + return s, errOut + } + + template = string(out) + } + } + + return raymond.Render(template, payload) +} + +// RenderTrim parses and executes a template, returning the results in string +// format. The result is trimmed to remove left and right padding and newlines +// that may be added unintentially in the template markup. +// please use wd_template.RegisterSettings(DefaultFunctions) once to use. +func RenderTrim(template string, payload interface{}) (string, error) { + out, err := Render(template, payload) + return strings.Trim(out, " \n"), err +} diff --git a/wd_template_test/init_test.go b/wd_template_test/init_test.go new file mode 100644 index 0000000..ddd3cb6 --- /dev/null +++ b/wd_template_test/init_test.go @@ -0,0 +1,7 @@ +package wd_template_test + +import "github.com/woodpecker-kit/woodpecker-tools/wd_template" + +func init() { + wd_template.RegisterSettings(wd_template.DefaultHelpers) +} diff --git a/wd_template_test/template_test.go b/wd_template_test/template_test.go new file mode 100644 index 0000000..a53d708 --- /dev/null +++ b/wd_template_test/template_test.go @@ -0,0 +1,65 @@ +package wd_template + +import ( + "github.com/stretchr/testify/assert" + "github.com/woodpecker-kit/woodpecker-tools/wd_template" + "testing" +) + +func Test_RenderTrim(t *testing.T) { + // mock _RenderTrim + t.Logf("~> mock _RenderTrim") + // do _RenderTrim + t.Logf("~> do _RenderTrim") + s := struct { + Foo string + }{ + Foo: "bar", + } + + trim, err := wd_template.RenderTrim("", s) + if err != nil { + t.Fatalf("RenderTrim error %v", err) + } + // verify _RenderTrim + assert.Equal(t, "", trim) + + var tpl = `{ + "Foo": "{{ Foo }}" +} +` + + trim2, err := wd_template.RenderTrim(tpl, s) + if err != nil { + t.Fatalf("RenderTrim error %v", err) + } + // verify _RenderTrim + assert.Equal(t, `{ + "Foo": "bar" +}`, trim2) + + var successCheckTpl = ` +{{#success Status }}✅{{/success}} +{{#failure Status}}❌{{/failure}} +` + sModeSucc := struct { + Status string + }{ + Status: "success", + } + trim3, err := wd_template.RenderTrim(successCheckTpl, sModeSucc) + if err != nil { + t.Fatalf("RenderTrim error %v", err) + } + sModeFail := struct { + Status string + }{ + Status: "failure", + } + assert.Equal(t, `✅`, trim3) + trim4, err := wd_template.RenderTrim(successCheckTpl, sModeFail) + if err != nil { + t.Fatalf("RenderTrim error %v", err) + } + assert.Equal(t, `❌`, trim4) +}