Gitlab CI: 设置动态变量
Gitlab CI: Set dynamic variables
对于 gitlab CI 我正在定义一些这样的变量:
variables:
PROD: project_package
STAGE: project_package_stage
PACKAGE_PATH: /opt/project/build/package
BUILD_PATH: /opt/project/build/package/bundle
CONTAINER_IMAGE: registry.example.com/project/package:e2e
我想更动态地设置这些变量,因为主要只有两部分:project
和 package
。其他一切都取决于这些值,这意味着我只需更改两个值即可获得所有其他变量。
所以我希望是这样的
variables:
PROJECT: project
PACKAGE: package
PROD: $PROJECT_$PACKAGE
STAGE: $PROD_stage
PACKAGE_PATH: /opt/$PROJECT/build/$PACKAGE
BUILD_PATH: /opt/$PROJECT/build/$PACKAGE/bundle
CONTAINER_IMAGE: registry.example.com/$PROJECT/$PACKAGE:e2e
但看起来,这样做的方式是错误的...
我不知道你的期望从何而来,但在 YAML 中是 trivial to check there is no special meaning for $
, _
, '/' nor :
if not followed by a space。 gitlab 中可能有,但我强烈怀疑是否存在您期望的方式。
为了形式化您的期望,您假设任何键(来自相同的映射)以 $
开头并以标量的末尾终止,由 _
或 /
将 "expanded" 到该键的值。 _
必须是这样的终止符,否则 $PROJECT_$PACKAGE
将无法正确展开。
现在考虑添加键值对:
BREAKING_TEST: $PACKAGE_PATH
这是否应该扩展为:
BREAKING_TEST: /opt/project/build/package/bundle
或遵循您暗示 _
是终止符的规则,只需扩展为:
BREAKING_TEST: project_PATH
为了防止这种歧义,像 bash
这样的程序使用引号来扩展变量名("$PROJECT"_PATH
与 $PROJECT_PATH
),但更明智、更现代的解决方案是使用夹紧开始和结束字符(例如 {
和 }
、$%
和 %
、)以及一些特殊规则以将夹紧字符用作普通文本。
所以这不会像您指出的那样工作,因为您确实做错了什么。
预处理 YAML 文件并不难,可以使用例如Python(但要注意 {
在 YAML 中有特殊含义),可以在 jinja2 的帮助下实现:加载变量,然后使用变量扩展原始文本,直到无法再进行替换。
但这一切都始于明智地选择分隔符。还要记住,虽然你的 "variables" 似乎在 YAML 文本中被排序,但当它们在你的程序中被构造为 dict/hash/mapping 时,并没有这样的保证。
你可以,例如使用 <<
和 >>
:
variables:
PROJECT: project
PACKAGE: package
PROD: <<PROJECT>>_<<PACKAGE>>
STAGE: <<PROD>>_stage
PACKAGE_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>
BUILD_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>/bundle
CONTAINER_IMAGE: registry.example.com/<<PROJECT>>/<<PACKAGE>>:e2
其中,使用以下程序(不处理转义 <<
以保持其正常含义)准确生成原始的、扩展的 YAML。
import sys
from ruamel import yaml
def expand(s, d):
max_recursion = 100
while '<<' in s:
res = ''
max_recursion -= 1
if max_recursion < 0:
raise NotImplementedError('max recursion exceeded')
for idx, chunk in enumerate(s.split('<<')):
if idx == 0:
res += chunk # first chunk is before <<, just append
continue
try:
var, rest = chunk.split('>>', 1)
except ValueError:
raise NotImplementedError('delimiters have to balance "{}"'.format(chunk))
if var not in d:
res += '<<' + chunk
else:
res += d[var] + rest
s = res
return s
with open('template.yaml') as fp:
yaml_str = fp.read()
variables = yaml.safe_load(yaml_str)['variables']
data = yaml.round_trip_load(expand(yaml_str, variables))
yaml.round_trip_dump(data, sys.stdout, indent=2)
对于 gitlab CI 我正在定义一些这样的变量:
variables:
PROD: project_package
STAGE: project_package_stage
PACKAGE_PATH: /opt/project/build/package
BUILD_PATH: /opt/project/build/package/bundle
CONTAINER_IMAGE: registry.example.com/project/package:e2e
我想更动态地设置这些变量,因为主要只有两部分:project
和 package
。其他一切都取决于这些值,这意味着我只需更改两个值即可获得所有其他变量。
所以我希望是这样的
variables:
PROJECT: project
PACKAGE: package
PROD: $PROJECT_$PACKAGE
STAGE: $PROD_stage
PACKAGE_PATH: /opt/$PROJECT/build/$PACKAGE
BUILD_PATH: /opt/$PROJECT/build/$PACKAGE/bundle
CONTAINER_IMAGE: registry.example.com/$PROJECT/$PACKAGE:e2e
但看起来,这样做的方式是错误的...
我不知道你的期望从何而来,但在 YAML 中是 trivial to check there is no special meaning for $
, _
, '/' nor :
if not followed by a space。 gitlab 中可能有,但我强烈怀疑是否存在您期望的方式。
为了形式化您的期望,您假设任何键(来自相同的映射)以 $
开头并以标量的末尾终止,由 _
或 /
将 "expanded" 到该键的值。 _
必须是这样的终止符,否则 $PROJECT_$PACKAGE
将无法正确展开。
现在考虑添加键值对:
BREAKING_TEST: $PACKAGE_PATH
这是否应该扩展为:
BREAKING_TEST: /opt/project/build/package/bundle
或遵循您暗示 _
是终止符的规则,只需扩展为:
BREAKING_TEST: project_PATH
为了防止这种歧义,像 bash
这样的程序使用引号来扩展变量名("$PROJECT"_PATH
与 $PROJECT_PATH
),但更明智、更现代的解决方案是使用夹紧开始和结束字符(例如 {
和 }
、$%
和 %
、)以及一些特殊规则以将夹紧字符用作普通文本。
所以这不会像您指出的那样工作,因为您确实做错了什么。
预处理 YAML 文件并不难,可以使用例如Python(但要注意 {
在 YAML 中有特殊含义),可以在 jinja2 的帮助下实现:加载变量,然后使用变量扩展原始文本,直到无法再进行替换。
但这一切都始于明智地选择分隔符。还要记住,虽然你的 "variables" 似乎在 YAML 文本中被排序,但当它们在你的程序中被构造为 dict/hash/mapping 时,并没有这样的保证。
你可以,例如使用 <<
和 >>
:
variables:
PROJECT: project
PACKAGE: package
PROD: <<PROJECT>>_<<PACKAGE>>
STAGE: <<PROD>>_stage
PACKAGE_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>
BUILD_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>/bundle
CONTAINER_IMAGE: registry.example.com/<<PROJECT>>/<<PACKAGE>>:e2
其中,使用以下程序(不处理转义 <<
以保持其正常含义)准确生成原始的、扩展的 YAML。
import sys
from ruamel import yaml
def expand(s, d):
max_recursion = 100
while '<<' in s:
res = ''
max_recursion -= 1
if max_recursion < 0:
raise NotImplementedError('max recursion exceeded')
for idx, chunk in enumerate(s.split('<<')):
if idx == 0:
res += chunk # first chunk is before <<, just append
continue
try:
var, rest = chunk.split('>>', 1)
except ValueError:
raise NotImplementedError('delimiters have to balance "{}"'.format(chunk))
if var not in d:
res += '<<' + chunk
else:
res += d[var] + rest
s = res
return s
with open('template.yaml') as fp:
yaml_str = fp.read()
variables = yaml.safe_load(yaml_str)['variables']
data = yaml.round_trip_load(expand(yaml_str, variables))
yaml.round_trip_dump(data, sys.stdout, indent=2)