1 Star 0 Fork 0

h79 / goutils

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
tmpl.go 7.99 KB
一键复制 编辑 原始数据 按行查看 历史
huqiuyun 提交于 2024-04-25 23:43 . build
package build
import (
"bytes"
"fmt"
"github.com/Masterminds/semver/v3"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"path/filepath"
"regexp"
"strings"
"text/template"
"time"
)
// Template holds data that can be applied to a template string.
type Template struct {
fields Fields
}
// Fields that will be available to the template engine.
type Fields map[string]interface{}
const (
// general keys.
productId = "ProductId"
projectName = "ProjectName"
version = "Version"
rawVersion = "RawVersion"
model = "Model"
tag = "Tag"
previousTag = "PreviousTag"
branch = "Branch"
commit = "Commit"
shortCommit = "ShortCommit"
fullCommit = "FullCommit"
commitDate = "CommitDate"
commitTimestamp = "CommitTimestamp"
gitURL = "GitURL"
summary = "Summary"
tagSubject = "TagSubject"
tagContents = "TagContents"
tagBody = "TagBody"
isGitDirty = "IsGitDirty"
major = "Major"
minor = "Minor"
patch = "Patch"
prerelease = "Prerelease"
isSnapshot = "IsSnapshot"
isNightly = "IsNightly"
isDraft = "IsDraft"
env = "Env"
date = "BuildDate"
now = "Now"
timestamp = "Timestamp"
runtimeK = "Runtime"
releaseNotes = "ReleaseNotes"
// artifact-only keys.
osKey = "Os"
amd64 = "Amd64"
arch = "Arch"
arm = "Arm"
mips = "Mips"
// build keys.
name = "Name"
ext = "Ext"
path = "Path"
target = "Target"
)
func NewEmptyTemplate() *Template {
return &Template{fields: Fields{}}
}
// NewTemplate Template.
func NewTemplate(ctx *Context) *Template {
rawVer := fmt.Sprintf("%d.%d.%d", ctx.Semver.Major, ctx.Semver.Minor, ctx.Semver.Patch)
return &Template{
fields: Fields{
productId: ctx.Config.ProductId,
projectName: ctx.Config.ProjectName,
version: ctx.Config.Version,
model: ctx.Config.Model,
osKey: ctx.Config.Os,
rawVersion: rawVer,
summary: ctx.Git.Summary,
tag: ctx.Git.CurrentTag,
previousTag: ctx.Git.PreviousTag,
branch: ctx.Git.Branch,
commit: ctx.Git.Commit,
shortCommit: ctx.Git.ShortCommit,
fullCommit: ctx.Git.FullCommit,
commitDate: ctx.Git.CommitDate.UTC().Format(time.RFC3339),
commitTimestamp: ctx.Git.CommitDate.UTC().Unix(),
gitURL: ctx.Git.URL,
isGitDirty: ctx.Git.Dirty,
env: ctx.Env,
date: ctx.DateFormat(ctx.Date),
timestamp: ctx.Date.UTC().Unix(),
now: ctx.Date.UTC(),
major: ctx.Semver.Major,
minor: ctx.Semver.Minor,
patch: ctx.Semver.Patch,
prerelease: ctx.Semver.Prerelease,
isSnapshot: ctx.Snapshot,
isNightly: false,
tagSubject: ctx.Git.TagSubject,
tagContents: ctx.Git.TagContents,
tagBody: ctx.Git.TagBody,
runtimeK: ctx.Runtime,
},
}
}
// WithEnvS overrides template's env field with the given KEY=VALUE list of
// environment variables.
func (t *Template) WithEnvS(envs []string) *Template {
result := map[string]string{}
for _, env := range envs {
k, v, ok := strings.Cut(env, "=")
if !ok || k == "" {
continue
}
result[k] = v
}
return t.WithEnv(result)
}
// WithEnv overrides template's env field with the given environment map.
func (t *Template) WithEnv(e map[string]string) *Template {
t.fields[env] = e
return t
}
// WithExtraFields allows to add new more custom fields to the template.
// It will override fields with the same name.
func (t *Template) WithExtraFields(f Fields) *Template {
for k, v := range f {
t.fields[k] = v
}
return t
}
func (t *Template) WithBuildOptions(opts Options) *Template {
return t.WithExtraFields(buildOptsToFields(opts))
}
func (t *Template) AddField(key string, value interface{}) *Template {
t.fields[key] = value
return t
}
func buildOptsToFields(opts Options) Fields {
return Fields{
target: opts.Target,
ext: opts.Ext,
name: opts.Name,
path: opts.Path,
osKey: opts.Goos,
arch: opts.Goarch,
}
}
// Bool Apply the given string, and converts it to a bool.
func (t *Template) Bool(s string) (bool, error) {
r, err := t.Apply(s)
return strings.TrimSpace(strings.ToLower(r)) == "true", err
}
// Apply applies the given string against the Fields stored in the template.
func (t *Template) Apply(s string) (string, error) {
var out bytes.Buffer
tmpl, err := template.New("tmpl").
Option("missingkey=error").
Funcs(template.FuncMap{
"replace": strings.ReplaceAll,
"split": strings.Split,
"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,
}).
Parse(s)
if err != nil {
return "", newTmplError(s, err)
}
err = tmpl.Execute(&out, t.fields)
return out.String(), newTmplError(s, err)
}
// ApplyAll applies all the given strings against the Fields stored in the
// template. Application stops as soon as an error is encountered.
func (t *Template) ApplyAll(in ...string) ([]string, error) {
out := make([]string, len(in))
for i, sp := range in {
r, err := t.Apply(sp)
if err != nil {
return out, newTmplError(sp, err)
}
out[i] = r
}
return out, nil
}
func (t *Template) envOrDefault(name, value string) string {
s, ok := t.fields[env].(Env)[name]
if !ok {
return value
}
return s
}
type ExpectedSingleEnvErr struct{}
func (e ExpectedSingleEnvErr) Error() string {
return "expected {{ .Env.VAR_NAME }} only (no plain-text or other interpolation)"
}
// ApplySingleEnvOnly enforces template to only contain a single environment variable
// and nothing else.
func (t *Template) ApplySingleEnvOnly(s string) (string, error) {
s = strings.TrimSpace(s)
if len(s) == 0 {
return "", nil
}
// text/template/parse (lexer) could be used here too,
// but regexp reduces the complexity and should be sufficient,
// given the context is mostly discouraging users from bad practice
// of hard-coded credentials, rather than catch all possible cases
envOnlyRe := regexp.MustCompile(`^{{\s*\.Env\.[^.\s}]+\s*}}$`)
if !envOnlyRe.Match([]byte(s)) {
return "", ExpectedSingleEnvErr{}
}
var out bytes.Buffer
tmpl, err := template.New("tmpl").
Option("missingkey=error").
Parse(s)
if err != nil {
return "", err
}
err = tmpl.Execute(&out, t.fields)
return out.String(), err
}
func incMajor(v string) string {
return prefix(v) + semver.MustParse(v).IncMajor().String()
}
func incMinor(v string) string {
return prefix(v) + semver.MustParse(v).IncMinor().String()
}
func incPatch(v string) string {
return prefix(v) + semver.MustParse(v).IncPatch().String()
}
func prefix(v string) string {
if v != "" && v[0] == 'v' {
return "v"
}
return ""
}
func filter(reverse bool) func(content, exp string) string {
return func(content, exp string) string {
re := regexp.MustCompilePOSIX(exp)
var lines []string
for _, line := range strings.Split(content, "\n") {
if reverse && re.MatchString(line) {
continue
}
if !reverse && !re.MatchString(line) {
continue
}
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}
}
func mdv2Escape(s string) string {
return strings.NewReplacer(
"_", "\\_",
"*", "\\*",
"[", "\\[",
"]", "\\]",
"(", "\\(",
")", "\\)",
"~", "\\~",
"`", "\\`",
">", "\\>",
"#", "\\#",
"+", "\\+",
"-", "\\-",
"=", "\\=",
"|", "\\|",
"{", "\\{",
"}", "\\}",
".", "\\.",
"!", "\\!",
).Replace(s)
}
Go
1
https://gitee.com/h79/goutils.git
git@gitee.com:h79/goutils.git
h79
goutils
goutils
v1.20.57

搜索帮助

53164aa7 5694891 3bd8fe86 5694891