Files
sofmani/installer/template.go

150 lines
4.8 KiB
Go
Executable File

package installer
import (
"bytes"
"strings"
"text/template"
"github.com/chenasraf/sofmani/logger"
"github.com/chenasraf/sofmani/machine"
"github.com/chenasraf/sofmani/platform"
)
// TemplateVars holds variables available for template replacement.
type TemplateVars struct {
// Tag is the full tag name (e.g., "v1.0.0").
Tag string
// Version is the version without the leading "v" (e.g., "1.0.0").
Version string
// Arch is the system architecture in Go format (e.g., "amd64", "arm64").
Arch string
// ArchAlias is the system architecture in common alias format (e.g., "x86_64", "arm64").
ArchAlias string
// ArchGnu is the system architecture in GNU/Linux format (e.g., "x86_64", "aarch64").
ArchGnu string
// OS is the current operating system (e.g., "macos", "linux", "windows").
OS string
// DeviceID is the unique machine identifier (truncated SHA-256 hash).
DeviceID string
// DeviceIDAlias is the friendly alias for the current machine, if one is defined in machine_aliases.
DeviceIDAlias string
// DownloadFile is the absolute path to the downloaded asset. Only populated for
// github-release custom extract commands.
DownloadFile string
// ExtractDir is the temp directory where a custom extract command should place
// extracted files. Only populated for github-release custom extract commands.
ExtractDir string
// Destination is the final destination directory. Only populated for github-release
// custom extract commands.
Destination string
// BinName is the expected output binary name. Only populated for github-release
// custom extract commands.
BinName string
// ArchiveBinName is the filename sofmani will copy from ExtractDir to Destination
// after the custom extract command finishes. Only populated for github-release
// custom extract commands.
ArchiveBinName string
}
// legacyTokens maps old-style tokens to their TemplateVars field names.
var legacyTokens = map[string]string{
"{tag}": "Tag",
"{version}": "Version",
"{arch}": "Arch",
"{arch_alias}": "ArchAlias",
"{arch_gnu}": "ArchGnu",
"{os}": "OS",
"{device_id}": "DeviceID",
"{device_id_alias}": "DeviceIDAlias",
}
// NewTemplateVars creates a new TemplateVars with the provided tag and current system info.
// The machineAliases parameter is a map of friendly names to machine IDs, used to resolve DeviceIDAlias.
func NewTemplateVars(tag string, machineAliases map[string]string) *TemplateVars {
version, _ := strings.CutPrefix(tag, "v")
deviceID := machine.GetMachineID()
return &TemplateVars{
Tag: tag,
Version: version,
Arch: string(platform.GetArch()),
ArchAlias: platform.GetArchAlias(),
ArchGnu: platform.GetArchGnu(),
OS: string(platform.GetPlatform()),
DeviceID: deviceID,
DeviceIDAlias: resolveDeviceAlias(deviceID, machineAliases),
}
}
// resolveDeviceAlias returns the alias for the given machine ID by reverse-looking up the aliases map.
// Returns an empty string if no alias is found.
func resolveDeviceAlias(machineID string, aliases map[string]string) string {
for alias, id := range aliases {
if id == machineID {
return alias
}
}
return ""
}
// ApplyTemplate applies template variables to a string.
// It supports both Go template syntax (e.g., "{{ .Tag }}") and legacy token syntax (e.g., "{tag}").
// When legacy tokens are detected, a deprecation warning is logged at DEBUG level.
func ApplyTemplate(input string, vars *TemplateVars, installerName string) (string, error) {
result := input
// First, handle legacy token replacement with deprecation warnings
result = applyLegacyTokens(result, vars, installerName)
// Then, handle Go template syntax if present
if strings.Contains(result, "{{") {
tmpl, err := template.New("template").Parse(result)
if err != nil {
return "", err
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, vars)
if err != nil {
return "", err
}
result = buf.String()
}
return result, nil
}
// applyLegacyTokens replaces legacy tokens and logs deprecation warnings.
func applyLegacyTokens(input string, vars *TemplateVars, installerName string) string {
result := input
for token, fieldName := range legacyTokens {
if strings.Contains(result, token) {
logger.Warn(
"Deprecated: installer %q uses legacy token %q. Please migrate to Go template syntax: {{ .%s }}",
installerName, token, fieldName,
)
var value string
switch fieldName {
case "Tag":
value = vars.Tag
case "Version":
value = vars.Version
case "Arch":
value = vars.Arch
case "ArchAlias":
value = vars.ArchAlias
case "ArchGnu":
value = vars.ArchGnu
case "OS":
value = vars.OS
case "DeviceID":
value = vars.DeviceID
case "DeviceIDAlias":
value = vars.DeviceIDAlias
}
result = strings.ReplaceAll(result, token, value)
}
}
return result
}