从异步执行的 PowerShell 进程更新 TFS 构建环境变量

Update TFS build environment variable from asynchronously executing PowerShell process

我正在研究一个 TFS 构建过程,该过程具有从 Artifactory 存储库下载大型 ZIP 存档的功能。下载通常需要 20 分钟以上,实际上使我的构建执行时间加倍。

为了加快我的构建速度,我想从 PowerShell 构建步骤开始一个异步过程以请求工件下载并在下载在后台执行的同时进入下一个构建步骤。

我已经成功解决了这个问题(Start-Process powershell -WindowStyle Hidden 等...);工件确实下载成功,而且至关重要的是,在 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')
}

当我的构建执行时,异步进程从 PowerShell 步骤中的 Start-Process 调用成功启动。这一步不等待;它终止并且异步进程继续在后台执行。

随着调试步骤中的脚本循环,每次执行 Get-ChildItem 调用都会显示文件大小随着异步过程的继续而增加,直到下载完成(我们通过文件大小知道)。

但是,即使下载完成,构建环境变量也无法更新;调试脚本步骤继续无限循环。

调试步骤的日志输出:

2020-09-11T13:46:23.5557526Z 目录:C:\BuildAgent_work\b[base_dir]

2020-09-11T13:46:23.5557526Z

2020-09-11T13:46:23.5687526Z 模式 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---- 2020 年 9 月 11 日 9:46 上午
30362999test.zip

2020-09-11T13:46:53.6224502Z 下载完成:false

2020-09-11T13:47:23.6271640Z -a---- 2020 年 9 月 11 日 9:46 上午
56742999test.zip

2020-09-11T13:47:23.6291560Z 下载完成:false

2020-09-11T13:47:53.6328511Z -a---- 2020 年 9 月 11 日 9:46 上午
83072999test.zip

2020-09-11T13:47:53.6338508Z 下载完成:false

2020-09-11T13:48:23.6377761Z -a---- 2020 年 9 月 11 日 9:48 上午
95577750test.zip

2020-09-11T13:48:23.6387762Z 下载完成:false

2020-09-11T13:48:53.6424119Z -a---- 2020 年 9 月 11 日 9:48 上午
95577750test.zip

2020-09-11T13:48:53.6434120Z 下载完成:false

2020-09-11T13:49:23.6473880Z -a---- 9/11/2020 9:48 AM 95577750 test.zip

2020-09-11T13:49:23.6483867Z 下载完成:false

2020-09-11T13:49:53.6519497Z -a---- 9/11/2020 9:48 AM 95577750 test.zip

2020-09-11T13:49:53.6529503Z 下载完成:false

2020-09-11T13:50:23.6560150Z -a---- 9/11/2020 9:48 AM 95577750 test.zip

注意每次迭代都会增加 'test.zip' 文件大小。

你离得不远了。我会执行以下操作

  1. 使用脚本下载巨大的 zip 文件。最后一步是设置变量 "Write-Host ##vso[task.setvariable variable=IsDownloadComplete]True"
  2. 执行运行与大型下载并行的步骤,直到联合一切的工作即将开始。
  3. 添加一个带有 powershell 脚本的步骤来检查 $env:IsDownloadComplete -eq 'True'。让它循环直到 IsDownloadComplete 为真,或者您遇到超时(您不想在出现问题时永远 运行 您的构建)。不要忘记在此步骤中添加一个 refreshenv,因为该值由不同的进程更新。
  4. 剩下的步骤

如上面 OP 和评论中所述,我无法使异步作业的环境变量更新调用正常工作。 最后,时间限制需要一个快速而肮脏的解决方案:

  1. 在启动异步作业的构建步骤中,在创建异步进程之前,我创建了一个包含单词“False”的文本文件:

    New-Item -Path $tempFilePath -Name "ArtifactDownloadComplete.txt" -ItemType "file" -Value "False"
    
  2. 开始异步下载进程;步骤结束并构建进度(异步过程在后台执行)。异步函数中的 Invoke-RestMethod 调用完成后,'ArtifactDownloadComplete.txt' 文件的内容更新为“True”:

    ((Get-Content -Path "$tempFilePath\ArtifactDownloadComplete.txt" -Raw) -replace "False","True") | Set-Content -Path "$tempFilePath\ArtifactDownloadComplete.txt"
    
  3. 在构建中需要下载的存档进行处理之前,我添加了一个解包存档的步骤。在此步骤的 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。