Browse code

Add support for more advanced ${xxx:...} syntax

Just ${xxx:+...} and ${xxx:-...} for now

Signed-off-by: Doug Davis <dug@us.ibm.com>

Doug Davis authored on 2015/01/29 11:28:48
Showing 4 changed files
... ...
@@ -157,7 +157,40 @@ func (sw *shellWord) processDollar() (string, error) {
157 157
 			sw.next()
158 158
 			return sw.getEnv(name), nil
159 159
 		}
160
-		return "", fmt.Errorf("Unsupported ${} substitution: %s", sw.word)
160
+		if ch == ':' {
161
+			// Special ${xx:...} format processing
162
+			// Yes it allows for recursive $'s in the ... spot
163
+
164
+			sw.next() // skip over :
165
+			modifier := sw.next()
166
+
167
+			word, err := sw.processStopOn('}')
168
+			if err != nil {
169
+				return "", err
170
+			}
171
+
172
+			// Grab the current value of the variable in question so we
173
+			// can use to to determine what to do based on the modifier
174
+			newValue := sw.getEnv(name)
175
+
176
+			switch modifier {
177
+			case '+':
178
+				if newValue != "" {
179
+					newValue = word
180
+				}
181
+				return newValue, nil
182
+
183
+			case '-':
184
+				if newValue == "" {
185
+					newValue = word
186
+				}
187
+				return newValue, nil
188
+
189
+			default:
190
+				return "", fmt.Errorf("Unsupported modifier (%c) in substitution: %s", modifier, sw.word)
191
+			}
192
+		}
193
+		return "", fmt.Errorf("Missing ':' in substitution: %s", sw.word)
161 194
 	}
162 195
 	// $xxx case
163 196
 	name := sw.processName()
... ...
@@ -30,6 +30,17 @@ he${hi}                  |     he
30 30
 he${hi}xx                |     hexx
31 31
 he${PWD}                 |     he/home
32 32
 he${.}                   |     error
33
+he${XXX:-000}xx          |     he000xx
34
+he${PWD:-000}xx          |     he/homexx
35
+he${XXX:-$PWD}xx         |     he/homexx
36
+he${XXX:-${PWD:-yyy}}xx  |     he/homexx
37
+he${XXX:-${YYY:-yyy}}xx  |     heyyyxx
38
+he${XXX:YYY}             |     error
39
+he${XXX:+${PWD}}xx       |     hexx
40
+he${PWD:+${XXX}}xx       |     hexx
41
+he${PWD:+${SHELL}}xx     |     hebashxx
42
+he${XXX:+000}xx          |     hexx
43
+he${PWD:+000}xx          |     he000xx
33 44
 'he${XX}'                |     he${XX}
34 45
 "he${PWD}"               |     he/home
35 46
 "he'$PWD'"               |     he'/home'
... ...
@@ -41,3 +52,7 @@ he\$PWD                  |     he$PWD
41 41
 "he\$PWD"                |     he$PWD
42 42
 'he\$PWD'                |     he\$PWD
