122 lines
3.0 KiB
Go
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, ".", "-"))
|
|
}
|