Skip to content

Commit 4ed5774

Browse files
author
Doug Greiman
committed
Update cloudbuild substitution rules
* Escaping $ is done with $$ not \$ * Undefined builtin substitutions are now an error, instead of being replaced by the empty string
1 parent 89773e9 commit 4ed5774

2 files changed

Lines changed: 21 additions & 19 deletions

File tree

scripts/local_cloudbuild.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@
4949
# Container Builder substitutions
5050
# https://cloud.google.com/container-builder/docs/api/build-requests#substitutions
5151
SUBSTITUTION_REGEX = re.compile(r"""(?x)
52-
(?<!\\) # Don't match if backslash before dollar sign
53-
\$ # Dollar sign
52+
[$] # Dollar sign
5453
(
5554
[A-Z_][A-Z0-9_]* # Variable name, no curly brackets
5655
|
5756
{[A-Z_][A-Z0-9_]*} # Variable name, with curly brackets
57+
|
58+
[$] # $$, translated to a single literal $
5859
)
5960
""")
6061

@@ -128,16 +129,14 @@ def sub(match):
128129
if variable_name[0] == '{':
129130
# Strip curly brackets
130131
variable_name = variable_name[1:-1]
131-
if variable_name not in substitutions:
132-
if variable_name.startswith('_'):
133-
# User variables must be set
134-
raise ValueError(
135-
'Variable "{}" used without being defined. Try adding '
136-
'it to the --substitutions flag'.format(
137-
variable_name))
138-
else:
139-
# Builtin variables are silently turned into empty strings
140-
value = ''
132+
if variable_name == '$':
133+
value = '$'
134+
elif variable_name not in substitutions:
135+
# Variables must be set
136+
raise ValueError(
137+
'Variable "{}" used without being defined. Try adding '
138+
'it to the --substitutions flag'.format(
139+
variable_name))
141140
else:
142141
value = substitutions.get(variable_name)
143142
substitutions_used.add(variable_name)

scripts/local_cloudbuild_test.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,12 @@ def test_sub_and_quote(self):
123123
('', {}, "''", []),
124124
# No substitutions
125125
('a', {}, 'a', []),
126-
# Unused builtin substitutions are fine
126+
# Unused substitition (ok here but error in generate_script)
127127
('a', {'FOO':'foo'}, 'a', []),
128-
# Unused user substitition (ok here but error in generate_script)
129128
('a', {'_FOO':'_foo'}, 'a', []),
130129
# Defined builtin substitution
131130
('a$FOOb', {'FOO':'foo'}, 'afoob', ['FOO']),
132131
('a${FOO}b', {'FOO':'foo'}, 'afoob', ['FOO']),
133-
# Undefined builtin substitution
134-
('a$FOOb', {}, 'ab', ['FOO']),
135-
('a${FOO}b', {}, 'ab', ['FOO']),
136132
# Defined user substitution
137133
('a$_FOOb', {'_FOO':'_foo'}, 'a_foob', ['_FOO']),
138134
('a${_FOO}b', {'_FOO':'_foo'}, 'a_foob', ['_FOO']),
@@ -153,6 +149,9 @@ def test_sub_and_quote(self):
153149
self.assertEqual(used, set(expected_used))
154150

155151
invalid_cases = (
152+
# Undefined builtin substitution
153+
('a$FOOb', {}),
154+
('a${FOO}b', {}),
156155
# Undefined user substitution
157156
('a$_FOOb', {}),
158157
('a${_FOO}b', {}),
@@ -293,8 +292,8 @@ def test_generate_command(self):
293292
self.assertIn("'a builtin substitution'", command)
294293

295294
step = base_step._replace(name='a $UNSET_BUILTIN substitution')
296-
command = local_cloudbuild.generate_command(step, subs, set())
297-
self.assertIn("'a substitution'", command)
295+
with self.assertRaises(ValueError):
296+
command = local_cloudbuild.generate_command(step, subs, set())
298297

299298
step = base_step._replace(name='a $_USER substitution')
300299
command = local_cloudbuild.generate_command(step, subs, set())
@@ -308,6 +307,10 @@ def test_generate_command(self):
308307
command = local_cloudbuild.generate_command(step, subs, set())
309308
self.assertIn("'a curly brace builtin substitution'", command)
310309

310+
step = base_step._replace(name='an escaped $$ or $$$$ or $$BUILTIN or $${BUILTIN} is unescaped')
311+
command = local_cloudbuild.generate_command(step, subs, set())
312+
self.assertIn("'an escaped $ or $$ or $BUILTIN or ${BUILTIN} is unescaped'", command)
313+
311314
def test_generate_script_golden(self):
312315
config_name = 'cloudbuild_ok.yaml'
313316
config = os.path.join(self.testdata_dir, config_name)

0 commit comments

Comments
 (0)