1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
package core
import (
"io"
"strings"
"text/template"
"time"
"github.com/Masterminds/sprig/v3"
)
// Batch data is a common structure for templates when generating change fragments.
type BatchData struct {
// Time of the change
Time time.Time
// Version of the change, will include "v" prefix if used
Version string
// Version of the release without the "v" prefix if used
VersionNoPrefix string
// Previous released version
PreviousVersion string
// Major value of the version
Major int
// Minor value of the version
Minor int
// Patch value of the version
Patch int
// Prerelease value of the version
Prerelease string
// Metadata value of the version
Metadata string
// Changes included in the batch
Changes []Change
// Env vars configured by the system.
// See [envPrefix](#config-envprefix) for configuration.
Env map[string]string
}
// Component data stores data related to writing component headers.
type ComponentData struct {
// Name of the component
Component string `required:"true"`
// Env vars configured by the system.
// See [envPrefix](#config-envprefix) for configuration.
Env map[string]string
}
// Kind data stores data related to writing kind headers.
type KindData struct {
// Name of the kind
Kind string `required:"true"`
// Env vars configured by the system.
// See [envPrefix](#config-envprefix) for configuration.
Env map[string]string
}
// Template cache handles running all the templates for change fragments.
// Included options include the default [go template](https://golang.org/pkg/text/template/)
// and [sprig functions](https://masterminds.github.io/sprig/) for formatting.
// Additionally, custom template functions are listed below for working with changes.
// example: yaml
// format: |
// ### Contributors
// {{- range (customs .Changes "Author" | uniq) }}
// * [{{.}}](https://github.com/{{.}})
// {{- end}}
type TemplateCache struct {
cache map[string]*template.Template
}
func NewTemplateCache() *TemplateCache {
return &TemplateCache{
cache: make(map[string]*template.Template),
}
}
func (tc *TemplateCache) Load(text string) (*template.Template, error) {
cachedTemplate, ok := tc.cache[text]
if ok {
return cachedTemplate, nil
}
templ, err := template.New(text).Funcs(tc.buildFuncMap()).Parse(text)
if err != nil {
// do not save our template if it had an error
return nil, err
}
tc.cache[text] = templ
return templ, err
}
func (tc *TemplateCache) Execute(text string, wr io.Writer, data any) error {
templ, err := tc.Load(text)
if err != nil {
return err
}
return templ.Execute(wr, data)
}
func (tc *TemplateCache) ExecuteString(text string, data any) (string, error) {
sb := strings.Builder{}
templ, err := tc.Load(text)
if err != nil {
return "", err
}
err = templ.Execute(&sb, data)
if err != nil {
return "", err
}
return sb.String(), nil
}
func (tc *TemplateCache) buildFuncMap() map[string]any {
funcs := sprig.TxtFuncMap()
funcs["count"] = tc.Count
funcs["components"] = tc.Components
funcs["kinds"] = tc.Kinds
funcs["bodies"] = tc.Bodies
funcs["times"] = tc.Times
funcs["customs"] = tc.Customs
return funcs
}
// Count will return the number of occurrences of a string in a slice.
// example: yaml
// format: "{{ kinds .Changes | count \"added\" }} kinds"
func (tc *TemplateCache) Count(value string, items []string) (int, error) {
count := 0
for _, i := range items {
if i == value {
count++
}
}
return count, nil
}
// Components will return all the components from the provided changes.
// example: yaml
// format: "{{components .Changes }} components"
func (tc *TemplateCache) Components(changes []Change) ([]string, error) {
comps := make([]string, len(changes))
for i, c := range changes {
comps[i] = c.Component
}
return comps, nil
}
// Kinds will return all the kindsi from the provided changes.
// example: yaml
// format: "{{ kinds .Changes }} kinds"
func (tc *TemplateCache) Kinds(changes []Change) ([]string, error) {
kinds := make([]string, len(changes))
for i, c := range changes {
kinds[i] = c.Kind
}
return kinds, nil
}
// Bodies will return all the bodies from the provided changes.
// example: yaml
// format: "{{ bodies .Changes }} bodies"
func (tc *TemplateCache) Bodies(changes []Change) ([]string, error) {
bodies := make([]string, len(changes))
for i, c := range changes {
bodies[i] = c.Body
}
return bodies, nil
}
// Times will return all the times from the provided changes.
// example: yaml
// format: "{{ times .Changes }} times"
func (tc *TemplateCache) Times(changes []Change) ([]time.Time, error) {
times := make([]time.Time, len(changes))
for i, c := range changes {
times[i] = c.Time
}
return times, nil
}
// Customs will return all the values from the custom map by a key.
// If a key is missing from a change, it will be an empty string.
// example: yaml
// format: "{{ customs .Changes \"Author\" }} authors"
func (tc *TemplateCache) Customs(changes []Change, key string) ([]string, error) {
values := make([]string, len(changes))
for i, c := range changes {
values[i] = c.Custom[key]
}
return values, nil
}
|