Files
kforge/pkg/interpolate/interpolate.go
T
nate.lubitz 532b912ffb
Build and Deploy / build-and-deploy (push) Failing after 20s
add tool to gitea
2026-06-05 02:34:00 +10:00

122 lines
3.0 KiB
Go

// Package interpolate resolves ${token} references in kforge.yml
// string values at generation time.
//
// Built-in tokens:
//
// ${name} meta.name
// ${tenant} meta.tenant
// ${env} current environment key
// ${env_prefix} short prefix (e.g. "prod")
// ${full_name} ${env_prefix}-${tenant}-${name}
// ${image_tag} resolved image tag
// ${namespace} resolved namespace
//
// Environment variable tokens (e.g. ${KFORGE_NODE_IP}) are
// resolved from the process environment at runtime, which is
// how Gitea Actions secrets are injected.
package interpolate
import (
"os"
"strings"
)
// Tokens is a map of token name → value used for interpolation.
type Tokens map[string]string
// FromEnvironment builds the standard token map for a resolved
// environment. Pass this to Apply or ApplyToMap.
func FromEnvironment(
name, tenant, envKey, envPrefix, fullName, imageTag, namespace string,
) Tokens {
return Tokens{
"name": name,
"tenant": tenant,
"env": envKey,
"env_prefix": envPrefix,
"full_name": fullName,
"image_tag": imageTag,
"namespace": namespace,
}
}
// Apply replaces all ${token} references in s with their values.
//
// Resolution order:
// 1. tokens map (built-in kforge tokens)
// 2. Process environment (Gitea/CI secrets)
//
// Unknown tokens are left as-is so they surface as obvious errors
// rather than silently becoming empty strings.
func Apply(s string, tokens Tokens) string {
if !strings.Contains(s, "${") {
return s
}
var b strings.Builder
b.Grow(len(s))
i := 0
for i < len(s) {
start := strings.Index(s[i:], "${")
if start == -1 {
b.WriteString(s[i:])
break
}
b.WriteString(s[i : i+start])
i += start
end := strings.Index(s[i:], "}")
if end == -1 {
// Unclosed brace — write the rest as-is.
b.WriteString(s[i:])
break
}
token := s[i+2 : i+end] // content between ${ and }
i += end + 1
if val, ok := tokens[token]; ok {
b.WriteString(val)
} else if val, ok := os.LookupEnv(token); ok {
b.WriteString(val)
} else {
// Leave unknown tokens intact.
b.WriteString("${")
b.WriteString(token)
b.WriteString("}")
}
}
return b.String()
}
// ApplyToSlice applies interpolation to every string in a slice.
func ApplyToSlice(ss []string, tokens Tokens) []string {
out := make([]string, len(ss))
for i, s := range ss {
out[i] = Apply(s, tokens)
}
return out
}
// Slug converts a string into a safe Kubernetes resource name
// component: lowercase, hyphens, no dots or underscores.
func Slug(s string) string {
var b strings.Builder
for _, r := range strings.ToLower(s) {
switch {
case r >= 'a' && r <= 'z', r >= '0' && r <= '9', r == '-':
b.WriteRune(r)
case r == '_' || r == '.' || r == ' ':
b.WriteRune('-')
}
}
return strings.Trim(b.String(), "-")
}
// HostSlug produces a safe slug from a hostname for use in
// resource names (e.g. TLS secret names).
// "mtt-ui.natelubitz.com" → "mtt-ui-natelubitz-com"
func HostSlug(hostname string) string {
return Slug(strings.ReplaceAll(hostname, ".", "-"))
}