如何将复杂的 DevOps 管道模板参数传递给脚本
Posted
技术标签:
【中文标题】如何将复杂的 DevOps 管道模板参数传递给脚本【英文标题】:How to pass complex DevOps pipeline template parameter to script 【发布时间】:2020-05-15 06:14:46 【问题描述】:在 Azure DevOps 管道模板中,我将参数声明为数组/序列
parameters:
mySubscription: ''
myArray: []
steps:
- AzureCLI@2
inputs:
azureSubscription: $ parameters.mySubscription
scriptType: pscore
scriptPath: $(Build.SourcesDirectory)/script.ps1
arguments: '-MyYAMLArgument $ parameters.myArray '
然后从管道定义中传递参数的值
steps:
- template: myTemplate.yml
parameters:
mySubscription: 'azure-connection'
myArray:
- field1: 'a'
field2: 'b'
- field1: 'aa'
field2: 'bb'
我的问题是我无法在 YAML 语法(ToString()
的种类)中按原样传递该数组,以便能够在我的模板中从 PowerShell 使用和处理该数组。尝试运行此管道时,出现以下错误:
/myTemplate.yml (Line: X, Col: X): Unable to convert from Array to String. Value: Array
。错误消息中引用的行/列对应于我的模板中的arguments: '-MyYAMLArgument $ parameters.myArray '
。
我还尝试将参数映射为我的脚本的环境
- AzureCLI@2
inputs:
azureSubscription: $ parameters.mySubscription
scriptType: pscore
scriptPath: $(Build.SourcesDirectory)/script.ps1
arguments: '-MyYAMLArgument $Env:MY_ENV_VAR'
env:
MY_ENV_VAR: $ parameters.myArray
这也不起作用:
/myTemplate.yml (Line: X, Col: Y): A sequence was not expected
。该时间线/列指的是MY_ENV_VAR: $ parameters.myArray
。
有没有人遇到过类似的要求,将管道定义中定义的复杂类型(这里是对象的数组/序列)传递给 PowerShell 脚本?如果有,您是如何实现的?
【问题讨论】:
【参考方案1】:您现在可以使用 ADO 管道中的 convertToJson
函数将这些类型的参数转换为字符串:
parameters:
- name: myParameter
type: object
default:
name1: value1
name2: value2
...
- task: Bash@3
inputs:
targetType: inline
script: |
echo "$ convertToJson(parameters.myParameter) "
参考:https://developercommunity.visualstudio.com/t/allow-type-casting-or-expression-function-from-yam/880210
convertToJson: https://docs.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops#converttojson
【讨论】:
这太棒了!如果您尝试将其作为参数传递给 PowerShell 脚本作为整个字符串未通过的参数,我确实遇到了问题。解决方法是将其设置为脚本的环境变量。 我们必须小心使用未记录的函数 - 希望这会成为主流,但它可能会在一夜之间消失! 一种方法可能是用PythonScript@0
替换Bash@3
并使用json 库将参数干净地解压到python 变量中。
谢谢!它对我来说不像 Powershell 那样工作 - 似乎 convertToJson 添加了 Powershell 脚本不喜欢的新行。在我的回答中查看我如何使用 ConvertFrom-Json Powershell 函数解决它。 BTW convertToJson 已经记录在这里:docs.microsoft.com/en-us/azure/devops/pipelines/process/…
编辑现在 convertToJson 现在是主流+记录-谢谢@MaMazav【参考方案2】:
我也面临类似的问题,我的解决方法是使用不同的分隔符将数组扁平化为字符串。
例如,我想让一些参数成为必需参数,如果未传递这些参数,则构建失败,而不是为每个要检查的参数添加一个任务,我想在单个任务中执行此操作。
为此,我首先将一个数组作为参数(传递给另一个名为check-required-params.yml
的模板,该模板负责检查参数的任务),其中每个元素都是name:value
类型的字符串,这是一个用冒号分隔的name
和value
所需参数的串联(使用format
表达式):
# templates/pipeline-template.yml
parameters:
- name: endpoint
type: string
default: ''
- name: rootDirectory
type: string
default: $(Pipeline.Workspace)
- name: remoteDirectory
type: string
default: '/'
- name: archiveName
type: string
default: ''
#other stuff
- template: check-required-params.yml
parameters:
requiredParams:
- $ format('endpoint:0', parameters.endpont)
- $ format('archiveName:0', parameters.archiveName)
然后在check-required-params.yml
中,我使用表达式$ join(';', parameters.requiredParams)
加入用分号分隔元素的数组,这将创建endpoint:value;archiveName:value
类型的字符串并将其作为环境变量传递。
此时,使用一点字符串操作,在脚本中我可以使用分号作为分隔符拆分字符串,这样我将得到一个字符串数组,例如name:value
,我可以进一步拆分,但这次使用冒号作为分隔器。
我的check-required-params.yml
看起来像:
# templates/check-required-params.yml
parameters:
- name: requiredParams
type: object
default: []
steps:
- task: PowerShell@2
env:
REQURED_PARAMS: $ join(';', parameters.requiredParams)
displayName: Check for required parameters
inputs:
targetType: inline
pwsh: true
script: |
$params = $env:REQURED_PARAMS -split ";"
foreach($param in $params)
if ([string]::IsNullOrEmpty($param.Split(":")[1]))
Write-Host "##vso[task.logissue type=error;]Missing template parameter $($param.Split(":")[0])"
Write-Host "##vso[task.complete result=Failed;]"
然后在我的azure-pipelines.yml
我可以做到:
#other stuff
- template: templates/pipeline-template.yml
parameters:
endpoint: 'myEndpoint'
rootDirectory: $(Pipeline.Workspace)/mycode
在这个例子中,构建将失败,因为我没有传递参数archiveName
您可以通过使用变量来定义分隔符来增加一些灵活性,而不是在脚本和表达式中进行硬编码
【讨论】:
这很好,但是如果没有指定参数,这将失败,因为$env:REQUIRED_PARAMS -split ";"
将返回一个包含空元素的数组。您可以通过快速检查来解决此问题if ([string]::IsNullOrEmpty($env:REQUIRED_PARAMS) -eq $false)
【参考方案3】:
基于 @ed-randall 的 convertToJson 想法,结合 ConvertFrom-Json Powershell 函数,我们可以使用 JSON 'contract' 在 yaml 和 PS 脚本之间传递值:
- powershell: |
$myArray = '$ convertToJson(parameters.myArray) ' | ConvertFrom-Json
...
【讨论】:
【参考方案4】:如何将复杂的 DevOps 管道模板参数传递给脚本
恐怕我们无法将复杂的 DevOps 管道模板参数传递给 PowerShell 脚本。
目前Azure devops的任务只支持一维数组的传输。它不能接受和传输二维数组。虽然我们可以定义一个二维数组的参数,但是我们需要通过如下脚本从模板中扩展参数:
- $ each field in parameters.myArray:
我们可以这样使用它:
- $ each step in parameters.buildSteps :
#- $ each pair in step :
- task: PowerShell@2
inputs:
targetType : inline
script: |
Write-Host 'Hello World'
但我们不能将二维数组直接传递给任务,例如:[field1: 'a', field2: 'b']
。这就是您收到错误Unable to convert from Array to String
的原因。
您可以查看文档Extend from a template 了解更多详情。
希望这会有所帮助。
【讨论】:
【参考方案5】:作为@Leo Liu MSFTmentioned in its answer,目前确实不支持,但有人已经opened an issue for this improvement 。
这个问题还包含一个很好的解决方法,现在可以改用环境变量。该解决方案的缺点是您需要了解数据结构才能正确映射它。
parameters:
mylist:[]
#where mylist is a sequence of object matching the mapping:
#- name: 'the name 1'
# value: 'the value of 1'
# index: 0
#- name: 'the name 2'
# value: 'the value of 2'
# index: 1
env:
$ each item in parameters.mylist :
$ format('SCRIPT_PARAM_0_KEY', item.index) : $ item.name
$ format('SCRIPT_PARAM_0_VAL', item.index) : $ item.value
【讨论】:
以上是关于如何将复杂的 DevOps 管道模板参数传递给脚本的主要内容,如果未能解决你的问题,请参考以下文章
使用 Azure DevOps 构建管道将机密传递给容器时,Docker BuildKit 返回“/run/secrets/<secret-id>:没有这样的文件或目录”