@@ -1081,6 +1081,7 @@ or interpolate them in attribute values in your Bake file.
1081
1081
1082
1082
``` hcl
1083
1083
variable "TAG" {
1084
+ type = string
1084
1085
default = "latest"
1085
1086
}
1086
1087
@@ -1102,6 +1103,206 @@ overriding the default `latest` value shown in the previous example.
1102
1103
$ TAG=dev docker buildx bake webapp-dev
1103
1104
```
1104
1105
1106
+ Variables can also be assigned an explicit type.
1107
+ If provided, it will be used to validate the default value (if set), as well as any overrides.
1108
+ This is particularly useful when using complex types which are intended to be overridden.
1109
+ The previous example could be expanded to apply an arbitrary series of tags.
1110
+ ``` hcl
1111
+ variable "TAGS" {
1112
+ default = ["latest"]
1113
+ type = list(string)
1114
+ }
1115
+
1116
+ target "webapp-dev" {
1117
+ dockerfile = "Dockerfile.webapp"
1118
+ tags = [for tag in TAGS: "docker.io/username/webapp:${tag}"]
1119
+ }
1120
+ ```
1121
+
1122
+ This example shows how to generate three tags without changing the file
1123
+ or using custom functions/parsing:
1124
+ ``` console
1125
+ $ TAGS=dev,latest,2 docker buildx bake webapp-dev
1126
+ ```
1127
+
1128
+ ### Variable typing
1129
+
1130
+ The following primitive types are available:
1131
+ * ` string `
1132
+ * ` number `
1133
+ * ` bool `
1134
+
1135
+ The type is expressed like a keyword; it must be expressed as a literal:
1136
+ ``` hcl
1137
+ variable "OK" {
1138
+ type = string
1139
+ }
1140
+
1141
+ # cannot be an actual string
1142
+ variable "BAD" {
1143
+ type = "string"
1144
+ }
1145
+
1146
+ # cannot be the result of an expression
1147
+ variable "ALSO_BAD" {
1148
+ type = lower("string")
1149
+ }
1150
+ ```
1151
+ Specifying primitive types can be valuable to show intent (especially when a default is not provided),
1152
+ but bake will generally behave as expected without explicit typing.
1153
+
1154
+ Complex types are expressed with "type constructors"; they are:
1155
+ * ` tuple([<type>,...]) `
1156
+ * ` list(<type>) `
1157
+ * ` set(<type>) `
1158
+ * ` map(<type>) `
1159
+ * ` object({<attr>=<type>},...}) `
1160
+
1161
+ The following are examples of each of those, as well as how the (optional) default value would be expressed:
1162
+ ``` hcl
1163
+ # structured way to express "1.2.3-alpha"
1164
+ variable "MY_VERSION" {
1165
+ type = tuple([number, number, number, string])
1166
+ default = [1, 2, 3, "alpha"]
1167
+ }
1168
+
1169
+ # JDK versions used in a matrix build
1170
+ variable "JDK_VERSIONS" {
1171
+ type = list(number)
1172
+ default = [11, 17, 21]
1173
+ }
1174
+
1175
+ # better way to express the previous example; this will also
1176
+ # enforce set semantics and allow use of set-based functions
1177
+ variable "JDK_VERSIONS" {
1178
+ type = set(number)
1179
+ default = [11, 17, 21]
1180
+ }
1181
+
1182
+ # with the help of lookup(), translate a 'feature' to a tag
1183
+ variable "FEATURE_TO_NAME" {
1184
+ type = map(string)
1185
+ default = {featureA = "slim", featureB = "tiny"}
1186
+ }
1187
+
1188
+ # map a branch name to a registry location
1189
+ variable "PUSH_DESTINATION" {
1190
+ type = object({branch = string, registry = string})
1191
+ default = {branch = "main", registry = "prod-registry.invalid.com"}
1192
+ }
1193
+
1194
+ # make the previous example more useful with composition
1195
+ variable "PUSH_DESTINATIONS" {
1196
+ type = list(object({branch = string, registry = string}))
1197
+ default = [
1198
+ {branch = "develop", registry = "test-registry.invalid.com"},
1199
+ {branch = "main", registry = "prod-registry.invalid.com"},
1200
+ ]
1201
+ }
1202
+ ```
1203
+ Note that in each example, the default value would be valid even if typing was not present.
1204
+ If typing was omitted, the first three would all be considered ` tuple ` ;
1205
+ you would be restricted to functions that operate on ` tuple ` and, for example, not be able to add elements.
1206
+ Similarly, the third and fourth would both be considered ` object ` , with the limits and semantics of that type.
1207
+ In short, in the absence of a type, any value delimited with ` [] ` is a ` tuple `
1208
+ and value delimited with ` {} ` is an ` object ` .
1209
+ Explicit typing for complex types not only opens up the ability to use functions applicable to that specialized type,
1210
+ but is also a precondition for providing overrides.
1211
+
1212
+ > [ !NOTE]
1213
+ > See [ HCL Type Expressions] [ typeexpr ] page for more details.
1214
+
1215
+ ### Overriding variables
1216
+
1217
+ As mentioned in the [ intro to variables] ( #variable ) , primitive types (` string ` , ` number ` , and ` bool ` )
1218
+ can be overridden without typing and will generally behave as expected.
1219
+ (When explicit typing is not provided, a variable is assumed to be primitive when the default value lacks ` {} ` or ` [] ` delimiters;
1220
+ a variable with neither typing nor a default value is treated as ` string ` .)
1221
+ Naturally, these same overrides can be used alongside explicit typing too;
1222
+ they may help in edge cases where you want ` VAR=true ` to be a ` string ` , where without typing,
1223
+ it may be a ` string ` or a ` bool ` depending on how/where it's used.
1224
+ Overriding a variable with a complex type can only be done when the type is provided.
1225
+ This is still done via environment variables, but the values can be provided via CSV or JSON.
1226
+
1227
+ #### CSV overrides
1228
+
1229
+ This is considered the canonical method and is well suited to interactive usage.
1230
+ It is assumed that ` list ` and ` set ` will be the most common complex type,
1231
+ as well as the most common complex type designed to be overridden.
1232
+ Thus, there is full CSV support for ` list ` and ` set `
1233
+ (and ` tuple ` ; despite being considered a structural type, it is more like a collection type in this regard).
1234
+
1235
+
1236
+ There is limited support for ` map ` and ` object ` and no support for composite types;
1237
+ for these advanced cases, an alternative mechanism [ using JSON] ( #json-overrides ) is available.
1238
+
1239
+ #### JSON overrides
1240
+
1241
+ Overrides can also be provided via JSON.
1242
+ This is the only method available for providing some complex types and may be convenient if overrides are already JSON
1243
+ (for example, if they come from a JSON API).
1244
+ It can also be used when dealing with values are difficult or impossible to specify using CSV (e.g., values containing quotes or commas).
1245
+ To use JSON, simply append ` _JSON ` to the variable name.
1246
+ In this contrived example, CSV cannot handle the second value; despite being a supported CSV type, JSON must be used:
1247
+ ``` hcl
1248
+ variable "VALS" {
1249
+ type = list(string)
1250
+ default = ["some", "list"]
1251
+ }
1252
+ ```
1253
+ ``` console
1254
+ $ cat data.json
1255
+ ["hello","with,comma","with\"quote"]
1256
+ $ VALS_JSON=$( < data.json) docker buildx bake
1257
+
1258
+ # CSV equivalent, though the second value cannot be expressed at all
1259
+ $ VALS=' hello,"with""quote"' docker buildx bake
1260
+ ```
1261
+
1262
+ This example illustrates some precedence and usage rules:
1263
+ ``` hcl
1264
+ variable "FOO" {
1265
+ type = string
1266
+ default = "foo"
1267
+ }
1268
+
1269
+ variable "FOO_JSON" {
1270
+ type = string
1271
+ default = "foo"
1272
+ }
1273
+ ```
1274
+
1275
+ The variable ` FOO ` can * only* be overridden using CSV because ` FOO_JSON ` , which would typically used for a JSON override,
1276
+ is already a defined variable.
1277
+ Since ` FOO_JSON ` is an actual variable, setting that environment variable would be expected to a CSV value.
1278
+ A JSON override * is* possible for this variable, using environment variable ` FOO_JSON_JSON ` .
1279
+
1280
+ ``` Console
1281
+ # These three are all equivalent, setting variable FOO=bar
1282
+ $ FOO=bar docker buildx bake < ...>
1283
+ $ FOO=' bar' docker buildx bake < ...>
1284
+ $ FOO=" bar" docker buildx bake < ...>
1285
+
1286
+ # Sets * only* variable FOO_JSON; FOO is untouched
1287
+ $ FOO_JSON=bar docker buildx bake < ...>
1288
+
1289
+ # This also sets FOO_JSON, but will fail due to not being valid JSON
1290
+ $ FOO_JSON_JSON=bar docker buildx bake < ...>
1291
+
1292
+ # These are all equivalent
1293
+ $ cat data.json
1294
+ "bar"
1295
+ $ FOO_JSON_JSON=$( < data.json) docker buildx bake < ...>
1296
+ $ FOO_JSON_JSON=' "bar"' docker buildx bake < ...>
1297
+ $ FOO_JSON=bar docker buildx bake < ...>
1298
+
1299
+ # This results in setting two different variables, both specified as CSV (FOO=bar and FOO_JSON=" baz" )
1300
+ $ FOO=bar FOO_JSON=' "baz"' docker buildx bake < ...>
1301
+
1302
+ # These refer to the same variable with FOO_JSON_JSON having precedence and read as JSON (FOO_JSON=baz)
1303
+ $ FOO_JSON=bar FOO_JSON_JSON=' "baz"' docker buildx bake < ...>
1304
+ ```
1305
+
1105
1306
### Built-in variables
1106
1307
1107
1308
The following variables are built-ins that you can use with Bake without having
@@ -1239,4 +1440,5 @@ target "webapp-dev" {
1239
1440
[ ssh ] : https://docs.docker.com/reference/cli/docker/buildx/build/#ssh
1240
1441
[ tag ] : https://docs.docker.com/reference/cli/docker/image/build/#tag
1241
1442
[ target ] : https://docs.docker.com/reference/cli/docker/image/build/#target
1443
+ [ typeexpr ] : https://github.com/hashicorp/hcl/tree/main/ext/typeexpr
1242
1444
[ userfunc ] : https://github.com/hashicorp/hcl/tree/main/ext/userfunc
0 commit comments