github.com/pdmccormick/importable-docker-buildx@v0.0.0-20240426161518-e47091289030/bake/hcl_test.go (about)

     1  package bake
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  )
     9  
    10  func TestHCLBasic(t *testing.T) {
    11  	t.Parallel()
    12  	dt := []byte(`
    13  		group "default" {
    14  			targets = ["db", "webapp"]
    15  		}
    16  
    17  		target "db" {
    18  			context = "./db"
    19  			tags = ["docker.io/tonistiigi/db"]
    20  		}
    21  
    22  		target "webapp" {
    23  			context = "./dir"
    24  			dockerfile = "Dockerfile-alternate"
    25  			args = {
    26  				buildno = "123"
    27  			}
    28  		}
    29  
    30  		target "cross" {
    31  			platforms = [
    32  				"linux/amd64",
    33  				"linux/arm64"
    34  			]
    35  		}
    36  
    37  		target "webapp-plus" {
    38  			inherits = ["webapp", "cross"]
    39  			args = {
    40  				IAMCROSS = "true"
    41  			}
    42  		}
    43  		`)
    44  
    45  	c, err := ParseFile(dt, "docker-bake.hcl")
    46  	require.NoError(t, err)
    47  	require.Equal(t, 1, len(c.Groups))
    48  	require.Equal(t, "default", c.Groups[0].Name)
    49  	require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
    50  
    51  	require.Equal(t, 4, len(c.Targets))
    52  	require.Equal(t, c.Targets[0].Name, "db")
    53  	require.Equal(t, "./db", *c.Targets[0].Context)
    54  
    55  	require.Equal(t, c.Targets[1].Name, "webapp")
    56  	require.Equal(t, 1, len(c.Targets[1].Args))
    57  	require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
    58  
    59  	require.Equal(t, c.Targets[2].Name, "cross")
    60  	require.Equal(t, 2, len(c.Targets[2].Platforms))
    61  	require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
    62  
    63  	require.Equal(t, c.Targets[3].Name, "webapp-plus")
    64  	require.Equal(t, 1, len(c.Targets[3].Args))
    65  	require.Equal(t, map[string]*string{"IAMCROSS": ptrstr("true")}, c.Targets[3].Args)
    66  }
    67  
    68  func TestHCLBasicInJSON(t *testing.T) {
    69  	dt := []byte(`
    70  		{
    71  			"group": {
    72  				"default": {
    73  					"targets": ["db", "webapp"]
    74  				}
    75  			},
    76  			"target": {
    77  				"db": {
    78  					"context": "./db",
    79  					"tags": ["docker.io/tonistiigi/db"]
    80  				},
    81  				"webapp": {
    82  					"context": "./dir",
    83  					"dockerfile": "Dockerfile-alternate",
    84  					"args": {
    85  						"buildno": "123"
    86  					}
    87  				},
    88  				"cross": {
    89  					"platforms": [
    90  						"linux/amd64",
    91  						"linux/arm64"
    92  					]
    93  				},
    94  				"webapp-plus": {
    95  					"inherits": ["webapp", "cross"],
    96  					"args": {
    97  						"IAMCROSS": "true"
    98  					}
    99  				}
   100  			}
   101  		}
   102  		`)
   103  
   104  	c, err := ParseFile(dt, "docker-bake.json")
   105  	require.NoError(t, err)
   106  
   107  	require.Equal(t, 1, len(c.Groups))
   108  	require.Equal(t, "default", c.Groups[0].Name)
   109  	require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
   110  
   111  	require.Equal(t, 4, len(c.Targets))
   112  	require.Equal(t, c.Targets[0].Name, "db")
   113  	require.Equal(t, "./db", *c.Targets[0].Context)
   114  
   115  	require.Equal(t, c.Targets[1].Name, "webapp")
   116  	require.Equal(t, 1, len(c.Targets[1].Args))
   117  	require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
   118  
   119  	require.Equal(t, c.Targets[2].Name, "cross")
   120  	require.Equal(t, 2, len(c.Targets[2].Platforms))
   121  	require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
   122  
   123  	require.Equal(t, c.Targets[3].Name, "webapp-plus")
   124  	require.Equal(t, 1, len(c.Targets[3].Args))
   125  	require.Equal(t, map[string]*string{"IAMCROSS": ptrstr("true")}, c.Targets[3].Args)
   126  }
   127  
   128  func TestHCLWithFunctions(t *testing.T) {
   129  	dt := []byte(`
   130  		group "default" {
   131  			targets = ["webapp"]
   132  		}
   133  
   134  		target "webapp" {
   135  			args = {
   136  				buildno = "${add(123, 1)}"
   137  			}
   138  		}
   139  		`)
   140  
   141  	c, err := ParseFile(dt, "docker-bake.hcl")
   142  	require.NoError(t, err)
   143  
   144  	require.Equal(t, 1, len(c.Groups))
   145  	require.Equal(t, "default", c.Groups[0].Name)
   146  	require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
   147  
   148  	require.Equal(t, 1, len(c.Targets))
   149  	require.Equal(t, c.Targets[0].Name, "webapp")
   150  	require.Equal(t, ptrstr("124"), c.Targets[0].Args["buildno"])
   151  }
   152  
   153  func TestHCLWithUserDefinedFunctions(t *testing.T) {
   154  	dt := []byte(`
   155  		function "increment" {
   156  			params = [number]
   157  			result = number + 1
   158  		}
   159  
   160  		group "default" {
   161  			targets = ["webapp"]
   162  		}
   163  
   164  		target "webapp" {
   165  			args = {
   166  				buildno = "${increment(123)}"
   167  			}
   168  		}
   169  		`)
   170  
   171  	c, err := ParseFile(dt, "docker-bake.hcl")
   172  	require.NoError(t, err)
   173  
   174  	require.Equal(t, 1, len(c.Groups))
   175  	require.Equal(t, "default", c.Groups[0].Name)
   176  	require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
   177  
   178  	require.Equal(t, 1, len(c.Targets))
   179  	require.Equal(t, c.Targets[0].Name, "webapp")
   180  	require.Equal(t, ptrstr("124"), c.Targets[0].Args["buildno"])
   181  }
   182  
   183  func TestHCLWithVariables(t *testing.T) {
   184  	dt := []byte(`
   185  		variable "BUILD_NUMBER" {
   186  			default = "123"
   187  		}
   188  
   189  		group "default" {
   190  			targets = ["webapp"]
   191  		}
   192  
   193  		target "webapp" {
   194  			args = {
   195  				buildno = "${BUILD_NUMBER}"
   196  			}
   197  		}
   198  		`)
   199  
   200  	c, err := ParseFile(dt, "docker-bake.hcl")
   201  	require.NoError(t, err)
   202  
   203  	require.Equal(t, 1, len(c.Groups))
   204  	require.Equal(t, "default", c.Groups[0].Name)
   205  	require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
   206  
   207  	require.Equal(t, 1, len(c.Targets))
   208  	require.Equal(t, c.Targets[0].Name, "webapp")
   209  	require.Equal(t, ptrstr("123"), c.Targets[0].Args["buildno"])
   210  
   211  	t.Setenv("BUILD_NUMBER", "456")
   212  
   213  	c, err = ParseFile(dt, "docker-bake.hcl")
   214  	require.NoError(t, err)
   215  
   216  	require.Equal(t, 1, len(c.Groups))
   217  	require.Equal(t, "default", c.Groups[0].Name)
   218  	require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
   219  
   220  	require.Equal(t, 1, len(c.Targets))
   221  	require.Equal(t, c.Targets[0].Name, "webapp")
   222  	require.Equal(t, ptrstr("456"), c.Targets[0].Args["buildno"])
   223  }
   224  
   225  func TestHCLWithVariablesInFunctions(t *testing.T) {
   226  	dt := []byte(`
   227  		variable "REPO" {
   228  			default = "user/repo"
   229  		}
   230  		function "tag" {
   231  			params = [tag]
   232  			result = ["${REPO}:${tag}"]
   233  		}
   234  
   235  		target "webapp" {
   236  			tags = tag("v1")
   237  		}
   238  		`)
   239  
   240  	c, err := ParseFile(dt, "docker-bake.hcl")
   241  	require.NoError(t, err)
   242  
   243  	require.Equal(t, 1, len(c.Targets))
   244  	require.Equal(t, c.Targets[0].Name, "webapp")
   245  	require.Equal(t, []string{"user/repo:v1"}, c.Targets[0].Tags)
   246  
   247  	t.Setenv("REPO", "docker/buildx")
   248  
   249  	c, err = ParseFile(dt, "docker-bake.hcl")
   250  	require.NoError(t, err)
   251  
   252  	require.Equal(t, 1, len(c.Targets))
   253  	require.Equal(t, c.Targets[0].Name, "webapp")
   254  	require.Equal(t, []string{"docker/buildx:v1"}, c.Targets[0].Tags)
   255  }
   256  
   257  func TestHCLMultiFileSharedVariables(t *testing.T) {
   258  	dt := []byte(`
   259  		variable "FOO" {
   260  			default = "abc"
   261  		}
   262  		target "app" {
   263  			args = {
   264  				v1 = "pre-${FOO}"
   265  			}
   266  		}
   267  		`)
   268  	dt2 := []byte(`
   269  		target "app" {
   270  			args = {
   271  				v2 = "${FOO}-post"
   272  			}
   273  		}
   274  		`)
   275  
   276  	c, err := ParseFiles([]File{
   277  		{Data: dt, Name: "c1.hcl"},
   278  		{Data: dt2, Name: "c2.hcl"},
   279  	}, nil)
   280  	require.NoError(t, err)
   281  	require.Equal(t, 1, len(c.Targets))
   282  	require.Equal(t, c.Targets[0].Name, "app")
   283  	require.Equal(t, ptrstr("pre-abc"), c.Targets[0].Args["v1"])
   284  	require.Equal(t, ptrstr("abc-post"), c.Targets[0].Args["v2"])
   285  
   286  	t.Setenv("FOO", "def")
   287  
   288  	c, err = ParseFiles([]File{
   289  		{Data: dt, Name: "c1.hcl"},
   290  		{Data: dt2, Name: "c2.hcl"},
   291  	}, nil)
   292  	require.NoError(t, err)
   293  
   294  	require.Equal(t, 1, len(c.Targets))
   295  	require.Equal(t, c.Targets[0].Name, "app")
   296  	require.Equal(t, ptrstr("pre-def"), c.Targets[0].Args["v1"])
   297  	require.Equal(t, ptrstr("def-post"), c.Targets[0].Args["v2"])
   298  }
   299  
   300  func TestHCLVarsWithVars(t *testing.T) {
   301  	dt := []byte(`
   302  		variable "FOO" {
   303  			default = upper("${BASE}def")
   304  		}
   305  		variable "BAR" {
   306  			default = "-${FOO}-"
   307  		}
   308  		target "app" {
   309  			args = {
   310  				v1 = "pre-${BAR}"
   311  			}
   312  		}
   313  		`)
   314  	dt2 := []byte(`
   315  		variable "BASE" {
   316  			default = "abc"
   317  		}
   318  		target "app" {
   319  			args = {
   320  				v2 = "${FOO}-post"
   321  			}
   322  		}
   323  		`)
   324  
   325  	c, err := ParseFiles([]File{
   326  		{Data: dt, Name: "c1.hcl"},
   327  		{Data: dt2, Name: "c2.hcl"},
   328  	}, nil)
   329  	require.NoError(t, err)
   330  	require.Equal(t, 1, len(c.Targets))
   331  	require.Equal(t, c.Targets[0].Name, "app")
   332  	require.Equal(t, ptrstr("pre--ABCDEF-"), c.Targets[0].Args["v1"])
   333  	require.Equal(t, ptrstr("ABCDEF-post"), c.Targets[0].Args["v2"])
   334  
   335  	t.Setenv("BASE", "new")
   336  
   337  	c, err = ParseFiles([]File{
   338  		{Data: dt, Name: "c1.hcl"},
   339  		{Data: dt2, Name: "c2.hcl"},
   340  	}, nil)
   341  	require.NoError(t, err)
   342  
   343  	require.Equal(t, 1, len(c.Targets))
   344  	require.Equal(t, c.Targets[0].Name, "app")
   345  	require.Equal(t, ptrstr("pre--NEWDEF-"), c.Targets[0].Args["v1"])
   346  	require.Equal(t, ptrstr("NEWDEF-post"), c.Targets[0].Args["v2"])
   347  }
   348  
   349  func TestHCLTypedVariables(t *testing.T) {
   350  	dt := []byte(`
   351  		variable "FOO" {
   352  			default = 3
   353  		}
   354  		variable "IS_FOO" {
   355  			default = true
   356  		}
   357  		target "app" {
   358  			args = {
   359  				v1 = FOO > 5 ? "higher" : "lower" 
   360  				v2 = IS_FOO ? "yes" : "no"
   361  			}
   362  		}
   363  		`)
   364  
   365  	c, err := ParseFile(dt, "docker-bake.hcl")
   366  	require.NoError(t, err)
   367  
   368  	require.Equal(t, 1, len(c.Targets))
   369  	require.Equal(t, c.Targets[0].Name, "app")
   370  	require.Equal(t, ptrstr("lower"), c.Targets[0].Args["v1"])
   371  	require.Equal(t, ptrstr("yes"), c.Targets[0].Args["v2"])
   372  
   373  	t.Setenv("FOO", "5.1")
   374  	t.Setenv("IS_FOO", "0")
   375  
   376  	c, err = ParseFile(dt, "docker-bake.hcl")
   377  	require.NoError(t, err)
   378  
   379  	require.Equal(t, 1, len(c.Targets))
   380  	require.Equal(t, c.Targets[0].Name, "app")
   381  	require.Equal(t, ptrstr("higher"), c.Targets[0].Args["v1"])
   382  	require.Equal(t, ptrstr("no"), c.Targets[0].Args["v2"])
   383  
   384  	t.Setenv("FOO", "NaN")
   385  	_, err = ParseFile(dt, "docker-bake.hcl")
   386  	require.Error(t, err)
   387  	require.Contains(t, err.Error(), "failed to parse FOO as number")
   388  
   389  	t.Setenv("FOO", "0")
   390  	t.Setenv("IS_FOO", "maybe")
   391  
   392  	_, err = ParseFile(dt, "docker-bake.hcl")
   393  	require.Error(t, err)
   394  	require.Contains(t, err.Error(), "failed to parse IS_FOO as bool")
   395  }
   396  
   397  func TestHCLNullVariables(t *testing.T) {
   398  	dt := []byte(`
   399  		variable "FOO" {
   400  			default = null
   401  		}
   402  		target "default" {
   403  			args = {
   404  				foo = FOO
   405  			}
   406  		}`)
   407  
   408  	c, err := ParseFile(dt, "docker-bake.hcl")
   409  	require.NoError(t, err)
   410  	require.Equal(t, ptrstr(nil), c.Targets[0].Args["foo"])
   411  
   412  	t.Setenv("FOO", "bar")
   413  	c, err = ParseFile(dt, "docker-bake.hcl")
   414  	require.NoError(t, err)
   415  	require.Equal(t, ptrstr("bar"), c.Targets[0].Args["foo"])
   416  }
   417  
   418  func TestJSONNullVariables(t *testing.T) {
   419  	dt := []byte(`{
   420  		"variable": {
   421  			"FOO": {
   422  				"default": null
   423  			}
   424  		},
   425  		"target": {
   426  			"default": {
   427  				"args": {
   428  					"foo": "${FOO}"
   429  				}
   430  			}
   431  		}
   432  	}`)
   433  
   434  	c, err := ParseFile(dt, "docker-bake.json")
   435  	require.NoError(t, err)
   436  	require.Equal(t, ptrstr(nil), c.Targets[0].Args["foo"])
   437  
   438  	t.Setenv("FOO", "bar")
   439  	c, err = ParseFile(dt, "docker-bake.json")
   440  	require.NoError(t, err)
   441  	require.Equal(t, ptrstr("bar"), c.Targets[0].Args["foo"])
   442  }
   443  
   444  func TestHCLVariableCycle(t *testing.T) {
   445  	dt := []byte(`
   446  		variable "FOO" {
   447  			default = BAR
   448  		}
   449  		variable "FOO2" {
   450  			default = FOO
   451  		}
   452  		variable "BAR" {
   453  			default = FOO
   454  		}
   455  		target "app" {}
   456  		`)
   457  
   458  	_, err := ParseFile(dt, "docker-bake.hcl")
   459  	require.Error(t, err)
   460  	require.Contains(t, err.Error(), "variable cycle not allowed")
   461  }
   462  
   463  func TestHCLAttrs(t *testing.T) {
   464  	dt := []byte(`
   465  		FOO="abc"
   466  		BAR="attr-${FOO}def"
   467  		target "app" {
   468  			args = {
   469  				"v1": BAR
   470  			}
   471  		}
   472  		`)
   473  
   474  	c, err := ParseFile(dt, "docker-bake.hcl")
   475  	require.NoError(t, err)
   476  
   477  	require.Equal(t, 1, len(c.Targets))
   478  	require.Equal(t, c.Targets[0].Name, "app")
   479  	require.Equal(t, ptrstr("attr-abcdef"), c.Targets[0].Args["v1"])
   480  
   481  	// env does not apply if no variable
   482  	t.Setenv("FOO", "bar")
   483  	c, err = ParseFile(dt, "docker-bake.hcl")
   484  	require.NoError(t, err)
   485  
   486  	require.Equal(t, 1, len(c.Targets))
   487  	require.Equal(t, c.Targets[0].Name, "app")
   488  	require.Equal(t, ptrstr("attr-abcdef"), c.Targets[0].Args["v1"])
   489  	// attr-multifile
   490  }
   491  
   492  func TestHCLTargetAttrs(t *testing.T) {
   493  	dt := []byte(`
   494  		target "foo" {
   495  			dockerfile = "xxx"
   496  			context = target.bar.context
   497  			target = target.foo.dockerfile
   498  		}
   499  		
   500  		target "bar" {
   501  			dockerfile = target.foo.dockerfile
   502  			context = "yyy"
   503  			target = target.bar.context
   504  		}
   505  		`)
   506  
   507  	c, err := ParseFile(dt, "docker-bake.hcl")
   508  	require.NoError(t, err)
   509  
   510  	require.Equal(t, 2, len(c.Targets))
   511  	require.Equal(t, "foo", c.Targets[0].Name)
   512  	require.Equal(t, "bar", c.Targets[1].Name)
   513  
   514  	require.Equal(t, "xxx", *c.Targets[0].Dockerfile)
   515  	require.Equal(t, "yyy", *c.Targets[0].Context)
   516  	require.Equal(t, "xxx", *c.Targets[0].Target)
   517  
   518  	require.Equal(t, "xxx", *c.Targets[1].Dockerfile)
   519  	require.Equal(t, "yyy", *c.Targets[1].Context)
   520  	require.Equal(t, "yyy", *c.Targets[1].Target)
   521  }
   522  
   523  func TestHCLTargetGlobal(t *testing.T) {
   524  	dt := []byte(`
   525  		target "foo" {
   526  			dockerfile = "x"
   527  		}
   528  		x = target.foo.dockerfile
   529  		y = x
   530  		target "bar" {
   531  			dockerfile = y
   532  		}
   533  		`)
   534  
   535  	c, err := ParseFile(dt, "docker-bake.hcl")
   536  	require.NoError(t, err)
   537  
   538  	require.Equal(t, 2, len(c.Targets))
   539  	require.Equal(t, "foo", c.Targets[0].Name)
   540  	require.Equal(t, "bar", c.Targets[1].Name)
   541  
   542  	require.Equal(t, "x", *c.Targets[0].Dockerfile)
   543  	require.Equal(t, "x", *c.Targets[1].Dockerfile)
   544  }
   545  
   546  func TestHCLTargetAttrName(t *testing.T) {
   547  	dt := []byte(`
   548  		target "foo" {
   549  			dockerfile = target.foo.name
   550  		}
   551  		`)
   552  
   553  	c, err := ParseFile(dt, "docker-bake.hcl")
   554  	require.NoError(t, err)
   555  
   556  	require.Equal(t, 1, len(c.Targets))
   557  	require.Equal(t, "foo", c.Targets[0].Name)
   558  	require.Equal(t, "foo", *c.Targets[0].Dockerfile)
   559  }
   560  
   561  func TestHCLTargetAttrEmptyChain(t *testing.T) {
   562  	dt := []byte(`
   563  		target "foo" {
   564  			# dockerfile = Dockerfile
   565  			context = target.foo.dockerfile
   566  			target = target.foo.context
   567  		}
   568  		`)
   569  
   570  	c, err := ParseFile(dt, "docker-bake.hcl")
   571  	require.NoError(t, err)
   572  
   573  	require.Equal(t, 1, len(c.Targets))
   574  	require.Equal(t, "foo", c.Targets[0].Name)
   575  	require.Nil(t, c.Targets[0].Dockerfile)
   576  	require.Nil(t, c.Targets[0].Context)
   577  	require.Nil(t, c.Targets[0].Target)
   578  }
   579  
   580  func TestHCLAttrsCustomType(t *testing.T) {
   581  	dt := []byte(`
   582  		platforms=["linux/arm64", "linux/amd64"]
   583  		target "app" {
   584  			platforms = platforms
   585  			args = {
   586  				"v1": platforms[0]
   587  			}
   588  		}
   589  		`)
   590  
   591  	c, err := ParseFile(dt, "docker-bake.hcl")
   592  	require.NoError(t, err)
   593  
   594  	require.Equal(t, 1, len(c.Targets))
   595  	require.Equal(t, c.Targets[0].Name, "app")
   596  	require.Equal(t, []string{"linux/arm64", "linux/amd64"}, c.Targets[0].Platforms)
   597  	require.Equal(t, ptrstr("linux/arm64"), c.Targets[0].Args["v1"])
   598  }
   599  
   600  func TestHCLMultiFileAttrs(t *testing.T) {
   601  	dt := []byte(`
   602  		variable "FOO" {
   603  			default = "abc"
   604  		}
   605  		target "app" {
   606  			args = {
   607  				v1 = "pre-${FOO}"
   608  			}
   609  		}
   610  		`)
   611  	dt2 := []byte(`
   612  		FOO="def"
   613  		`)
   614  
   615  	c, err := ParseFiles([]File{
   616  		{Data: dt, Name: "c1.hcl"},
   617  		{Data: dt2, Name: "c2.hcl"},
   618  	}, nil)
   619  	require.NoError(t, err)
   620  	require.Equal(t, 1, len(c.Targets))
   621  	require.Equal(t, c.Targets[0].Name, "app")
   622  	require.Equal(t, ptrstr("pre-def"), c.Targets[0].Args["v1"])
   623  
   624  	t.Setenv("FOO", "ghi")
   625  
   626  	c, err = ParseFiles([]File{
   627  		{Data: dt, Name: "c1.hcl"},
   628  		{Data: dt2, Name: "c2.hcl"},
   629  	}, nil)
   630  	require.NoError(t, err)
   631  
   632  	require.Equal(t, 1, len(c.Targets))
   633  	require.Equal(t, c.Targets[0].Name, "app")
   634  	require.Equal(t, ptrstr("pre-ghi"), c.Targets[0].Args["v1"])
   635  }
   636  
   637  func TestHCLMultiFileGlobalAttrs(t *testing.T) {
   638  	dt := []byte(`
   639  		FOO = "abc"
   640  		target "app" {
   641  			args = {
   642  				v1 = "pre-${FOO}"
   643  			}
   644  		}
   645  		`)
   646  	dt2 := []byte(`
   647  		FOO = "def"
   648  		`)
   649  
   650  	c, err := ParseFiles([]File{
   651  		{Data: dt, Name: "c1.hcl"},
   652  		{Data: dt2, Name: "c2.hcl"},
   653  	}, nil)
   654  	require.NoError(t, err)
   655  	require.Equal(t, 1, len(c.Targets))
   656  	require.Equal(t, c.Targets[0].Name, "app")
   657  	require.Equal(t, "pre-def", *c.Targets[0].Args["v1"])
   658  }
   659  
   660  func TestHCLDuplicateTarget(t *testing.T) {
   661  	dt := []byte(`
   662  		target "app" {
   663  			dockerfile = "x"
   664  		}
   665  		target "app" {
   666  			dockerfile = "y"
   667  		}
   668  		`)
   669  
   670  	c, err := ParseFile(dt, "docker-bake.hcl")
   671  	require.NoError(t, err)
   672  
   673  	require.Equal(t, 1, len(c.Targets))
   674  	require.Equal(t, "app", c.Targets[0].Name)
   675  	require.Equal(t, "y", *c.Targets[0].Dockerfile)
   676  }
   677  
   678  func TestHCLRenameTarget(t *testing.T) {
   679  	dt := []byte(`
   680  		target "abc" {
   681  			name = "xyz"
   682  			dockerfile = "foo"
   683  		}
   684  		`)
   685  
   686  	_, err := ParseFile(dt, "docker-bake.hcl")
   687  	require.ErrorContains(t, err, "requires matrix")
   688  }
   689  
   690  func TestHCLRenameGroup(t *testing.T) {
   691  	dt := []byte(`
   692  		group "foo" {
   693  			name = "bar"
   694  			targets = ["x", "y"]
   695  		}
   696  		`)
   697  
   698  	_, err := ParseFile(dt, "docker-bake.hcl")
   699  	require.ErrorContains(t, err, "not supported")
   700  
   701  	dt = []byte(`
   702  		group "foo" {
   703  			matrix = {
   704  				name = ["x", "y"]
   705  			}
   706  		}
   707  		`)
   708  
   709  	_, err = ParseFile(dt, "docker-bake.hcl")
   710  	require.ErrorContains(t, err, "not supported")
   711  }
   712  
   713  func TestHCLRenameTargetAttrs(t *testing.T) {
   714  	dt := []byte(`
   715  		target "abc" {
   716  			name = "xyz"
   717  			matrix = {}
   718  			dockerfile = "foo"
   719  		}
   720  
   721  		target "def" {
   722  			dockerfile = target.xyz.dockerfile
   723  		}
   724  		`)
   725  
   726  	c, err := ParseFile(dt, "docker-bake.hcl")
   727  	require.NoError(t, err)
   728  	require.Equal(t, 2, len(c.Targets))
   729  	require.Equal(t, "xyz", c.Targets[0].Name)
   730  	require.Equal(t, "foo", *c.Targets[0].Dockerfile)
   731  	require.Equal(t, "def", c.Targets[1].Name)
   732  	require.Equal(t, "foo", *c.Targets[1].Dockerfile)
   733  
   734  	dt = []byte(`
   735  		target "def" {
   736  			dockerfile = target.xyz.dockerfile
   737  		}
   738  
   739  		target "abc" {
   740  			name = "xyz"
   741  			matrix = {}
   742  			dockerfile = "foo"
   743  		}
   744  		`)
   745  
   746  	c, err = ParseFile(dt, "docker-bake.hcl")
   747  	require.NoError(t, err)
   748  	require.Equal(t, 2, len(c.Targets))
   749  	require.Equal(t, "def", c.Targets[0].Name)
   750  	require.Equal(t, "foo", *c.Targets[0].Dockerfile)
   751  	require.Equal(t, "xyz", c.Targets[1].Name)
   752  	require.Equal(t, "foo", *c.Targets[1].Dockerfile)
   753  
   754  	dt = []byte(`
   755  		target "abc" {
   756  			name = "xyz"
   757  			matrix  = {}
   758  			dockerfile = "foo"
   759  		}
   760  
   761  		target "def" {
   762  			dockerfile = target.abc.dockerfile
   763  		}
   764  		`)
   765  
   766  	_, err = ParseFile(dt, "docker-bake.hcl")
   767  	require.ErrorContains(t, err, "abc")
   768  
   769  	dt = []byte(`
   770  		target "def" {
   771  			dockerfile = target.abc.dockerfile
   772  		}
   773  
   774  		target "abc" {
   775  			name = "xyz"
   776  			matrix = {}
   777  			dockerfile = "foo"
   778  		}
   779  		`)
   780  
   781  	_, err = ParseFile(dt, "docker-bake.hcl")
   782  	require.ErrorContains(t, err, "abc")
   783  }
   784  
   785  func TestHCLRenameSplit(t *testing.T) {
   786  	dt := []byte(`
   787  		target "x" {
   788  			name = "y"
   789  			matrix = {}
   790  			dockerfile = "foo"
   791  		}
   792  
   793  		target "x" {
   794  			name = "z"
   795  			matrix = {}
   796  			dockerfile = "bar"
   797  		}
   798  		`)
   799  
   800  	c, err := ParseFile(dt, "docker-bake.hcl")
   801  	require.NoError(t, err)
   802  
   803  	require.Equal(t, 2, len(c.Targets))
   804  	require.Equal(t, "y", c.Targets[0].Name)
   805  	require.Equal(t, "foo", *c.Targets[0].Dockerfile)
   806  	require.Equal(t, "z", c.Targets[1].Name)
   807  	require.Equal(t, "bar", *c.Targets[1].Dockerfile)
   808  
   809  	require.Equal(t, 1, len(c.Groups))
   810  	require.Equal(t, "x", c.Groups[0].Name)
   811  	require.Equal(t, []string{"y", "z"}, c.Groups[0].Targets)
   812  }
   813  
   814  func TestHCLRenameMultiFile(t *testing.T) {
   815  	dt := []byte(`
   816  		target "foo" {
   817  			name = "bar"
   818  			matrix = {}
   819  			dockerfile = "x"
   820  		}
   821  		`)
   822  	dt2 := []byte(`
   823  		target "foo" {
   824  			context = "y"
   825  		}
   826  		`)
   827  	dt3 := []byte(`
   828  		target "bar" {
   829  			target = "z"
   830  		}
   831  		`)
   832  
   833  	c, err := ParseFiles([]File{
   834  		{Data: dt, Name: "c1.hcl"},
   835  		{Data: dt2, Name: "c2.hcl"},
   836  		{Data: dt3, Name: "c3.hcl"},
   837  	}, nil)
   838  	require.NoError(t, err)
   839  
   840  	require.Equal(t, 2, len(c.Targets))
   841  
   842  	require.Equal(t, c.Targets[0].Name, "bar")
   843  	require.Equal(t, *c.Targets[0].Dockerfile, "x")
   844  	require.Equal(t, *c.Targets[0].Target, "z")
   845  
   846  	require.Equal(t, c.Targets[1].Name, "foo")
   847  	require.Equal(t, *c.Targets[1].Context, "y")
   848  }
   849  
   850  func TestHCLMatrixBasic(t *testing.T) {
   851  	dt := []byte(`
   852  		target "default" {
   853  			matrix = {
   854  				foo = ["x", "y"]
   855  			}
   856  			name = foo
   857  			dockerfile = "${foo}.Dockerfile"
   858  		}
   859  		`)
   860  
   861  	c, err := ParseFile(dt, "docker-bake.hcl")
   862  	require.NoError(t, err)
   863  
   864  	require.Equal(t, 2, len(c.Targets))
   865  	require.Equal(t, c.Targets[0].Name, "x")
   866  	require.Equal(t, c.Targets[1].Name, "y")
   867  	require.Equal(t, *c.Targets[0].Dockerfile, "x.Dockerfile")
   868  	require.Equal(t, *c.Targets[1].Dockerfile, "y.Dockerfile")
   869  
   870  	require.Equal(t, 1, len(c.Groups))
   871  	require.Equal(t, "default", c.Groups[0].Name)
   872  	require.Equal(t, []string{"x", "y"}, c.Groups[0].Targets)
   873  }
   874  
   875  func TestHCLMatrixMultipleKeys(t *testing.T) {
   876  	dt := []byte(`
   877  		target "default" {
   878  			matrix = {
   879  				foo = ["a"]
   880  				bar = ["b", "c"]
   881  				baz = ["d", "e", "f"]
   882  			}
   883  			name = "${foo}-${bar}-${baz}"
   884  		}
   885  		`)
   886  
   887  	c, err := ParseFile(dt, "docker-bake.hcl")
   888  	require.NoError(t, err)
   889  
   890  	require.Equal(t, 6, len(c.Targets))
   891  	names := make([]string, len(c.Targets))
   892  	for i, t := range c.Targets {
   893  		names[i] = t.Name
   894  	}
   895  	require.ElementsMatch(t, []string{"a-b-d", "a-b-e", "a-b-f", "a-c-d", "a-c-e", "a-c-f"}, names)
   896  
   897  	require.Equal(t, 1, len(c.Groups))
   898  	require.Equal(t, "default", c.Groups[0].Name)
   899  	require.ElementsMatch(t, []string{"a-b-d", "a-b-e", "a-b-f", "a-c-d", "a-c-e", "a-c-f"}, c.Groups[0].Targets)
   900  }
   901  
   902  func TestHCLMatrixLists(t *testing.T) {
   903  	dt := []byte(`
   904  	target "foo" {
   905  		matrix = {
   906  			aa = [["aa", "bb"], ["cc", "dd"]]
   907  		}
   908  		name = aa[0]
   909  		args = {
   910  			target = "val${aa[1]}"
   911  		}
   912  	}
   913  	`)
   914  
   915  	c, err := ParseFile(dt, "docker-bake.hcl")
   916  	require.NoError(t, err)
   917  
   918  	require.Equal(t, 2, len(c.Targets))
   919  	require.Equal(t, "aa", c.Targets[0].Name)
   920  	require.Equal(t, ptrstr("valbb"), c.Targets[0].Args["target"])
   921  	require.Equal(t, "cc", c.Targets[1].Name)
   922  	require.Equal(t, ptrstr("valdd"), c.Targets[1].Args["target"])
   923  }
   924  
   925  func TestHCLMatrixMaps(t *testing.T) {
   926  	dt := []byte(`
   927  	target "foo" {
   928  		matrix = {
   929  			aa = [
   930  				{
   931  					foo = "aa"
   932  					bar = "bb"
   933  				},
   934  				{
   935  					foo = "cc"
   936  					bar = "dd"
   937  				}
   938  			]
   939  		}
   940  		name = aa.foo
   941  		args = {
   942  			target = "val${aa.bar}"
   943  		}
   944  	}
   945  	`)
   946  
   947  	c, err := ParseFile(dt, "docker-bake.hcl")
   948  	require.NoError(t, err)
   949  
   950  	require.Equal(t, 2, len(c.Targets))
   951  	require.Equal(t, c.Targets[0].Name, "aa")
   952  	require.Equal(t, c.Targets[0].Args["target"], ptrstr("valbb"))
   953  	require.Equal(t, c.Targets[1].Name, "cc")
   954  	require.Equal(t, c.Targets[1].Args["target"], ptrstr("valdd"))
   955  }
   956  
   957  func TestHCLMatrixMultipleTargets(t *testing.T) {
   958  	dt := []byte(`
   959  		target "x" {
   960  			matrix = {
   961  				foo = ["a", "b"]
   962  			}
   963  			name = foo
   964  		}
   965  		target "y" {
   966  			matrix = {
   967  				bar = ["c", "d"]
   968  			}
   969  			name = bar
   970  		}
   971  		`)
   972  
   973  	c, err := ParseFile(dt, "docker-bake.hcl")
   974  	require.NoError(t, err)
   975  
   976  	require.Equal(t, 4, len(c.Targets))
   977  	names := make([]string, len(c.Targets))
   978  	for i, t := range c.Targets {
   979  		names[i] = t.Name
   980  	}
   981  	require.ElementsMatch(t, []string{"a", "b", "c", "d"}, names)
   982  
   983  	require.Equal(t, 2, len(c.Groups))
   984  	names = make([]string, len(c.Groups))
   985  	for i, c := range c.Groups {
   986  		names[i] = c.Name
   987  	}
   988  	require.ElementsMatch(t, []string{"x", "y"}, names)
   989  
   990  	for _, g := range c.Groups {
   991  		switch g.Name {
   992  		case "x":
   993  			require.Equal(t, []string{"a", "b"}, g.Targets)
   994  		case "y":
   995  			require.Equal(t, []string{"c", "d"}, g.Targets)
   996  		}
   997  	}
   998  }
   999  
  1000  func TestHCLMatrixDuplicateNames(t *testing.T) {
  1001  	dt := []byte(`
  1002  		target "default" {
  1003  			matrix = {
  1004  				foo = ["a", "b"]
  1005  			}
  1006  			name = "c"
  1007  		}
  1008  		`)
  1009  
  1010  	_, err := ParseFile(dt, "docker-bake.hcl")
  1011  	require.Error(t, err)
  1012  }
  1013  
  1014  func TestHCLMatrixArgs(t *testing.T) {
  1015  	dt := []byte(`
  1016  		a = 1
  1017  		variable "b" {
  1018  			default = 2
  1019  		}
  1020  		target "default" {
  1021  			matrix = {
  1022  				foo = [a, b]
  1023  			}
  1024  			name = foo
  1025  		}
  1026  		`)
  1027  
  1028  	c, err := ParseFile(dt, "docker-bake.hcl")
  1029  	require.NoError(t, err)
  1030  
  1031  	require.Equal(t, 2, len(c.Targets))
  1032  	require.Equal(t, "1", c.Targets[0].Name)
  1033  	require.Equal(t, "2", c.Targets[1].Name)
  1034  }
  1035  
  1036  func TestHCLMatrixArgsOverride(t *testing.T) {
  1037  	dt := []byte(`
  1038  	variable "ABC" {
  1039  		default = "def"
  1040  	}
  1041  
  1042  	target "bar" {
  1043  		matrix = {
  1044  			aa = split(",", ABC)
  1045  		}
  1046  		name = "bar-${aa}"
  1047  		args = {
  1048  			foo = aa
  1049  		}
  1050  	}
  1051  	`)
  1052  
  1053  	c, err := ParseFiles([]File{
  1054  		{Data: dt, Name: "docker-bake.hcl"},
  1055  	}, map[string]string{"ABC": "11,22,33"})
  1056  	require.NoError(t, err)
  1057  
  1058  	require.Equal(t, 3, len(c.Targets))
  1059  	require.Equal(t, "bar-11", c.Targets[0].Name)
  1060  	require.Equal(t, "bar-22", c.Targets[1].Name)
  1061  	require.Equal(t, "bar-33", c.Targets[2].Name)
  1062  
  1063  	require.Equal(t, ptrstr("11"), c.Targets[0].Args["foo"])
  1064  	require.Equal(t, ptrstr("22"), c.Targets[1].Args["foo"])
  1065  	require.Equal(t, ptrstr("33"), c.Targets[2].Args["foo"])
  1066  }
  1067  
  1068  func TestHCLMatrixBadTypes(t *testing.T) {
  1069  	dt := []byte(`
  1070  		target "default" {
  1071  			matrix = "test"
  1072  		}
  1073  		`)
  1074  	_, err := ParseFile(dt, "docker-bake.hcl")
  1075  	require.Error(t, err)
  1076  
  1077  	dt = []byte(`
  1078  		target "default" {
  1079  			matrix = ["test"]
  1080  		}
  1081  		`)
  1082  	_, err = ParseFile(dt, "docker-bake.hcl")
  1083  	require.Error(t, err)
  1084  
  1085  	dt = []byte(`
  1086  		target "default" {
  1087  			matrix = {
  1088  				["a"] = ["b"]
  1089  			}
  1090  		}
  1091  		`)
  1092  	_, err = ParseFile(dt, "docker-bake.hcl")
  1093  	require.Error(t, err)
  1094  
  1095  	dt = []byte(`
  1096  		target "default" {
  1097  			matrix = {
  1098  				1 = 2
  1099  			}
  1100  		}
  1101  		`)
  1102  	_, err = ParseFile(dt, "docker-bake.hcl")
  1103  	require.Error(t, err)
  1104  
  1105  	dt = []byte(`
  1106  		target "default" {
  1107  			matrix = {
  1108  				a = "b"
  1109  			}
  1110  		}
  1111  		`)
  1112  	_, err = ParseFile(dt, "docker-bake.hcl")
  1113  	require.Error(t, err)
  1114  }
  1115  
  1116  func TestHCLMatrixWithGlobalTarget(t *testing.T) {
  1117  	dt := []byte(`
  1118  		target "x" {
  1119  			tags = ["a", "b"]
  1120  		}
  1121  		
  1122  		target "default" {
  1123  			tags = target.x.tags
  1124  			matrix = {
  1125  				dummy = [""]
  1126  			}
  1127  		}
  1128  	`)
  1129  	c, err := ParseFile(dt, "docker-bake.hcl")
  1130  	require.NoError(t, err)
  1131  	require.Equal(t, 2, len(c.Targets))
  1132  	require.Equal(t, "x", c.Targets[0].Name)
  1133  	require.Equal(t, "default", c.Targets[1].Name)
  1134  	require.Equal(t, []string{"a", "b"}, c.Targets[1].Tags)
  1135  }
  1136  
  1137  func TestJSONAttributes(t *testing.T) {
  1138  	dt := []byte(`{"FOO": "abc", "variable": {"BAR": {"default": "def"}}, "target": { "app": { "args": {"v1": "pre-${FOO}-${BAR}"}} } }`)
  1139  
  1140  	c, err := ParseFile(dt, "docker-bake.json")
  1141  	require.NoError(t, err)
  1142  
  1143  	require.Equal(t, 1, len(c.Targets))
  1144  	require.Equal(t, c.Targets[0].Name, "app")
  1145  	require.Equal(t, ptrstr("pre-abc-def"), c.Targets[0].Args["v1"])
  1146  }
  1147  
  1148  func TestJSONFunctions(t *testing.T) {
  1149  	dt := []byte(`{
  1150  	"FOO": "abc",
  1151  	"function": {
  1152  		"myfunc": {
  1153  			"params": ["inp"],
  1154  			"result": "<${upper(inp)}-${FOO}>"
  1155  		}
  1156  	},
  1157  	"target": {
  1158  		"app": {
  1159  			"args": {
  1160  				"v1": "pre-${myfunc(\"foo\")}"
  1161  			}
  1162  		}
  1163  	}}`)
  1164  
  1165  	c, err := ParseFile(dt, "docker-bake.json")
  1166  	require.NoError(t, err)
  1167  
  1168  	require.Equal(t, 1, len(c.Targets))
  1169  	require.Equal(t, c.Targets[0].Name, "app")
  1170  	require.Equal(t, ptrstr("pre-<FOO-abc>"), c.Targets[0].Args["v1"])
  1171  }
  1172  
  1173  func TestJSONInvalidFunctions(t *testing.T) {
  1174  	dt := []byte(`{
  1175  	"target": {
  1176  		"app": {
  1177  			"args": {
  1178  				"v1": "myfunc(\"foo\")"
  1179  			}
  1180  		}
  1181  	}}`)
  1182  
  1183  	c, err := ParseFile(dt, "docker-bake.json")
  1184  	require.NoError(t, err)
  1185  
  1186  	require.Equal(t, 1, len(c.Targets))
  1187  	require.Equal(t, c.Targets[0].Name, "app")
  1188  	require.Equal(t, ptrstr(`myfunc("foo")`), c.Targets[0].Args["v1"])
  1189  }
  1190  
  1191  func TestHCLFunctionInAttr(t *testing.T) {
  1192  	dt := []byte(`
  1193  	function "brace" {
  1194  		params = [inp]
  1195  		result = "[${inp}]"
  1196  	}
  1197  	function "myupper" {
  1198  		params = [val]
  1199  		result = "${upper(val)} <> ${brace(v2)}"
  1200  	}
  1201  
  1202  		v1=myupper("foo")
  1203  		v2=lower("BAZ")
  1204  		target "app" {
  1205  			args = {
  1206  				"v1": v1
  1207  			}
  1208  		}
  1209  		`)
  1210  
  1211  	c, err := ParseFile(dt, "docker-bake.hcl")
  1212  	require.NoError(t, err)
  1213  
  1214  	require.Equal(t, 1, len(c.Targets))
  1215  	require.Equal(t, c.Targets[0].Name, "app")
  1216  	require.Equal(t, ptrstr("FOO <> [baz]"), c.Targets[0].Args["v1"])
  1217  }
  1218  
  1219  func TestHCLCombineCompose(t *testing.T) {
  1220  	dt := []byte(`
  1221  		target "app" {
  1222  			context = "dir"
  1223  			args = {
  1224  				v1 = "foo"
  1225  			}
  1226  		}
  1227  		`)
  1228  	dt2 := []byte(`
  1229  version: "3"
  1230  
  1231  services:
  1232    app:
  1233      build:
  1234        dockerfile: Dockerfile-alternate
  1235        args:
  1236          v2: "bar"
  1237  `)
  1238  
  1239  	c, err := ParseFiles([]File{
  1240  		{Data: dt, Name: "c1.hcl"},
  1241  		{Data: dt2, Name: "c2.yml"},
  1242  	}, nil)
  1243  	require.NoError(t, err)
  1244  
  1245  	require.Equal(t, 1, len(c.Targets))
  1246  	require.Equal(t, c.Targets[0].Name, "app")
  1247  	require.Equal(t, ptrstr("foo"), c.Targets[0].Args["v1"])
  1248  	require.Equal(t, ptrstr("bar"), c.Targets[0].Args["v2"])
  1249  	require.Equal(t, "dir", *c.Targets[0].Context)
  1250  	require.Equal(t, "Dockerfile-alternate", *c.Targets[0].Dockerfile)
  1251  }
  1252  
  1253  func TestHCLBuiltinVars(t *testing.T) {
  1254  	dt := []byte(`
  1255  		target "app" {
  1256  			context = BAKE_CMD_CONTEXT
  1257  			dockerfile = "test"
  1258  		}
  1259  		`)
  1260  
  1261  	c, err := ParseFiles([]File{
  1262  		{Data: dt, Name: "c1.hcl"},
  1263  	}, map[string]string{
  1264  		"BAKE_CMD_CONTEXT": "foo",
  1265  	})
  1266  	require.NoError(t, err)
  1267  
  1268  	require.Equal(t, 1, len(c.Targets))
  1269  	require.Equal(t, c.Targets[0].Name, "app")
  1270  	require.Equal(t, "foo", *c.Targets[0].Context)
  1271  	require.Equal(t, "test", *c.Targets[0].Dockerfile)
  1272  }
  1273  
  1274  func TestCombineHCLAndJSONTargets(t *testing.T) {
  1275  	c, err := ParseFiles([]File{
  1276  		{
  1277  			Name: "docker-bake.hcl",
  1278  			Data: []byte(`
  1279  group "default" {
  1280    targets = ["a"]
  1281  }
  1282  
  1283  target "metadata-a" {}
  1284  target "metadata-b" {}
  1285  
  1286  target "a" {
  1287    inherits = ["metadata-a"]
  1288    context = "."
  1289    target = "a"
  1290  }
  1291  
  1292  target "b" {
  1293    inherits = ["metadata-b"]
  1294    context = "."
  1295    target = "b"
  1296  }`),
  1297  		},
  1298  		{
  1299  			Name: "metadata-a.json",
  1300  			Data: []byte(`
  1301  {
  1302    "target": [{
  1303      "metadata-a": [{
  1304        "tags": [
  1305          "app/a:1.0.0",
  1306          "app/a:latest"
  1307        ]
  1308      }]
  1309    }]
  1310  }`),
  1311  		},
  1312  		{
  1313  			Name: "metadata-b.json",
  1314  			Data: []byte(`
  1315  {
  1316    "target": [{
  1317      "metadata-b": [{
  1318        "tags": [
  1319          "app/b:1.0.0",
  1320          "app/b:latest"
  1321        ]
  1322      }]
  1323    }]
  1324  }`),
  1325  		},
  1326  	}, nil)
  1327  	require.NoError(t, err)
  1328  
  1329  	require.Equal(t, 1, len(c.Groups))
  1330  	require.Equal(t, "default", c.Groups[0].Name)
  1331  	require.Equal(t, []string{"a"}, c.Groups[0].Targets)
  1332  
  1333  	require.Equal(t, 4, len(c.Targets))
  1334  
  1335  	require.Equal(t, c.Targets[0].Name, "metadata-a")
  1336  	require.Equal(t, []string{"app/a:1.0.0", "app/a:latest"}, c.Targets[0].Tags)
  1337  
  1338  	require.Equal(t, c.Targets[1].Name, "metadata-b")
  1339  	require.Equal(t, []string{"app/b:1.0.0", "app/b:latest"}, c.Targets[1].Tags)
  1340  
  1341  	require.Equal(t, c.Targets[2].Name, "a")
  1342  	require.Equal(t, ".", *c.Targets[2].Context)
  1343  	require.Equal(t, "a", *c.Targets[2].Target)
  1344  
  1345  	require.Equal(t, c.Targets[3].Name, "b")
  1346  	require.Equal(t, ".", *c.Targets[3].Context)
  1347  	require.Equal(t, "b", *c.Targets[3].Target)
  1348  }
  1349  
  1350  func TestCombineHCLAndJSONVars(t *testing.T) {
  1351  	c, err := ParseFiles([]File{
  1352  		{
  1353  			Name: "docker-bake.hcl",
  1354  			Data: []byte(`
  1355  variable "ABC" {
  1356    default = "foo"
  1357  }
  1358  variable "DEF" {
  1359    default = ""
  1360  }
  1361  group "default" {
  1362    targets = ["one"]
  1363  }
  1364  target "one" {
  1365    args = {
  1366      a = "pre-${ABC}"
  1367    }
  1368  }
  1369  target "two" {
  1370    args = {
  1371      b = "pre-${DEF}"
  1372    }
  1373  }`),
  1374  		},
  1375  		{
  1376  			Name: "foo.json",
  1377  			Data: []byte(`{"variable": {"DEF": {"default": "bar"}}, "target": { "one": { "args": {"a": "pre-${ABC}-${DEF}"}} } }`),
  1378  		},
  1379  		{
  1380  			Name: "bar.json",
  1381  			Data: []byte(`{"ABC": "ghi", "DEF": "jkl"}`),
  1382  		},
  1383  	}, nil)
  1384  	require.NoError(t, err)
  1385  
  1386  	require.Equal(t, 1, len(c.Groups))
  1387  	require.Equal(t, "default", c.Groups[0].Name)
  1388  	require.Equal(t, []string{"one"}, c.Groups[0].Targets)
  1389  
  1390  	require.Equal(t, 2, len(c.Targets))
  1391  
  1392  	require.Equal(t, c.Targets[0].Name, "one")
  1393  	require.Equal(t, map[string]*string{"a": ptrstr("pre-ghi-jkl")}, c.Targets[0].Args)
  1394  
  1395  	require.Equal(t, c.Targets[1].Name, "two")
  1396  	require.Equal(t, map[string]*string{"b": ptrstr("pre-jkl")}, c.Targets[1].Args)
  1397  }
  1398  
  1399  func TestEmptyVariableJSON(t *testing.T) {
  1400  	dt := []byte(`{
  1401  	  "variable": {
  1402  	    "VAR": {}
  1403  	  }
  1404  	}`)
  1405  	_, err := ParseFile(dt, "docker-bake.json")
  1406  	require.NoError(t, err)
  1407  }
  1408  
  1409  func TestFunctionNoParams(t *testing.T) {
  1410  	dt := []byte(`
  1411  		function "foo" {
  1412  			result = "bar"
  1413  		}
  1414  		target "foo_target" {
  1415  			args = {
  1416  				test = foo()
  1417  			}
  1418  		}
  1419  		`)
  1420  
  1421  	_, err := ParseFile(dt, "docker-bake.hcl")
  1422  	require.Error(t, err)
  1423  }
  1424  
  1425  func TestFunctionNoResult(t *testing.T) {
  1426  	dt := []byte(`
  1427  		function "foo" {
  1428  			params = ["a"]
  1429  		}
  1430  		`)
  1431  
  1432  	_, err := ParseFile(dt, "docker-bake.hcl")
  1433  	require.Error(t, err)
  1434  }
  1435  
  1436  func TestVarUnsupportedType(t *testing.T) {
  1437  	dt := []byte(`
  1438  		variable "FOO" {
  1439  			default = []
  1440  		}
  1441  		target "default" {}`)
  1442  
  1443  	t.Setenv("FOO", "bar")
  1444  	_, err := ParseFile(dt, "docker-bake.hcl")
  1445  	require.Error(t, err)
  1446  }
  1447  
  1448  func TestHCLIndexOfFunc(t *testing.T) {
  1449  	dt := []byte(`
  1450  		variable "APP_VERSIONS" {
  1451  		  default = [
  1452  			"1.42.4",
  1453  			"1.42.3"
  1454  		  ]
  1455  		}
  1456  		target "default" {
  1457  			args = {
  1458  				APP_VERSION = app_version
  1459  			}
  1460  			matrix = {
  1461  				app_version = APP_VERSIONS
  1462  			}
  1463  			name="app-${replace(app_version, ".", "-")}"
  1464  			tags = [
  1465  				"app:${app_version}",
  1466  				indexof(APP_VERSIONS, app_version) == 0 ? "app:latest" : "",
  1467  			]
  1468  		}
  1469  		`)
  1470  
  1471  	c, err := ParseFile(dt, "docker-bake.hcl")
  1472  	require.NoError(t, err)
  1473  
  1474  	require.Equal(t, 2, len(c.Targets))
  1475  	require.Equal(t, "app-1-42-4", c.Targets[0].Name)
  1476  	require.Equal(t, "app:latest", c.Targets[0].Tags[1])
  1477  	require.Equal(t, "app-1-42-3", c.Targets[1].Name)
  1478  	require.Empty(t, c.Targets[1].Tags[1])
  1479  }
  1480  
  1481  func ptrstr(s interface{}) *string {
  1482  	var n *string
  1483  	if reflect.ValueOf(s).Kind() == reflect.String {
  1484  		ss := s.(string)
  1485  		n = &ss
  1486  	}
  1487  	return n
  1488  }