This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// 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, ".", "-"))
|
||||
}
|
||||
Reference in New Issue
Block a user