@@ -1081,6 +1081,7 @@ or interpolate them in attribute values in your Bake file.
10811081
10821082``` hcl
10831083variable "TAG" {
1084+ type = string
10841085 default = "latest"
10851086}
10861087
@@ -1102,6 +1103,206 @@ overriding the default `latest` value shown in the previous example.
11021103$ TAG=dev docker buildx bake webapp-dev
11031104```
11041105
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+
11051306### Built-in variables
11061307
11071308The following variables are built-ins that you can use with Bake without having
@@ -1239,4 +1440,5 @@ target "webapp-dev" {
12391440[ ssh ] : https://docs.docker.com/reference/cli/docker/buildx/build/#ssh
12401441[ tag ] : https://docs.docker.com/reference/cli/docker/image/build/#tag
12411442[ target ] : https://docs.docker.com/reference/cli/docker/image/build/#target
1443+ [ typeexpr ] : https://github.com/hashicorp/hcl/tree/main/ext/typeexpr
12421444[ userfunc ] : https://github.com/hashicorp/hcl/tree/main/ext/userfunc
0 commit comments