Как передать в скрипт сложный параметр шаблона конвейера DevOps

В шаблоне конвейера 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? Если да, то как вы этого добились?

6 ответов

Решение

Как передать в скрипт сложный параметр шаблона конвейера 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 из шаблона для получения более подробной информации.

Надеюсь это поможет.

Теперь вы можете преобразовать эти типы параметров в String, используя (недокументированный) convertToJson функция в конвейере ADO:

      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 от @ ed-randall вместе с функцией ConvertFrom-Json Powershell мы можем использовать JSON-контракт для передачи значений между yaml и скриптом PS:

      - powershell: |
    $myArray = '${{ convertToJson(parameters.myArray) }}' | ConvertFrom-Json
    ...

Я также сталкиваюсь с аналогичной проблемой, мой обходной путь - сгладить массив в строке, используя другой разделитель для разных размеров.

Например, я хочу сделать некоторые параметры обязательными и завершить сборку, если эти параметры не переданы, вместо добавления задачи для каждого параметра, который нужно проверить, я хочу сделать это в одной задаче.

Для этого я сначала передаю в качестве параметра (в другой шаблон, называемый check-required-params.yml которые содержат задачу, отвечающую за проверку параметров), массив, в котором каждый элемент представляет собой строку типа name:value который является конкатенацией (с использованием format выражение) name и value обязательных параметров через двоеточие:

# 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
  inputs:
  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;]"
      }
    }
  targetType: inline
  pwsh: true
  env:
    REQURED_PARAMS: ${{ join(';', parameters.requiredParams) }}
  displayName: Check for required parameters

Тогда в моем azure-pipelines.yml Я могу сделать:

#other stuff
- template: templates/pipeline-template.yml
  parameters:
    endpoint: 'myEndpoint'
    rootDirectory: $(Pipeline.Workspace)/mycode

В этом примере сборка завершится ошибкой, потому что я не передаю параметр archiveName

Вы можете добавить некоторую гибкость, используя переменные также для определения разделителей вместо жесткого кодирования в скриптах и ​​в выражениях.

Аргументы файла скрипта

В приведенном ниже примере представлен синтаксис, необходимый для передачи логического значения yaml Azure DevOps и массива в файл сценария PowerShell с помощью аргументов.

логический -> Переключить
объект -> Массив

Скрипт PowerShell

      [CmdletBinding()]
param (
    [Parameter()]
    [switch]
    $Check,

    [Parameter()]
    [string[]]
    $Array
)

If($Check.IsPresent)
{
    Write-Host "Check is present"
}
else {
    Write-Host "Check is not present"
}

Write-Host "Next we loop the array..."
Foreach($a in $Array){
    Write-Host "Item in the array: $a"
}

Ямл Пайплайн

      trigger: none

pool:
  vmImage: windows-latest

parameters:
  - name: checkBool
    type: boolean

  - name: paramArray
    type: object
    default:
      - one
      - two

steps:
- task: PowerShell@2
  inputs:
    filePath: 'Scripts/DebugSwitches.ps1'
    arguments: -Check:$${{ parameters.checkBool }} -Array ${{ join(', ', parameters.paramArray) }}

Логический синтаксис

Обратите внимание, что логическое значение yaml передается параметру переключателя PowerShell с двоеточием ':' без пробелов.

Синтаксис массива

Обратите внимание, что в приведенном выше массиве объектов yaml используется оператор Join для форматирования массива как массива, разделенного запятыми, который передается в аргумент массива PowerShell.

Как Leo Liu-MSFT в своем ответе, это действительно не поддерживается прямо сейчас, но кто-то уже открыл проблему для этого улучшения.

Эта проблема также содержит хороший обходной путь для использования вместо этого переменных среды. Недостатком этого решения является то, что вам нужно знать структуру данных, чтобы правильно ее отобразить.

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 }}
Другие вопросы по тегам