Skip to content

Commit 776dbd4

Browse files
authored
Merge pull request #3198 from rrjjvv/var-typing-no-value-fix
Consider typed, value-less variables to have `null` value
2 parents 75f1d5e + 291c353 commit 776dbd4

File tree

2 files changed

+78
-35
lines changed

2 files changed

+78
-35
lines changed

bake/hcl_test.go

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,63 @@ func TestHCLNullVariables(t *testing.T) {
423423
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["foo"])
424424
}
425425

426+
func TestHCLTypedNullVariables(t *testing.T) {
427+
types := []string{
428+
"any",
429+
"string", "number", "bool",
430+
"list(string)", "set(string)", "map(string)",
431+
"tuple([string])", "object({val: string})",
432+
}
433+
for _, varType := range types {
434+
tName := fmt.Sprintf("variable typed %q with null default remains null", varType)
435+
t.Run(tName, func(t *testing.T) {
436+
dt := fmt.Sprintf(`
437+
variable "FOO" {
438+
type = %s
439+
default = null
440+
}
441+
442+
target "default" {
443+
args = {
444+
foo = equal(FOO, null)
445+
}
446+
}`, varType)
447+
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
448+
require.NoError(t, err)
449+
require.Equal(t, 1, len(c.Targets))
450+
require.Equal(t, "true", *c.Targets[0].Args["foo"])
451+
})
452+
}
453+
}
454+
455+
func TestHCLTypedValuelessVariables(t *testing.T) {
456+
types := []string{
457+
"any",
458+
"string", "number", "bool",
459+
"list(string)", "set(string)", "map(string)",
460+
"tuple([string])", "object({val: string})",
461+
}
462+
for _, varType := range types {
463+
tName := fmt.Sprintf("variable typed %q with no default is null", varType)
464+
t.Run(tName, func(t *testing.T) {
465+
dt := fmt.Sprintf(`
466+
variable "FOO" {
467+
type = %s
468+
}
469+
470+
target "default" {
471+
args = {
472+
foo = equal(FOO, null)
473+
}
474+
}`, varType)
475+
c, err := ParseFile([]byte(dt), "docker-bake.hcl")
476+
require.NoError(t, err)
477+
require.Equal(t, 1, len(c.Targets))
478+
require.Equal(t, "true", *c.Targets[0].Args["foo"])
479+
})
480+
}
481+
}
482+
426483
func TestJSONNullVariables(t *testing.T) {
427484
dt := []byte(`{
428485
"variable": {
@@ -1565,6 +1622,20 @@ target "two" {
15651622
require.Equal(t, map[string]*string{"b": ptrstr("pre-jkl")}, c.Targets[1].Args)
15661623
}
15671624

1625+
func TestEmptyVariable(t *testing.T) {
1626+
dt := []byte(`
1627+
variable "FOO" {}
1628+
target "default" {
1629+
args = {
1630+
foo = equal(FOO, "")
1631+
}
1632+
}`)
1633+
c, err := ParseFile(dt, "docker-bake.hcl")
1634+
require.NoError(t, err)
1635+
require.Equal(t, 1, len(c.Targets))
1636+
require.Equal(t, "true", *c.Targets[0].Args["foo"])
1637+
}
1638+
15681639
func TestEmptyVariableJSON(t *testing.T) {
15691640
dt := []byte(`{
15701641
"variable": {
@@ -1877,19 +1948,6 @@ func TestTypedVarOverrides(t *testing.T) {
18771948
override: `"hello"`,
18781949
wantValue: `"hello"`,
18791950
},
1880-
{
1881-
name: "any",
1882-
varType: "any",
1883-
override: "[1,2]",
1884-
wantValue: "[1,2]",
1885-
},
1886-
{
1887-
name: "any never convert to complex types",
1888-
varType: "any",
1889-
override: "[1,2]",
1890-
argValue: "length(FOO)",
1891-
wantErrorMsg: "collection must be a list",
1892-
},
18931951
{
18941952
name: "proper CSV list of strings",
18951953
varType: "list(string)",
@@ -2090,19 +2148,6 @@ func TestTypedVarOverrides_JSON(t *testing.T) {
20902148
override: `"hello"`,
20912149
wantValue: "hello",
20922150
},
2093-
{
2094-
name: "any",
2095-
varType: "any",
2096-
override: "[1,2]",
2097-
wantValue: "[1,2]",
2098-
},
2099-
{
2100-
name: "any never convert to complex types",
2101-
varType: "any",
2102-
override: "[1,2]",
2103-
argValue: "length(FOO)",
2104-
wantErrorMsg: "collection must be a list",
2105-
},
21062151
{
21072152
name: "list of strings",
21082153
varType: "list(string)",
@@ -2313,6 +2358,7 @@ func TestJSONOverridePriority(t *testing.T) {
23132358
dt := []byte(`
23142359
variable "foo" {
23152360
type = number
2361+
default = 101
23162362
}
23172363
23182364
target "default" {
@@ -2325,8 +2371,7 @@ func TestJSONOverridePriority(t *testing.T) {
23252371
c, err := ParseFile(dt, "docker-bake.hcl")
23262372
require.NoError(t, err)
23272373
require.Equal(t, 1, len(c.Targets))
2328-
// a variable with no value has always resulted in an empty string
2329-
require.Equal(t, "", *c.Targets[0].Args["bar"])
2374+
require.Equal(t, "101", *c.Targets[0].Args["bar"])
23302375

23312376
t.Setenv("foo_JSON", "42")
23322377
c, err = ParseFile(dt, "docker-bake.hcl")

bake/hclparser/hclparser.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
282282
}
283283

284284
var diags hcl.Diagnostics
285-
varType := cty.DynamicPseudoType
285+
varType, typeSpecified := cty.DynamicPseudoType, false
286286
def, ok := p.attrs[name]
287287
if !ok {
288288
vr, ok := p.vars[name]
@@ -295,12 +295,13 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
295295
if diags.HasErrors() {
296296
return diags
297297
}
298+
typeSpecified = !varType.Equals(cty.DynamicPseudoType) || hcl.ExprAsKeyword(vr.Type) == "any"
298299
}
299300

300301
if def == nil {
301-
// lack of specified value is considered to have an empty string value,
302-
// but any overrides get type checked
303-
if _, ok, _ := p.valueHasOverride(name, false); !ok {
302+
// Lack of specified value, when untyped is considered to have an empty string value.
303+
// A typed variable with no value will result in (typed) nil.
304+
if _, ok, _ := p.valueHasOverride(name, false); !ok && !typeSpecified {
304305
vv := cty.StringVal("")
305306
v = &vv
306307
return
@@ -322,9 +323,6 @@ func (p *parser) resolveValue(ectx *hcl.EvalContext, name string) (err error) {
322323
}
323324
}
324325

325-
// Not entirely true... this doesn't differentiate between a user that specified 'any'
326-
// and a user that specified nothing. But the result is the same; both are treated as strings.
327-
typeSpecified := !varType.Equals(cty.DynamicPseudoType)
328326
envv, hasEnv, jsonEnv := p.valueHasOverride(name, typeSpecified)
329327
_, isVar := p.vars[name]
330328

0 commit comments

Comments
 (0)