Использование переменных в пайплайнах Azure DevOps
Продолжаем обзор замечательной тулы для разработки под Windows и не только, Azure DevOps. На этот раз, намучавшись с переменными окружения я решил вынести весь опыт в одну статью. Начиная от того что, для каждой среды исполнения у них разный синтаксис, оканчивая отсуствием стандартной возможности переноса переменных из одной стадии пайплайна в другую.
Оговорюсь, что основные примеры будут на Release Pipelines, потому, что YAML туда еще не доехал, а мне нужна функциональность множества этапов и множества артефактов. Это, вроде стало доступно в обынчых Pipelines, что практически уровняло их по функциональности. В Pipelines YAML допилили и добавили к текстовому представлению, небольшую графическую подсказку с параметрами, которые можно задать. Очень удобно, не надо лезть в документацию по каждому модулю. Но это буду описывать в следующей статье, пока вот картинка самого нововведения.
Хранение и использование
Начнем с того, что в системе у нас есть переменные по умолчанию. Они начинаются, в зависимости от происхождения со слов 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.
Те же законы не распростроняются на ваши собственные переменные, тут уже вы несете ответственность за синтаксис. Задать переменные можно локально в каждой задаче.
Глобально в хранилище переменных и линковать их из хранилища. Очень удобно.
Бонусом к этому, если переменные шибко секретные, их можно хранить в облаке Azure в хранилище переменных под названием Azure Vault, залинковать хранилище к переменным проекта можно в Library.
В целом с переменными все понятно, в pipelines их еще можно задавать в ручную на каждый запуск, в release такой функциональности нет. Посмотреть что вы передаете в пайплайн можно еще раз в логах инициализации агента, но учтите, они там уже в преобразованном виде.
Динамические переменные
Самое интересное начинается, когда мы хотим получить некоторое значение в одном этапе и передать его в следующий.
Такой функциональности нам не завезли. Но наши руки не для скуки и при помощи гугла было найдено решение. Слава богу у 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
Для примера делаем некоторый рандомный генератор переменных. Обратите внимание на синтаксис объявления переменной внутри этого этапа, такой функционал завезли.
В следующем этапе мы передаем переменную скрипту, да да, напрямую нельзя, надо через параметр.
Скрипт 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. В следующем этапе, используя синтаксис системных переменных, мы можем ее посмотреть.
Пример довольно простой, но функционал открывает нам хорошие возможности в сумме с моей прошлой статьей, когда мы можем создать виртуальную машину на первом этапе тестирования, проделать с ней какие-то манипуляции дальше, причем параллельно несколько. И финальным этапом ее уничтожить. Сейчас у нас гоняются автотесты кода каждый раз на свежих виртуалках таким образом. А учитывая что живут они минут 10, стоит это копейки.
В следующей статье, если будет необходимость, расскажу о YAML пайплайнах, там довольно много интересных нововведений последнее время.