Skip to content

Commit

Permalink
feat: add map and indexOrDefault template functions (#4317)
Browse files Browse the repository at this point in the history
Add a new function `map` to template functions.

`map` creates a map from a list of pairs of key values, then you can
convert a key to a value using `.Get`. This simplifies writing
key-mapping templates. For example, the defaule `archives.name_template`

```yaml
name_template: >-
    {{ .ProjectName }}_
    {{- title .Os }}_
    {{- if eq .Arch "amd64" }}x86_64
    {{- else if eq .Arch "386" }}i386
    {{- else }}{{ .Arch }}{{ end }}
    {{- if .Arm }}v{{ .Arm }}{{ end }}
```

becomes

```yaml
name_template: >-
  {{ $arch := map "amd64" "x86_64" "386" "i386" -}}
  {{ .ProjectName }}_
  {{- title .OS }}_
  {{- $arch.Get .Arch .Arch }}
  {{- if .Arm }}v{{ .Arm }}{{ end }}
```

---------

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
  • Loading branch information
j178 and caarlos0 authored Sep 20, 2023
1 parent bedf38c commit 2434735
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 34 deletions.
53 changes: 37 additions & 16 deletions internal/tmpl/tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,22 +204,24 @@ func (t *Template) Apply(s string) (string, error) {
"time": func(s string) string {
return time.Now().UTC().Format(s)
},
"tolower": strings.ToLower,
"toupper": strings.ToUpper,
"trim": strings.TrimSpace,
"trimprefix": strings.TrimPrefix,
"trimsuffix": strings.TrimSuffix,
"title": cases.Title(language.English).String,
"dir": filepath.Dir,
"base": filepath.Base,
"abs": filepath.Abs,
"incmajor": incMajor,
"incminor": incMinor,
"incpatch": incPatch,
"filter": filter(false),
"reverseFilter": filter(true),
"mdv2escape": mdv2Escape,
"envOrDefault": t.envOrDefault,
"tolower": strings.ToLower,
"toupper": strings.ToUpper,
"trim": strings.TrimSpace,
"trimprefix": strings.TrimPrefix,
"trimsuffix": strings.TrimSuffix,
"title": cases.Title(language.English).String,
"dir": filepath.Dir,
"base": filepath.Base,
"abs": filepath.Abs,
"incmajor": incMajor,
"incminor": incMinor,
"incpatch": incPatch,
"filter": filter(false),
"reverseFilter": filter(true),
"mdv2escape": mdv2Escape,
"envOrDefault": t.envOrDefault,
"map": makemap,
"indexOrDefault": indexOrDefault,
}).
Parse(s)
if err != nil {
Expand Down Expand Up @@ -346,3 +348,22 @@ func mdv2Escape(s string) string {
"!", "\\!",
).Replace(s)
}

func makemap(kvs ...string) (map[string]string, error) {
if len(kvs)%2 != 0 {
return nil, fmt.Errorf("map expects even number of arguments, got %d", len(kvs))
}
m := make(map[string]string)
for i := 0; i < len(kvs); i += 2 {
m[kvs[i]] = kvs[i+1]
}
return m, nil
}

func indexOrDefault(m map[string]string, name, value string) string {
s, ok := m[name]
if ok {
return s
}
return value
}
10 changes: 10 additions & 0 deletions internal/tmpl/tmpl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,13 @@ func TestMdv2Escape(t *testing.T) {
"aaa\\_\\*\\[\\]\\(\\)\\~\\`\\>\\#\\+\\-\\=\\|\\{\\}\\.\\!",
mdv2Escape("aaa_*[]()~`>#+-=|{}.!"))
}

func TestMap(t *testing.T) {
ctx := testctx.New()
out, err := New(ctx).Apply(`{{ $m := map "a" "1" "b" "2" }}{{ index $m "a" }}{{ indexOrDefault $m "b" "1" }}{{ indexOrDefault $m "c" "3" }}{{ index $m "z" }}`)
require.NoError(t, err)
require.Equal(t, "123", out)

_, err = New(ctx).Apply(`{{ $m := map "a" }}`)
require.ErrorContains(t, err, "map expects even number of arguments, got 1")
}
38 changes: 20 additions & 18 deletions www/docs/customization/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,26 @@ In the `release.body` field, you can use these extra fields:

On all fields, you have these available functions:

| Usage | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `replace "v1.2" "v" ""` | replaces all matches. See [ReplaceAll](https://golang.org/pkg/strings/#ReplaceAll). |
| `split "1.2" "."` | split string at separator. See [Split](https://golang.org/pkg/strings/#Split). Since v1.11 |
| `time "01/02/2006"` | current UTC time in the specified format (this is not deterministic, a new time for every call). |
| `tolower "V1.2"` | makes input string lowercase. See [ToLower](https://golang.org/pkg/strings/#ToLower). |
| `toupper "v1.2"` | makes input string uppercase. See [ToUpper](https://golang.org/pkg/strings/#ToUpper). |
| `trim " v1.2 "` | removes all leading and trailing white space. See [TrimSpace](https://golang.org/pkg/strings/#TrimSpace). |
| `trimprefix "v1.2" "v"` | removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix). |
| `trimsuffix "1.2v" "v"` | removes provided trailing suffix string, if present. See [TrimSuffix](https://pkg.go.dev/strings#TrimSuffix). |
| `dir .Path` | returns all but the last element of path, typically the path's directory. See [Dir](https://golang.org/pkg/path/filepath/#Dir). |
| `base .Path` | returns the last element of path. See [Base](https://golang.org/pkg/path/filepath/#Base). Since v1.16 |
| `abs .ArtifactPath` | returns an absolute representation of path. See [Abs](https://golang.org/pkg/path/filepath/#Abs). |
| `filter "text" "regex"` | keeps only the lines matching the given regex, analogous to `grep -E`. Since v1.6 |
| `reverseFilter "text" "regex"` | keeps only the lines **not** matching the given regex, analogous to `grep -vE`. Since v1.6 |
| `title "foo"` | "titlenize" the string using english as language. See [Title](https://pkg.go.dev/golang.org/x/text/cases#Title). Since v1.14 |
| `mdv2escape "foo"` | escape characters according to MarkdownV2, especially useful in the Telegram integration. Since v1.19 |
| `envOrDefault "NAME" "value"` | either gets the value of the given environment variable, or the given default. Since v1.19 |
| Usage | Description |
|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| `replace "v1.2" "v" ""` | replaces all matches. See [ReplaceAll](https://golang.org/pkg/strings/#ReplaceAll). |
| `split "1.2" "."` | split string at separator. See [Split](https://golang.org/pkg/strings/#Split). Since v1.11 |
| `time "01/02/2006"` | current UTC time in the specified format (this is not deterministic, a new time for every call). |
| `tolower "V1.2"` | makes input string lowercase. See [ToLower](https://golang.org/pkg/strings/#ToLower). |
| `toupper "v1.2"` | makes input string uppercase. See [ToUpper](https://golang.org/pkg/strings/#ToUpper). |
| `trim " v1.2 "` | removes all leading and trailing white space. See [TrimSpace](https://golang.org/pkg/strings/#TrimSpace). |
| `trimprefix "v1.2" "v"` | removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix). |
| `trimsuffix "1.2v" "v"` | removes provided trailing suffix string, if present. See [TrimSuffix](https://pkg.go.dev/strings#TrimSuffix). |
| `dir .Path` | returns all but the last element of path, typically the path's directory. See [Dir](https://golang.org/pkg/path/filepath/#Dir). |
| `base .Path` | returns the last element of path. See [Base](https://golang.org/pkg/path/filepath/#Base). Since v1.16 |
| `abs .ArtifactPath` | returns an absolute representation of path. See [Abs](https://golang.org/pkg/path/filepath/#Abs). |
| `filter "text" "regex"` | keeps only the lines matching the given regex, analogous to `grep -E`. Since v1.6 |
| `reverseFilter "text" "regex"` | keeps only the lines **not** matching the given regex, analogous to `grep -vE`. Since v1.6 |
| `title "foo"` | "titlenize" the string using english as language. See [Title](https://pkg.go.dev/golang.org/x/text/cases#Title). Since v1.14 |
| `mdv2escape "foo"` | escape characters according to MarkdownV2, especially useful in the Telegram integration. Since v1.19 |
| `envOrDefault "NAME" "value"` | either gets the value of the given environment variable, or the given default. Since v1.19 |
| `$m := map "KEY" "VALUE"` | creates a map from a list of key and value pairs. Since v1.21 |
| `indexOrDefault $m "KEY" "value"` | either gets the value of the given key or the given default value from the given map. Since v1.21 |

With all those fields, you may be able to compose the name of your artifacts
pretty much the way you want:
Expand Down

0 comments on commit 2434735

Please sign in to comment.