feat(build): expand env vars in build config
This commit is contained in:
parent
489e118779
commit
479612fdf7
2 changed files with 189 additions and 1 deletions
|
|
@ -5,6 +5,7 @@ package build
|
|||
import (
|
||||
"iter"
|
||||
|
||||
"dappco.re/go/core"
|
||||
"dappco.re/go/core/build/internal/ax"
|
||||
"dappco.re/go/core/build/pkg/build/signing"
|
||||
"dappco.re/go/core/io"
|
||||
|
|
@ -144,6 +145,10 @@ func LoadConfigAtPath(fs io.Medium, configPath string) (*BuildConfig, error) {
|
|||
// Apply defaults for any missing fields
|
||||
applyDefaults(cfg)
|
||||
|
||||
// Expand environment variables after defaults so overrides can still be
|
||||
// expressed declaratively in config files.
|
||||
cfg.ExpandEnv()
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
|
@ -202,10 +207,117 @@ func applyDefaults(cfg *BuildConfig) {
|
|||
cfg.Targets = defaults.Targets
|
||||
}
|
||||
|
||||
// Expand environment variables in sign config
|
||||
}
|
||||
|
||||
// ExpandEnv expands environment variables across the build config.
|
||||
//
|
||||
// cfg.ExpandEnv() // expands $APP_NAME, $IMAGE_TAG, $GPG_KEY_ID, etc.
|
||||
func (cfg *BuildConfig) ExpandEnv() {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
cfg.Project.Name = expandEnv(cfg.Project.Name)
|
||||
cfg.Project.Description = expandEnv(cfg.Project.Description)
|
||||
cfg.Project.Main = expandEnv(cfg.Project.Main)
|
||||
cfg.Project.Binary = expandEnv(cfg.Project.Binary)
|
||||
|
||||
cfg.Build.Type = expandEnv(cfg.Build.Type)
|
||||
cfg.Build.WebView2 = expandEnv(cfg.Build.WebView2)
|
||||
cfg.Build.ArchiveFormat = expandEnv(cfg.Build.ArchiveFormat)
|
||||
cfg.Build.Dockerfile = expandEnv(cfg.Build.Dockerfile)
|
||||
cfg.Build.Registry = expandEnv(cfg.Build.Registry)
|
||||
cfg.Build.Image = expandEnv(cfg.Build.Image)
|
||||
cfg.Build.LinuxKitConfig = expandEnv(cfg.Build.LinuxKitConfig)
|
||||
|
||||
cfg.Build.Flags = expandEnvSlice(cfg.Build.Flags)
|
||||
cfg.Build.LDFlags = expandEnvSlice(cfg.Build.LDFlags)
|
||||
cfg.Build.BuildTags = expandEnvSlice(cfg.Build.BuildTags)
|
||||
cfg.Build.Env = expandEnvSlice(cfg.Build.Env)
|
||||
cfg.Build.Tags = expandEnvSlice(cfg.Build.Tags)
|
||||
cfg.Build.Formats = expandEnvSlice(cfg.Build.Formats)
|
||||
|
||||
cfg.Build.Cache.Directory = expandEnv(cfg.Build.Cache.Directory)
|
||||
cfg.Build.Cache.KeyPrefix = expandEnv(cfg.Build.Cache.KeyPrefix)
|
||||
cfg.Build.Cache.Paths = expandEnvSlice(cfg.Build.Cache.Paths)
|
||||
cfg.Build.Cache.RestoreKeys = expandEnvSlice(cfg.Build.Cache.RestoreKeys)
|
||||
|
||||
cfg.Build.BuildArgs = expandEnvMap(cfg.Build.BuildArgs)
|
||||
|
||||
cfg.Sign.ExpandEnv()
|
||||
}
|
||||
|
||||
func expandEnvSlice(values []string) []string {
|
||||
if len(values) == 0 {
|
||||
return values
|
||||
}
|
||||
|
||||
result := make([]string, len(values))
|
||||
for i, value := range values {
|
||||
result[i] = expandEnv(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func expandEnvMap(values map[string]string) map[string]string {
|
||||
if len(values) == 0 {
|
||||
return values
|
||||
}
|
||||
|
||||
result := make(map[string]string, len(values))
|
||||
for key, value := range values {
|
||||
result[key] = expandEnv(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// expandEnv expands $VAR or ${VAR} using the current process environment.
|
||||
func expandEnv(s string) string {
|
||||
if !core.Contains(s, "$") {
|
||||
return s
|
||||
}
|
||||
|
||||
buf := core.NewBuilder()
|
||||
for i := 0; i < len(s); {
|
||||
if s[i] != '$' {
|
||||
buf.WriteByte(s[i])
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if i+1 < len(s) && s[i+1] == '{' {
|
||||
j := i + 2
|
||||
for j < len(s) && s[j] != '}' {
|
||||
j++
|
||||
}
|
||||
if j < len(s) {
|
||||
buf.WriteString(core.Env(s[i+2 : j]))
|
||||
i = j + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
j := i + 1
|
||||
for j < len(s) {
|
||||
c := s[j]
|
||||
if c != '_' && (c < '0' || c > '9') && (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') {
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
if j > i+1 {
|
||||
buf.WriteString(core.Env(s[i+1 : j]))
|
||||
i = j
|
||||
continue
|
||||
}
|
||||
|
||||
buf.WriteByte(s[i])
|
||||
i++
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ConfigPath returns the path to the build config file for a given directory.
|
||||
//
|
||||
// path := build.ConfigPath("/home/user/my-project") // → "/home/user/my-project/.core/build.yaml"
|
||||
|
|
|
|||
|
|
@ -84,6 +84,82 @@ targets:
|
|||
assert.Equal(t, "arm64", cfg.Targets[1].Arch)
|
||||
})
|
||||
|
||||
t.Run("expands environment variables in build and signing config", func(t *testing.T) {
|
||||
t.Setenv("APP_NAME", "demo-app")
|
||||
t.Setenv("APP_ROOT", "./cmd/demo")
|
||||
t.Setenv("APP_BINARY", "demo-bin")
|
||||
t.Setenv("BUILD_TYPE", "wails")
|
||||
t.Setenv("WEBVIEW2", "embed")
|
||||
t.Setenv("ARCHIVE_FORMAT", "xz")
|
||||
t.Setenv("APP_VERSION", "v1.2.3")
|
||||
t.Setenv("APP_TAG", "integration")
|
||||
t.Setenv("CACHE_DIR", ".core/cache/demo-app")
|
||||
t.Setenv("DOCKERFILE", "Dockerfile.release")
|
||||
t.Setenv("IMAGE_NAME", "owner/demo-app")
|
||||
t.Setenv("GPG_KEY_ID", "ABCD1234")
|
||||
|
||||
content := `
|
||||
version: 1
|
||||
project:
|
||||
name: ${APP_NAME}
|
||||
main: ${APP_ROOT}
|
||||
binary: ${APP_BINARY}
|
||||
build:
|
||||
type: ${BUILD_TYPE}
|
||||
webview2: ${WEBVIEW2}
|
||||
archive_format: ${ARCHIVE_FORMAT}
|
||||
flags:
|
||||
- -trimpath
|
||||
- -X
|
||||
- main.version=${APP_VERSION}
|
||||
ldflags:
|
||||
- -s
|
||||
- -w
|
||||
build_tags:
|
||||
- ${APP_TAG}
|
||||
env:
|
||||
- VERSION=${APP_VERSION}
|
||||
cache:
|
||||
enabled: true
|
||||
dir: ${CACHE_DIR}
|
||||
paths:
|
||||
- ${CACHE_DIR}/go-build
|
||||
dockerfile: ${DOCKERFILE}
|
||||
image: ${IMAGE_NAME}
|
||||
tags:
|
||||
- latest
|
||||
- ${APP_VERSION}
|
||||
build_args:
|
||||
VERSION: ${APP_VERSION}
|
||||
sign:
|
||||
gpg:
|
||||
key: ${GPG_KEY_ID}
|
||||
`
|
||||
dir := setupConfigTestDir(t, content)
|
||||
|
||||
cfg, err := LoadConfig(fs, dir)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
|
||||
assert.Equal(t, "demo-app", cfg.Project.Name)
|
||||
assert.Equal(t, "./cmd/demo", cfg.Project.Main)
|
||||
assert.Equal(t, "demo-bin", cfg.Project.Binary)
|
||||
assert.Equal(t, "wails", cfg.Build.Type)
|
||||
assert.Equal(t, "embed", cfg.Build.WebView2)
|
||||
assert.Equal(t, "xz", cfg.Build.ArchiveFormat)
|
||||
assert.Equal(t, []string{"-trimpath", "-X", "main.version=v1.2.3"}, cfg.Build.Flags)
|
||||
assert.Equal(t, []string{"-s", "-w"}, cfg.Build.LDFlags)
|
||||
assert.Equal(t, []string{"integration"}, cfg.Build.BuildTags)
|
||||
assert.Equal(t, []string{"VERSION=v1.2.3"}, cfg.Build.Env)
|
||||
assert.Equal(t, ".core/cache/demo-app", cfg.Build.Cache.Directory)
|
||||
assert.Equal(t, []string{".core/cache/demo-app/go-build"}, cfg.Build.Cache.Paths)
|
||||
assert.Equal(t, "Dockerfile.release", cfg.Build.Dockerfile)
|
||||
assert.Equal(t, "owner/demo-app", cfg.Build.Image)
|
||||
assert.Equal(t, []string{"latest", "v1.2.3"}, cfg.Build.Tags)
|
||||
assert.Equal(t, map[string]string{"VERSION": "v1.2.3"}, cfg.Build.BuildArgs)
|
||||
assert.Equal(t, "ABCD1234", cfg.Sign.GPG.Key)
|
||||
})
|
||||
|
||||
t.Run("returns defaults when config file missing", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue