mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 09:17:49 +08:00
bake: avoid early-exit for resolution failures
With changes made to allow lazy evaluation, we were early exiting if an undefined name was detected, either for a variable or a function. This had two key implications: 1. The error messages changed, and became significantly less informative. For example, we went from: > Unknown variable; There is no variable named "FO". Did you mean "FOO"?, and 1 other diagnostic(s) To > Invalid expression; undefined variable "FO" 2. Any issues in our function detection from funcCalls which cause JSON functions to be erroneously detected cause invalid functions to be resolved, which causes new name resolution errors. To avoid the above problems, we can defer the error from an undefined name until HCL evaluation - which produces the more informative errors, and does not suffer from incorrectly detecting JSON functions. Signed-off-by: Justin Chadwell <me@jedevc.com> (cherry picked from commit dc8a2b03987f1266ace9a7cdcba92f5e4ccdb8ed)
This commit is contained in:
parent
582cc04be6
commit
cc87bd104e
@ -670,6 +670,24 @@ func TestJSONFunctions(t *testing.T) {
|
||||
require.Equal(t, ptrstr("pre-<FOO-abc>"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
func TestJSONInvalidFunctions(t *testing.T) {
|
||||
dt := []byte(`{
|
||||
"target": {
|
||||
"app": {
|
||||
"args": {
|
||||
"v1": "myfunc(\"foo\")"
|
||||
}
|
||||
}
|
||||
}}`)
|
||||
|
||||
c, err := ParseFile(dt, "docker-bake.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, ptrstr(`myfunc("foo")`), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
func TestHCLFunctionInAttr(t *testing.T) {
|
||||
dt := []byte(`
|
||||
function "brace" {
|
||||
|
@ -62,7 +62,9 @@ type parser struct {
|
||||
doneB map[*hcl.Block]map[string]struct{}
|
||||
}
|
||||
|
||||
func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.Diagnostics {
|
||||
var errUndefined = errors.New("undefined")
|
||||
|
||||
func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}, allowMissing bool) hcl.Diagnostics {
|
||||
fns, hcldiags := funcCalls(exp)
|
||||
if hcldiags.HasErrors() {
|
||||
return hcldiags
|
||||
@ -70,6 +72,9 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.D
|
||||
|
||||
for _, fn := range fns {
|
||||
if err := p.resolveFunction(fn); err != nil {
|
||||
if allowMissing && errors.Is(err, errUndefined) {
|
||||
continue
|
||||
}
|
||||
return hcl.Diagnostics{
|
||||
&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
@ -128,6 +133,9 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.D
|
||||
}
|
||||
}
|
||||
if err := p.resolveBlock(blocks[0], target); err != nil {
|
||||
if allowMissing && errors.Is(err, errUndefined) {
|
||||
continue
|
||||
}
|
||||
return hcl.Diagnostics{
|
||||
&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
@ -140,6 +148,9 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.D
|
||||
}
|
||||
} else {
|
||||
if err := p.resolveValue(v.RootName()); err != nil {
|
||||
if allowMissing && errors.Is(err, errUndefined) {
|
||||
continue
|
||||
}
|
||||
return hcl.Diagnostics{
|
||||
&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
@ -167,7 +178,7 @@ func (p *parser) resolveFunction(name string) error {
|
||||
if _, ok := p.ectx.Functions[name]; ok {
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("undefined function %s", name)
|
||||
return errors.Wrapf(errUndefined, "function %q does not exit", name)
|
||||
}
|
||||
if _, ok := p.progressF[name]; ok {
|
||||
return errors.Errorf("function cycle not allowed for %s", name)
|
||||
@ -217,7 +228,7 @@ func (p *parser) resolveFunction(name string) error {
|
||||
return diags
|
||||
}
|
||||
|
||||
if diags := p.loadDeps(f.Result.Expr, params); diags.HasErrors() {
|
||||
if diags := p.loadDeps(f.Result.Expr, params, false); diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
@ -255,7 +266,7 @@ func (p *parser) resolveValue(name string) (err error) {
|
||||
if _, builtin := p.opt.Vars[name]; !ok && !builtin {
|
||||
vr, ok := p.vars[name]
|
||||
if !ok {
|
||||
return errors.Errorf("undefined variable %q", name)
|
||||
return errors.Wrapf(errUndefined, "variable %q does not exit", name)
|
||||
}
|
||||
def = vr.Default
|
||||
}
|
||||
@ -270,7 +281,7 @@ func (p *parser) resolveValue(name string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if diags := p.loadDeps(def.Expr, nil); diags.HasErrors() {
|
||||
if diags := p.loadDeps(def.Expr, nil, true); diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
vv, diags := def.Expr.Value(p.ectx)
|
||||
@ -395,7 +406,7 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
|
||||
return diag
|
||||
}
|
||||
for _, a := range content.Attributes {
|
||||
diag := p.loadDeps(a.Expr, nil)
|
||||
diag := p.loadDeps(a.Expr, nil, true)
|
||||
if diag.HasErrors() {
|
||||
return diag
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user