43 43
 he${PWD                  |     error
44
+he${PWD:=000}xx          |     error
45
+he${PWD:+${PWD}:}xx      |     he/home:xx
46
+he${XXX:-\$PWD:}xx       |     he$PWD:xx
47
+he${XXX:-\${PWD}z}xx     |     he${PWDz}xx
... ...
@@ -113,18 +113,30 @@ images.
113 113
 > replacement at the time. After 1.3 this behavior will be preserved and
114 114
 > canonical.
115 115
 
116
-Environment variables (declared with [the `ENV` statement](#env)) can also be used in
117
-certain instructions as variables to be interpreted by the `Dockerfile`. Escapes
118
-are also handled for including variable-like syntax into a statement literally.
116
+Environment variables (declared with [the `ENV` statement](#env)) can also be
117
+used in certain instructions as variables to be interpreted by the
118
+`Dockerfile`. Escapes are also handled for including variable-like syntax
119
+into a statement literally.
119 120
 
120 121
 Environment variables are notated in the `Dockerfile` either with
121 122
 `$variable_name` or `${variable_name}`. They are treated equivalently and the
122 123
 brace syntax is typically used to address issues with variable names with no
123 124
 whitespace, like `${foo}_bar`.
124 125
 
126
+The `${variable_name}` syntax also supports a few of the standard `bash`
127
+modifiers as specified below:
128
+
129
+* `${variable:-word}` indicates that if `variable` is set then the result
130
+  will be that value. If `variable` is not set then `word` will be the result.
131
+* `${variable:+word}` indiates that if `variable` is set then `word` will be
132
+  the result, otherwise the result is the empty string.
133
+
134
+In all cases, `word` can be any string, including additional environment
135
+variables.
136
+
125 137
 Escaping is possible by adding a `\` before the variable: `\$foo` or `\${foo}`,
126 138
 for example, will translate to `$foo` and `${foo}` literals respectively.
127
- 
139
+
128 140
 Example (parsed representation is displayed after the `#`):
129 141
 
130 142
     FROM busybox
... ...
@@ -214,13 +214,19 @@ func TestBuildEnvironmentReplacementAddCopy(t *testing.T) {
214 214
   ENV baz foo
215 215
   ENV quux bar
216 216
   ENV dot .
217
+  ENV fee fff
218
+  ENV gee ggg
217 219
 
218 220
   ADD ${baz} ${dot}
219 221
   COPY ${quux} ${dot}
222
+  ADD ${zzz:-${fee}} ${dot}
223
+  COPY ${zzz:-${gee}} ${dot}
220 224
   `,
221 225
 		map[string]string{
222 226
 			"foo": "test1",
223 227
 			"bar": "test2",
228
+			"fff": "test3",
229
+			"ggg": "test4",
224 230
 		})
225 231
 
226 232
 	if err != nil {
... ...
@@ -286,6 +292,11 @@ func TestBuildEnvironmentReplacementEnv(t *testing.T) {
286 286
 			if parts[1] != "zzz" {
287 287
 				t.Fatalf("%s should be 'foo' but instead its %q", parts[0], parts[1])
288 288
 			}
289
+		} else if strings.HasPrefix(parts[0], "env") {
290
+			envCount++
291
+			if parts[1] != "foo" {
292
+				t.Fatalf("%s should be 'foo' but instead its %q", parts[0], parts[1])
293
+			}
289 294
 		}
290 295
 	}
291 296
 
... ...
@@ -4069,6 +4080,27 @@ RUN    [ "$abc" = "'foo'" ]
4069 4069
 ENV    abc \"foo\"
4070 4070
 RUN    [ "$abc" = '"foo"' ]
4071 4071
 
4072
+ENV    abc=ABC
4073
+RUN    [ "$abc" = "ABC" ]
4074
+ENV    def=${abc:-DEF}
4075
+RUN    [ "$def" = "ABC" ]
4076
+ENV    def=${ccc:-DEF}
4077
+RUN    [ "$def" = "DEF" ]
4078
+ENV    def=${ccc:-${def}xx}
4079
+RUN    [ "$def" = "DEFxx" ]
4080
+ENV    def=${def:+ALT}
4081
+RUN    [ "$def" = "ALT" ]
4082
+ENV    def=${def:+${abc}:}
4083
+RUN    [ "$def" = "ABC:" ]
4084
+ENV    def=${ccc:-\$abc:}
4085
+RUN    [ "$def" = '$abc:' ]
4086
+ENV    def=${ccc:-\${abc}:}
4087
+RUN    [ "$def" = '${abc:}' ]
4088
+ENV    mypath=${mypath:+$mypath:}/home
4089
+RUN    [ "$mypath" = '/home' ]
4090
+ENV    mypath=${mypath:+$mypath:}/away
4091
+RUN    [ "$mypath" = '/home:/away' ]
4092
+
4072 4093
 ENV    e1=bar
4073 4094
 ENV    e2=$e1
4074 4095
 ENV    e3=$e11