Продолжаем обзор замечательной тулы для разработки под 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, залинковать 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
Для примера делаем некоторый рандомный генератор значений. Обратите внимание на синтаксис объявления переменной внутри этого этапа, такой функционал завезли.
В следующем этапе мы передаем переменную скрипту, да да, напрямую нельзя, надо через аргумент.
Скрипт под спойлером
#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
Или
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 пайплайнах, там довольно много интересных нововведений последнее время.
Автор: vsantonov