mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-13 06:57:09 +08:00
bump compose-go to v2.1.4
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
59
vendor/github.com/compose-spec/compose-go/v2/loader/environment.go
generated
vendored
59
vendor/github.com/compose-spec/compose-go/v2/loader/environment.go
generated
vendored
@ -22,9 +22,14 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
|
||||
// Will update the environment variables for the format {- VAR} (without interpolation)
|
||||
// This function should resolve context environment vars for include (passed in env_file)
|
||||
func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails) {
|
||||
// ResolveEnvironment update the environment variables for the format {- VAR} (without interpolation)
|
||||
func ResolveEnvironment(dict map[string]any, environment types.Mapping) {
|
||||
resolveServicesEnvironment(dict, environment)
|
||||
resolveSecretsEnvironment(dict, environment)
|
||||
resolveConfigsEnvironment(dict, environment)
|
||||
}
|
||||
|
||||
func resolveServicesEnvironment(dict map[string]any, environment types.Mapping) {
|
||||
services, ok := dict["services"].(map[string]any)
|
||||
if !ok {
|
||||
return
|
||||
@ -45,7 +50,7 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if found, ok := config.Environment[varEnv]; ok {
|
||||
if found, ok := environment[varEnv]; ok {
|
||||
envs = append(envs, fmt.Sprintf("%s=%s", varEnv, found))
|
||||
} else {
|
||||
// either does not exist or it was already resolved in interpolation
|
||||
@ -57,3 +62,49 @@ func resolveServicesEnvironment(dict map[string]any, config types.ConfigDetails)
|
||||
}
|
||||
dict["services"] = services
|
||||
}
|
||||
|
||||
func resolveSecretsEnvironment(dict map[string]any, environment types.Mapping) {
|
||||
secrets, ok := dict["secrets"].(map[string]any)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for name, cfg := range secrets {
|
||||
secret, ok := cfg.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
env, ok := secret["environment"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if found, ok := environment[env]; ok {
|
||||
secret["content"] = found
|
||||
}
|
||||
secrets[name] = secret
|
||||
}
|
||||
dict["secrets"] = secrets
|
||||
}
|
||||
|
||||
func resolveConfigsEnvironment(dict map[string]any, environment types.Mapping) {
|
||||
configs, ok := dict["configs"].(map[string]any)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for name, cfg := range configs {
|
||||
config, ok := cfg.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
env, ok := config["environment"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if found, ok := environment[env]; ok {
|
||||
config["content"] = found
|
||||
}
|
||||
configs[name] = config
|
||||
}
|
||||
dict["configs"] = configs
|
||||
}
|
||||
|
88
vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
generated
vendored
88
vendor/github.com/compose-spec/compose-go/v2/loader/extends.go
generated
vendored
@ -22,7 +22,11 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/consts"
|
||||
"github.com/compose-spec/compose-go/v2/interpolation"
|
||||
"github.com/compose-spec/compose-go/v2/override"
|
||||
"github.com/compose-spec/compose-go/v2/paths"
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/transform"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
)
|
||||
|
||||
@ -67,25 +71,43 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
||||
)
|
||||
switch v := extends.(type) {
|
||||
case map[string]any:
|
||||
if opts.Interpolate != nil {
|
||||
v, err = interpolation.Interpolate(v, *opts.Interpolate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ref = v["service"].(string)
|
||||
file = v["file"]
|
||||
opts.ProcessEvent("extends", v)
|
||||
case string:
|
||||
if opts.Interpolate != nil {
|
||||
v, err = opts.Interpolate.Substitute(v, template.Mapping(opts.Interpolate.LookupValue))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ref = v
|
||||
opts.ProcessEvent("extends", map[string]any{"service": ref})
|
||||
}
|
||||
|
||||
var base any
|
||||
var (
|
||||
base any
|
||||
processor PostProcessor
|
||||
)
|
||||
|
||||
if file != nil {
|
||||
filename = file.(string)
|
||||
services, err = getExtendsBaseFromFile(ctx, ref, filename, opts, tracker)
|
||||
refFilename := file.(string)
|
||||
services, processor, err = getExtendsBaseFromFile(ctx, name, ref, filename, refFilename, opts, tracker)
|
||||
post = append(post, processor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filename = refFilename
|
||||
} else {
|
||||
_, ok := services[ref]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, filename)
|
||||
return nil, fmt.Errorf("cannot extend service %q in %s: service %q not found", name, filename, ref)
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,47 +143,71 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
|
||||
return merged, nil
|
||||
}
|
||||
|
||||
func getExtendsBaseFromFile(ctx context.Context, name string, path string, opts *Options, ct *cycleTracker) (map[string]any, error) {
|
||||
func getExtendsBaseFromFile(
|
||||
ctx context.Context,
|
||||
name, ref string,
|
||||
path, refPath string,
|
||||
opts *Options,
|
||||
ct *cycleTracker,
|
||||
) (map[string]any, PostProcessor, error) {
|
||||
for _, loader := range opts.ResourceLoaders {
|
||||
if !loader.Accept(path) {
|
||||
if !loader.Accept(refPath) {
|
||||
continue
|
||||
}
|
||||
local, err := loader.Load(ctx, path)
|
||||
local, err := loader.Load(ctx, refPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
localdir := filepath.Dir(local)
|
||||
relworkingdir := loader.Dir(path)
|
||||
relworkingdir := loader.Dir(refPath)
|
||||
|
||||
extendsOpts := opts.clone()
|
||||
// replace localResourceLoader with a new flavour, using extended file base path
|
||||
extendsOpts.ResourceLoaders = append(opts.RemoteResourceLoaders(), localResourceLoader{
|
||||
WorkingDir: localdir,
|
||||
})
|
||||
extendsOpts.ResolvePaths = true
|
||||
extendsOpts.ResolvePaths = false // we do relative path resolution after file has been loaded
|
||||
extendsOpts.SkipNormalization = true
|
||||
extendsOpts.SkipConsistencyCheck = true
|
||||
extendsOpts.SkipInclude = true
|
||||
extendsOpts.SkipExtends = true // we manage extends recursively based on raw service definition
|
||||
extendsOpts.SkipValidation = true // we validate the merge result
|
||||
extendsOpts.SkipDefaultValues = true
|
||||
source, err := loadYamlModel(ctx, types.ConfigDetails{
|
||||
WorkingDir: relworkingdir,
|
||||
ConfigFiles: []types.ConfigFile{
|
||||
{Filename: local},
|
||||
},
|
||||
}, extendsOpts, ct, nil)
|
||||
source, processor, err := loadYamlFile(ctx, types.ConfigFile{Filename: local},
|
||||
extendsOpts, relworkingdir, nil, ct, map[string]any{}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
services := source["services"].(map[string]any)
|
||||
_, ok := services[name]
|
||||
_, ok := services[ref]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot extend service %q in %s: service not found", name, path)
|
||||
return nil, nil, fmt.Errorf(
|
||||
"cannot extend service %q in %s: service %q not found in %s",
|
||||
name,
|
||||
path,
|
||||
ref,
|
||||
refPath,
|
||||
)
|
||||
}
|
||||
return services, nil
|
||||
|
||||
// Attempt to make a canonical model so ResolveRelativePaths can operate on source:target short syntaxes
|
||||
source, err = transform.Canonical(source, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var remotes []paths.RemoteResource
|
||||
for _, loader := range opts.RemoteResourceLoaders() {
|
||||
remotes = append(remotes, loader.Accept)
|
||||
}
|
||||
err = paths.ResolveRelativePaths(source, relworkingdir, remotes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return services, processor, nil
|
||||
}
|
||||
return nil, fmt.Errorf("cannot read %s", path)
|
||||
return nil, nil, fmt.Errorf("cannot read %s", refPath)
|
||||
}
|
||||
|
||||
func deepClone(value any) any {
|
||||
|
27
vendor/github.com/compose-spec/compose-go/v2/loader/include.go
generated
vendored
27
vendor/github.com/compose-spec/compose-go/v2/loader/include.go
generated
vendored
@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
// loadIncludeConfig parse the required config from raw yaml
|
||||
func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
|
||||
func loadIncludeConfig(source any, options *Options) ([]types.IncludeConfig, error) {
|
||||
if source == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@ -45,21 +45,32 @@ func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if options.Interpolate != nil {
|
||||
for i, config := range configs {
|
||||
interpolated, err := interp.Interpolate(config.(map[string]any), *options.Interpolate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configs[i] = interpolated
|
||||
}
|
||||
}
|
||||
|
||||
var requires []types.IncludeConfig
|
||||
err := Transform(source, &requires)
|
||||
return requires, err
|
||||
}
|
||||
|
||||
func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options, included []string) error {
|
||||
includeConfig, err := loadIncludeConfig(model["include"])
|
||||
func ApplyInclude(ctx context.Context, workingDir string, environment types.Mapping, model map[string]any, options *Options, included []string) error {
|
||||
includeConfig, err := loadIncludeConfig(model["include"], options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range includeConfig {
|
||||
for _, listener := range options.Listeners {
|
||||
listener("include", map[string]any{
|
||||
"path": r.Path,
|
||||
"workingdir": configDetails.WorkingDir,
|
||||
"workingdir": workingDir,
|
||||
})
|
||||
}
|
||||
|
||||
@ -83,7 +94,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||
r.ProjectDirectory = filepath.Dir(path)
|
||||
case !filepath.IsAbs(r.ProjectDirectory):
|
||||
relworkingdir = loader.Dir(r.ProjectDirectory)
|
||||
r.ProjectDirectory = filepath.Join(configDetails.WorkingDir, r.ProjectDirectory)
|
||||
r.ProjectDirectory = filepath.Join(workingDir, r.ProjectDirectory)
|
||||
|
||||
default:
|
||||
relworkingdir = r.ProjectDirectory
|
||||
@ -117,7 +128,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||
envFile := []string{}
|
||||
for _, f := range r.EnvFile {
|
||||
if !filepath.IsAbs(f) {
|
||||
f = filepath.Join(configDetails.WorkingDir, f)
|
||||
f = filepath.Join(workingDir, f)
|
||||
s, err := os.Stat(f)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -131,7 +142,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||
r.EnvFile = envFile
|
||||
}
|
||||
|
||||
envFromFile, err := dotenv.GetEnvFromFile(configDetails.Environment, r.EnvFile)
|
||||
envFromFile, err := dotenv.GetEnvFromFile(environment, r.EnvFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -139,7 +150,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
|
||||
config := types.ConfigDetails{
|
||||
WorkingDir: relworkingdir,
|
||||
ConfigFiles: types.ToConfigFiles(r.Path),
|
||||
Environment: configDetails.Environment.Clone().Merge(envFromFile),
|
||||
Environment: environment.Clone().Merge(envFromFile),
|
||||
}
|
||||
loadOptions.Interpolate = &interp.Options{
|
||||
Substitute: options.Interpolate.Substitute,
|
||||
|
193
vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
generated
vendored
193
vendor/github.com/compose-spec/compose-go/v2/loader/loader.go
generated
vendored
@ -89,7 +89,7 @@ var versionWarning []string
|
||||
|
||||
func (o *Options) warnObsoleteVersion(file string) {
|
||||
if !slices.Contains(versionWarning, file) {
|
||||
logrus.Warning(fmt.Sprintf("%s: `version` is obsolete", file))
|
||||
logrus.Warning(fmt.Sprintf("%s: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion", file))
|
||||
}
|
||||
versionWarning = append(versionWarning, file)
|
||||
}
|
||||
@ -358,100 +358,19 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||
dict = map[string]interface{}{}
|
||||
err error
|
||||
)
|
||||
workingDir, environment := config.WorkingDir, config.Environment
|
||||
|
||||
for _, file := range config.ConfigFiles {
|
||||
fctx := context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
|
||||
if file.Content == nil && file.Config == nil {
|
||||
content, err := os.ReadFile(file.Filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file.Content = content
|
||||
dict, _, err = loadYamlFile(ctx, file, opts, workingDir, environment, ct, dict, included)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
processRawYaml := func(raw interface{}, processors ...PostProcessor) error {
|
||||
converted, err := convertToStringKeysRecursive(raw, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, ok := converted.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("Top-level object must be a mapping")
|
||||
}
|
||||
|
||||
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
||||
cfg, err = interp.Interpolate(cfg, *opts.Interpolate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fixEmptyNotNull(cfg)
|
||||
|
||||
if !opts.SkipExtends {
|
||||
err = ApplyExtends(fctx, cfg, opts, ct, processors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, processor := range processors {
|
||||
if err := processor.Apply(dict); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.SkipInclude {
|
||||
included = append(included, config.ConfigFiles[0].Filename)
|
||||
err = ApplyInclude(ctx, config, cfg, opts, included)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dict, err = override.Merge(dict, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dict, err = override.EnforceUnicity(dict)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !opts.SkipValidation {
|
||||
if err := schema.Validate(dict); err != nil {
|
||||
return fmt.Errorf("validating %s: %w", file.Filename, err)
|
||||
}
|
||||
if _, ok := dict["version"]; ok {
|
||||
opts.warnObsoleteVersion(file.Filename)
|
||||
delete(dict, "version")
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if file.Config == nil {
|
||||
r := bytes.NewReader(file.Content)
|
||||
decoder := yaml.NewDecoder(r)
|
||||
for {
|
||||
var raw interface{}
|
||||
processor := &ResetProcessor{target: &raw}
|
||||
err := decoder.Decode(processor)
|
||||
if err != nil && errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := processRawYaml(raw, processor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := processRawYaml(file.Config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.Interpolate != nil && !opts.SkipInterpolation {
|
||||
dict, err = interp.Interpolate(dict, *opts.Interpolate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,7 +379,6 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Canonical transformation can reveal duplicates, typically as ports can be a range and conflict with an override
|
||||
dict, err = override.EnforceUnicity(dict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -489,11 +407,98 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
resolveServicesEnvironment(dict, config)
|
||||
ResolveEnvironment(dict, config.Environment)
|
||||
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
func loadYamlFile(ctx context.Context, file types.ConfigFile, opts *Options, workingDir string, environment types.Mapping, ct *cycleTracker, dict map[string]interface{}, included []string) (map[string]interface{}, PostProcessor, error) {
|
||||
ctx = context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
|
||||
if file.Content == nil && file.Config == nil {
|
||||
content, err := os.ReadFile(file.Filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
file.Content = content
|
||||
}
|
||||
|
||||
processRawYaml := func(raw interface{}, processors ...PostProcessor) error {
|
||||
converted, err := convertToStringKeysRecursive(raw, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, ok := converted.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("Top-level object must be a mapping")
|
||||
}
|
||||
|
||||
fixEmptyNotNull(cfg)
|
||||
|
||||
if !opts.SkipExtends {
|
||||
err = ApplyExtends(ctx, cfg, opts, ct, processors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, processor := range processors {
|
||||
if err := processor.Apply(dict); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.SkipInclude {
|
||||
included = append(included, file.Filename)
|
||||
err = ApplyInclude(ctx, workingDir, environment, cfg, opts, included)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dict, err = override.Merge(dict, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !opts.SkipValidation {
|
||||
if err := schema.Validate(dict); err != nil {
|
||||
return fmt.Errorf("validating %s: %w", file.Filename, err)
|
||||
}
|
||||
if _, ok := dict["version"]; ok {
|
||||
opts.warnObsoleteVersion(file.Filename)
|
||||
delete(dict, "version")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var processor PostProcessor
|
||||
if file.Config == nil {
|
||||
r := bytes.NewReader(file.Content)
|
||||
decoder := yaml.NewDecoder(r)
|
||||
for {
|
||||
var raw interface{}
|
||||
reset := &ResetProcessor{target: &raw}
|
||||
err := decoder.Decode(reset)
|
||||
if err != nil && errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
processor = reset
|
||||
if err := processRawYaml(raw, processor); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := processRawYaml(file.Config); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return dict, processor, nil
|
||||
}
|
||||
|
||||
func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options, loaded []string) (map[string]interface{}, error) {
|
||||
mainFile := configDetails.ConfigFiles[0].Filename
|
||||
for _, f := range loaded {
|
||||
|
13
vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go
generated
vendored
13
vendor/github.com/compose-spec/compose-go/v2/loader/normalize.go
generated
vendored
@ -52,14 +52,14 @@ func Normalize(dict map[string]any, env types.Mapping) (map[string]any, error) {
|
||||
}
|
||||
|
||||
if a, ok := build["args"]; ok {
|
||||
build["args"], _ = resolve(a, fn)
|
||||
build["args"], _ = resolve(a, fn, false)
|
||||
}
|
||||
|
||||
service["build"] = build
|
||||
}
|
||||
|
||||
if e, ok := service["environment"]; ok {
|
||||
service["environment"], _ = resolve(e, fn)
|
||||
service["environment"], _ = resolve(e, fn, true)
|
||||
}
|
||||
|
||||
var dependsOn map[string]any
|
||||
@ -178,12 +178,12 @@ func normalizeNetworks(dict map[string]any) {
|
||||
}
|
||||
}
|
||||
|
||||
func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
||||
func resolve(a any, fn func(s string) (string, bool), keepEmpty bool) (any, bool) {
|
||||
switch v := a.(type) {
|
||||
case []any:
|
||||
var resolved []any
|
||||
for _, val := range v {
|
||||
if r, ok := resolve(val, fn); ok {
|
||||
if r, ok := resolve(val, fn, keepEmpty); ok {
|
||||
resolved = append(resolved, r)
|
||||
}
|
||||
}
|
||||
@ -197,6 +197,8 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
||||
}
|
||||
if s, ok := fn(key); ok {
|
||||
resolved[key] = s
|
||||
} else if keepEmpty {
|
||||
resolved[key] = nil
|
||||
}
|
||||
}
|
||||
return resolved, true
|
||||
@ -205,6 +207,9 @@ func resolve(a any, fn func(s string) (string, bool)) (any, bool) {
|
||||
if val, ok := fn(v); ok {
|
||||
return fmt.Sprintf("%s=%s", v, val), true
|
||||
}
|
||||
if keepEmpty {
|
||||
return v, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
return v, true
|
||||
|
Reference in New Issue
Block a user