in load.go [120:254]
func (s *Spec) SubstituteArgs(env map[string]string, opts ...SubstituteOpt) error {
var cfg SubstituteConfig
cfg.AllowArg = DisallowAllUndeclared
for _, o := range opts {
o(&cfg)
}
lex := shell.NewLex('\\')
// force the shell lexer to skip unresolved env vars so they aren't
// replaced with ""
lex.SkipUnsetEnv = true
var errs []error
appendErr := func(err error) {
errs = append(errs, err)
}
args := make(map[string]string)
for k, v := range s.Args {
args[k] = v
}
for k, v := range env {
if _, ok := args[k]; !ok {
if !knownArg(k) && !cfg.AllowArg(k) {
appendErr(fmt.Errorf("%w: %q", errUnknownArg, k))
}
// if the build arg isn't present in args by opt-in, skip
// and don't automatically inject a value
continue
}
args[k] = v
}
for name, src := range s.Sources {
if err := src.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrapf(err, "source %q", name))
}
s.Sources[name] = src
}
for src, patchList := range s.Patches {
for i, patch := range patchList {
updated, err := expandArgs(lex, patch.Path, args, cfg.AllowArg)
if err != nil {
appendErr(errors.Wrapf(err, "patch %s path %d", src, i))
}
s.Patches[src][i].Path = updated
}
}
updated, err := expandArgs(lex, s.Version, args, cfg.AllowArg)
if err != nil {
appendErr(errors.Wrap(err, "version"))
}
s.Version = updated
updated, err = expandArgs(lex, s.Revision, args, cfg.AllowArg)
if err != nil {
appendErr(errors.Wrap(err, "revision"))
}
s.Revision = updated
if err := s.Build.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrap(err, "build"))
}
if s.Build.NetworkMode != "" {
updated, err := expandArgs(lex, s.Build.NetworkMode, args, cfg.AllowArg)
if err != nil {
appendErr(fmt.Errorf("error performing shell expansion on build network mode: %s: %w", s.Build.NetworkMode, err))
}
s.Build.NetworkMode = updated
}
for i, step := range s.Build.Steps {
bs := &step
if err := bs.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrapf(err, "step index %d", i))
}
s.Build.Steps[i] = *bs
}
for _, t := range s.Tests {
if err := t.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(err)
}
}
for name, t := range s.Targets {
if err := t.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrapf(err, "target %s", name))
}
s.Targets[name] = t
}
if s.PackageConfig != nil {
if err := s.PackageConfig.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrap(err, "package config"))
}
}
if err := s.Image.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrap(err, "package config"))
}
if err := s.Dependencies.processBuildArgs(lex, args, cfg.AllowArg); err != nil {
appendErr(errors.Wrap(err, "dependencies"))
}
for k, v := range s.Provides {
for i, ver := range v.Version {
updated, err := expandArgs(lex, ver, args, cfg.AllowArg)
if err != nil {
appendErr(errors.Wrapf(err, "provides %s version %d", k, i))
}
s.Provides[k].Version[i] = updated
}
}
for k, v := range s.Replaces {
for i, ver := range v.Version {
updated, err := expandArgs(lex, ver, args, cfg.AllowArg)
if err != nil {
appendErr(errors.Wrapf(err, "replaces %s version %d", k, i))
}
s.Replaces[k].Version[i] = updated
}
}
return goerrors.Join(errs...)
}