mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-30 01:15:45 +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>
This commit is contained in:
parent
d9780e27cd
commit
dc8a2b0398
@ -670,6 +670,24 @@ func TestJSONFunctions(t *testing.T) {
|
|||||||
require.Equal(t, ptrstr("pre-<FOO-abc>"), c.Targets[0].Args["v1"])
|
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) {
|
func TestHCLFunctionInAttr(t *testing.T) {
|
||||||
dt := []byte(`
|
dt := []byte(`
|
||||||
function "brace" {
|
function "brace" {
|
||||||
|
@ -62,7 +62,9 @@ type parser struct {
|
|||||||
doneB map[*hcl.Block]map[string]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)
|
fns, hcldiags := funcCalls(exp)
|
||||||
if hcldiags.HasErrors() {
|
if hcldiags.HasErrors() {
|
||||||
return hcldiags
|
return hcldiags
|
||||||
@ -70,6 +72,9 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.D
|
|||||||
|
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
if err := p.resolveFunction(fn); err != nil {
|
if err := p.resolveFunction(fn); err != nil {
|
||||||
|
if allowMissing && errors.Is(err, errUndefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return hcl.Diagnostics{
|
return hcl.Diagnostics{
|
||||||
&hcl.Diagnostic{
|
&hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
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 err := p.resolveBlock(blocks[0], target); err != nil {
|
||||||
|
if allowMissing && errors.Is(err, errUndefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return hcl.Diagnostics{
|
return hcl.Diagnostics{
|
||||||
&hcl.Diagnostic{
|
&hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
@ -140,6 +148,9 @@ func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.D
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := p.resolveValue(v.RootName()); err != nil {
|
if err := p.resolveValue(v.RootName()); err != nil {
|
||||||
|
if allowMissing && errors.Is(err, errUndefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return hcl.Diagnostics{
|
return hcl.Diagnostics{
|
||||||
&hcl.Diagnostic{
|
&hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
@ -167,7 +178,7 @@ func (p *parser) resolveFunction(name string) error {
|
|||||||
if _, ok := p.ectx.Functions[name]; ok {
|
if _, ok := p.ectx.Functions[name]; ok {
|
||||||
return nil
|
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 {
|
if _, ok := p.progressF[name]; ok {
|
||||||
return errors.Errorf("function cycle not allowed for %s", name)
|
return errors.Errorf("function cycle not allowed for %s", name)
|
||||||
@ -217,7 +228,7 @@ func (p *parser) resolveFunction(name string) error {
|
|||||||
return diags
|
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
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +266,7 @@ func (p *parser) resolveValue(name string) (err error) {
|
|||||||
if _, builtin := p.opt.Vars[name]; !ok && !builtin {
|
if _, builtin := p.opt.Vars[name]; !ok && !builtin {
|
||||||
vr, ok := p.vars[name]
|
vr, ok := p.vars[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.Errorf("undefined variable %q", name)
|
return errors.Wrapf(errUndefined, "variable %q does not exit", name)
|
||||||
}
|
}
|
||||||
def = vr.Default
|
def = vr.Default
|
||||||
}
|
}
|
||||||
@ -270,7 +281,7 @@ func (p *parser) resolveValue(name string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if diags := p.loadDeps(def.Expr, nil); diags.HasErrors() {
|
if diags := p.loadDeps(def.Expr, nil, true); diags.HasErrors() {
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
vv, diags := def.Expr.Value(p.ectx)
|
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
|
return diag
|
||||||
}
|
}
|
||||||
for _, a := range content.Attributes {
|
for _, a := range content.Attributes {
|
||||||
diag := p.loadDeps(a.Expr, nil)
|
diag := p.loadDeps(a.Expr, nil, true)
|
||||||
if diag.HasErrors() {
|
if diag.HasErrors() {
|
||||||
return diag
|
return diag
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user