#powershell #asynchronous #tfsbuild
#powershell #асинхронный #tfsbuild
Вопрос:
Я работаю над процессом сборки TFS, в котором выполняется загрузка большого ZIP-архива из репозитория Artifactory. Загрузка обычно занимает более 20 минут, что фактически удваивает продолжительность выполнения моей сборки.
Чтобы ускорить мою сборку, я хотел запустить асинхронный процесс с шага сборки PowerShell, чтобы запросить загрузку артефакта и перейти к следующим шагам сборки, пока загрузка выполняется в фоновом режиме.
Я успешно справился с этим (запуск-процесс powershell -Скрытый стиль окна и т. Д.); артефакт загружается успешно, и, что важно, загрузка продолжается и завершается после завершения шага PowerShell и продолжения сборки на последующих шагах.
Теперь загруженный архив потребуется для последующей обработки в сборке. Итак, чтобы гарантировать, что обработка не начнется до завершения загрузки, я хочу обновить переменную среды сборки ‘IsDownloadComplete’ с ‘False’ на ‘True’.
Обычный PowerShell для обновления переменной среды не работает (я подозреваю, что из-за выполнения из потерянного асинхронного процесса?):
"##vso[task.setvariable variable='IsDownloadComplete';]'True'"
Итак, что делать? Как можно обновить переменную среды из асинхронного процесса?
ОБНОВЛЕНИЯ: Вызов асинхронного процесса с моего шага сборки TFS выглядит следующим образом:
$cmd = "$(SupportFunctionsLocation)[script].ps1 -artifactUrl '$(artifactUrl)' -artifactoryApiKey '$(ArtifactoryApiKey)' -baseDir '$(Build.BinariesDirectory)[base_dir]' -envVarName 'IsDownloadComplete' -envVarValue 'true'"
try {
Start-Process PowerShell.exe -ArgumentList "-NoProfile -WindowStyle Hidden -Command $cmd"
Start-Sleep -s 10
} catch {
Write-Host $_.ErrorMessage
}
Вызов для обновления переменной среды сборки находится в скрипте ‘$(SupportFunctionsLocation)[script].ps1’ после завершения извлечения артефакта:
param (
[parameter(mandatory=$true)][string]$artifactUrl,
[parameter(mandatory=$true)][string]$apiKey,
[parameter(mandatory=$true)][string]$baseDir,
[parameter(mandatory=$true)][string]$envVarName,
[parameter(mandatory=$true)][string]$envVarValue
)
function Pull-FromArtifactory ($url, $apiKey, $dir) {
if (-not (Test-Path -Path $dir)) {
New-Item -Path $dir -ItemType "Directory"
}
$archive = $null
Write-Host "Pulling archive from Artifactory repository (this may take a minute or two)..."
try {
$archive = Split-Path $url -leaf
$headers = @{"X-JFrog-Art-Api" = $apiKey}
Invoke-RestMethod -Uri $url -Headers $headers -OutFile "$dir$archive"
}
catch {
Write-Host "Failed to retrieve archive from Artifactory repository..."
Write-Host $_.Exception.Message
Exit 1
}
}
Pull-FromArtifactory $artifactUrl $apiKey $baseDir
# Update 'IsDownloadComplete' build environment variable
Write-Host "##vso[task.setvariable variable=$envVarName;]$envVarValue"
И мой последующий этап сборки отладки выглядит следующим образом:
if (Test-Path ("$(Build.BinariesDirectory)[base_dir]")) {
do {
Get-ChildItem "$(Build.BinariesDirectory)[base_dir]" -Include "test.zip" -Force -Recurse
Write-Host "Download complete: " $env:IsDownloadComplete
Start-Sleep -s 30
} while ($env:IsDownloadComplete -ne 'true')
}
When my build executes, the async process kicks off successfully from the Start-Process call in the PowerShell step. The step doesn’t wait; it terminates and the async process keeps executing in the background.
Поскольку сценарий на этапе отладки повторяется, каждое выполнение вызова Get-ChildItem показывает увеличение размера файла по мере продолжения асинхронного процесса до завершения загрузки (мы знаем по размеру файла).
Но, даже несмотря на то, что загрузка завершена, переменная среды сборки не обновляется; этап сценария отладки продолжает цикл бесконечно.
Вывод журнала с этапа отладки:
2020-09-11T13:46:23.5557526Каталог Z: C:BuildAgent_work15b [base_dir]
2020-09-11T13:46:23.5557526Z
2020-09-11T13:46:23.5687526Z Mode LastWriteTime Длина имени
2020-09-11T13:46:23.5707484Z —- ————- —— —-
2020-09-11T13:46:23.5737531Z -a—- 9/11/2020 9 :46 УТРА 3572999 test.zip
2020-09-11T13:46:23.6027538Z Загрузка завершена: false
2020-09-11T13:46:53.6214501Z -a—- 9/11/2020 9 :46 утра
30362999 test.zip
2020-09-11T13:46:53.6224502Z Загрузка завершена: false
2020-09-11T13:47:23.6271640Z -a—- 9/11/2020 9 :46 утра
56742999 test.zip
2020-09-11T13:47:23.6291560Z Загрузка завершена: false
2020-09-11T13:47:53.6328511Z -a—- 9/11/2020 9 :46 утра
83072999 test.zip
2020-09-11T13:47:53.6338508Z Загрузка завершена: false
2020-09-11T13:48:23.6377761Z -a—- 9/11/2020 9 :48 утра
95577750 test.zip
2020-09-11T13:48:23.6387762Z Загрузка завершена: false
2020-09-11T13:48:53.6424119Z -a—- 9/11/2020 9 :48 утра
95577750 test.zip
2020-09-11T13:48:53.6434120Z Загрузка завершена: false
2020-09-11T13:49:23.6473880Z -a—- 9/11/2020 9 :48 утра 95577750 test.zip
2020-09-11T13:49:23.6483867Z Загрузка завершена: false
2020-09-11T13:49:53.6519497Z -a—- 9/11/2020 9 :48 утра 95577750 test.zip
2020-09-11T13:49:53.6529503Z Загрузка завершена: false
2020-09-11T13:50:23.6560150Z -a—- 9/11/2020 9 :48 утра 95577750 test.zip
Обратите внимание на увеличение ‘test.zip размер файла с каждой итерацией.
Ответ №1:
Вы не за горами. Я бы сделал следующее
- Загрузите огромный zip-файл со сценарием. Последний шаг — установить переменную
"Write-Host ##vso[task.setvariable variable=IsDownloadComplete]True"
- Выполняйте шаги, которые выполняются параллельно с большой загрузкой, до тех пор, пока не начнется задание, объединяющее все.
- Добавьте шаг со сценарием powershell, который проверяет
$env:IsDownloadComplete -eq 'True'
. Позвольте ему выполнять цикл до тех пор, пока значение IsDownloadComplete не станет истинным, или вы не нажмете тайм-аут (вы же не хотите запускать свою сборку вечно, если что-то пойдет не так). Не забудьте добавитьrefreshenv
на этом шаге, потому что значение обновляется другим процессом. - Остальные шаги
Комментарии:
1. Это то, что я подумал; Я зациклил вызов Get-ChildItem для просмотра размера файла, а также вызов для чтения переменной среды (с обновлением переменной до ‘True’ в качестве условия выхода из цикла) на последующем этапе отладки. Я ясно вижу, что при выполнении размер файла увеличивается до его завершения, но переменная среды остается установленной «False», и шаг продолжает повторяться.
2. Можете ли вы добавить некоторые выходные данные журнала и сценарий?
3. Вы также удалили все кавычки вокруг вашего setvariable? Смотрите мой пример в ответе. С кавычками это не будет работать должным образом.
4. Привет, Pba, я обновил приведенный выше вопрос с помощью командного сценария для асинхронного выполнения, асинхронного вызова для выполнения и последующего выполнения сценариев отладки. Спасибо, что нашли время взглянуть на это.
5. На самом деле вполне логично, что это не работает. Вы проверяете среду, но на самом деле никогда не обновляете ее. добавьте
refreshenv
в свой скрипт, и это может сработать. У вас нет настройки, чтобы протестировать ее прямо сейчас.
Ответ №2:
Как описано в OP и комментариях выше, я не смог заставить вызов обновления переменной среды из асинхронного задания работать. В конце концов, временные ограничения потребовали быстрого и грязного решения:
-
На этапе сборки, запускающем асинхронное задание, перед созданием асинхронного процесса я создал текстовый файл, содержащий слово «False»:
New-Item -Path $tempFilePath -Name "ArtifactDownloadComplete.txt" -ItemType "file" -Value "False"
-
Запустите процесс асинхронной загрузки; этап завершается, и сборка продолжается (асинхронный процесс выполняется в фоновом режиме). После завершения вызова Invoke-RestMethod в функции async содержимое ‘ArtifactDownloadComplete.txt ‘ файл обновлен до «True»:
((Get-Content -Path "$tempFilePathArtifactDownloadComplete.txt" -Raw) -replace "False","True") | Set-Content -Path "$tempFilePathArtifactDownloadComplete.txt"
-
Прежде чем загруженный архив потребуется для обработки в сборке, я добавил шаг для распаковки архива. В реализацию этого шага PowerShell включена задержка цикла; условием прерывания этого цикла для начала обработки шага является содержимое ‘ArtifactDownloadComplete.txt ‘ файл обновляется для чтения «True»:
do { if ((Get-Content $statusFile) -eq "False") { Start-Sleep -s 20 } else { Write-ConsoleLog "Artifact download complete." } } while ((Get-Content $statusFile) -eq "False")
Действительно, быстро и грязно.
Возможная область интереса для будущей разработки его процесса: TFS REST API.