Как передать в скрипт сложный параметр шаблона конвейера 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) }}"
На основе идеи 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 }}