Использование переменных в пайплайнах Azure DevOps

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

Оговорюсь, что основные примеры будут на Release Pipelines, потому, что YAML туда еще не доехал, а мне нужна функциональность множества этапов и множества артефактов. Это, вроде стало доступно в обынчых Pipelines, что практически уровняло их по функциональности. В Pipelines YAML допилили и добавили к текстовому представлению, небольшую графическую подсказку с параметрами, которые можно задать. Очень удобно, не надо лезть в документацию по каждому модулю. Но это буду описывать в следующей статье, пока вот картинка самого нововведения.

lz5qqf9rnzsjid2whxlewwhpya4.png

Хранение и использование


Начнем с того, что в системе у нас есть переменные по умолчанию. Они начинаются, в зависимости от происхождения со слов Release, System, etc. Полный список (как оказалось, нет), доступен в документации. Всю шизофрению с синтаксисом иллюстрирует пример из документации ниже. У одной и той же переменной есть три представления, в зависимости от того, где мы ее вызываем.

steps:
- bash: echo This script could use $SYSTEM_ACCESSTOKEN
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- powershell: Write-Host "This is a script that could use $env:SYSTEM_ACCESSTOKEN"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)

Если вы задаете переменную на агенте, котором исполняется таск это $(System.AccessToken). Если вы хотите использовать ее внутри powershell скрипта на том же агенте, это уже будет $env: SYSTEM_ACCESSTOKEN. Если вы, упаси бог, хотите использовать эту переменную на каком то удаленном хосте, используя таск PowerShell on target machines, вам нужно передать это через аргумент к скрипту, используя param. С bash попроще, можно просто прокидывать внутрь используя аргумент и синтаксис $SYSTEM_ACCESSTOKEN.

Те же законы не распростроняются на ваши собственные переменные, тут уже вы несете ответственность за синтаксис. Задать переменные можно локально в каждой задаче.

unharrer1ddqsnkrnn6qfjragly.png

Глобально в хранилище переменных и линковать их из хранилища. Очень удобно.

qreopja_8vopoi0tbpisinbbg-4.png

Бонусом к этому, если переменные шибко секретные, их можно хранить в облаке Azure в хранилище переменных под названием Azure Vault, залинковать хранилище к переменным проекта можно в Library.

9jz5seel1mw7naaar1gnpfg0irq.png

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

cikzajzltpijm7brh7c2naszbua.png

Динамические переменные


Самое интересное начинается, когда мы хотим получить некоторое значение в одном этапе и передать его в следующий.

up5uxbrurswegydejopblc1__di.png

Такой функциональности нам не завезли. Но наши руки не для скуки и при помощи гугла было найдено решение. Слава богу у Azure DevOps есть API, который нам позволяет сделать чуть больше, чем нарисовали в интерфейсе.

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

$releaseurl = ('{0}{1}/_apis/release/releases/{2}?api-version=5.0' -f $($env:SYSTEM_TEAMFOUNDATIONSERVERURI), $($env:SYSTEM_TEAMPROJECTID), $($env:RELEASE_RELEASEID)  )

Задаем пустое значение переменной, которую мы хотим передать, ставим Scope — Release

unharrer1ddqsnkrnn6qfjragly.png

Для примера делаем некоторый рандомный генератор переменных. Обратите внимание на синтаксис объявления переменной внутри этого этапа, такой функционал завезли.

r4imki4fsx5rylzi0zgtquo4qnq.png

В следующем этапе мы передаем переменную скрипту, да да, напрямую нельзя, надо через параметр.

jxoiiiny9rzwy3xmrlzuwrfhbaw.png

Скрипт powershell под спойлером

Заголовок спойлера
#Script requires stageVar variable in release variables set to Release scope

param ( [string] $expVar )
#region variables
$ReleaseVariableName = 'StageVar'
$releaseurl = ('{0}{1}/_apis/release/releases/{2}?api-version=5.0' -f $($env:SYSTEM_TEAMFOUNDATIONSERVERURI), $($env:SYSTEM_TEAMPROJECTID), $($env:RELEASE_RELEASEID)  )
#endregion


#region Get Release Definition
Write-Host "URL: $releaseurl"
$Release = Invoke-RestMethod -Uri $releaseurl -Headers @{
    Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
#endregion

#region Output current Release Pipeline
Write-Output ('Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))
#endregion


#region Update StageVar with new value
$release.variables.($ReleaseVariableName).value = "$expVar"
#endregion

#region update release pipeline
Write-Output ('Updating Release Definition')
$json = @($release) | ConvertTo-Json -Depth 99
Invoke-RestMethod -Uri $releaseurl -Method Put -Body $json -ContentType "application/json" -Headers @{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
#endregion

#region Get updated Release Definition
Write-Output ('Get updated Release Definition')
Write-Host "URL: $releaseurl"
$Release = Invoke-RestMethod -Uri $releaseurl -Headers @{
    Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
#endregion

#region Output Updated Release Pipeline
Write-Output ('Updated Release Pipeline variables output: {0}' -f $($Release.variables | ConvertTo-Json -Depth 10))
#endregion

Вариант для bash

Заголовок спойлера
INPUT_VAR=$1
RELEASE_VAR=$2

echo Test ID: ${INPUT_VAR}

RELEASE_URL="${SYSTEM_TEAMFOUNDATIONSERVERURI}${SYSTEM_TEAMPROJECTID}/_apis/release/releases/${RELEASE_RELEASEID}?api-version=5.0"

echo release url: $RELEASE_URL

RELEASE_JSON=$(curl -H "Authorization: Bearer $SYSTEM_ACCESSTOKEN" $RELEASE_URL)

OUTPUT=`jq ''.variables.${RELEASE_VAR}.value' = '\"${INPUT_VAR}\"'' <<< $RELEASE_JSON`

curl -H "Authorization: Bearer $SYSTEM_ACCESSTOKEN" -H "Content-Type: application/json" -X PUT -d "$OUTPUT" $RELEASE_URL

В двух словах наш скрипт берет на входе переменную myVar и используя API кладет значение этой переменной в stageVar. В следующем этапе, используя синтаксис системных переменных, мы можем ее посмотреть.

bcuqdwhchkrykedarfq-fjmgz9a.png

Пример довольно простой, но функционал открывает нам хорошие возможности в сумме с моей прошлой статьей, когда мы можем создать виртуальную машину на первом этапе тестирования, проделать с ней какие-то манипуляции дальше, причем параллельно несколько. И финальным этапом ее уничтожить. Сейчас у нас гоняются автотесты кода каждый раз на свежих виртуалках таким образом. А учитывая что живут они минут 10, стоит это копейки.

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

© Habrahabr.ru