【问题标题】:How to pass complex DevOps pipeline template parameter to script如何将复杂的 DevOps 管道模板参数传递给脚本
【发布时间】: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 脚本?如果有,您是如何实现的?

【问题讨论】:

    标签: azure-devops yaml azure-pipelines azure-pipelines-yaml


    【解决方案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 类型的字符串,这是一个用冒号分隔的namevalue 所需参数的串联(使用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 }}
        

        【讨论】:

          猜你喜欢
          • 2021-08-09
          • 2016-07-03
          • 1970-01-01
          • 2021-09-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-11
          • 2020-09-08
          • 2019-12-13
          相关资源
          最近更新 更多