【问题标题】:Update TFS build environment variable from asynchronously executing PowerShell process从异步执行的 PowerShell 进程更新 TFS 构建环境变量
【发布时间】:2020-12-30 00:37:42
【问题描述】:

我正在开发一个 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\15\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 AM 3572999 test.zip

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

2020-09-11T13:46:53.6214501Z -a---- 2020 年 9 月 11 日上午 9:46
30362999 test.zip

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

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

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

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

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

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

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

2020-09-11T13:48:53.6424119Z -a---- 2020 年 9 月 11 日上午 9:48
95577750 test.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”文件的大小。

【问题讨论】:

    标签: powershell asynchronous tfsbuild


    【解决方案1】:

    离你不远了。我会做以下事情

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

    【讨论】:

    • 这是我的想法;我在随后的调试步骤中循环了一个 Get-ChildItem 调用以查看文件大小以及读取环境变量的调用(将变量更新为“True”作为循环转义条件)。我可以清楚地看到执行文件大小会增加,直到完成,但环境变量仍设置为“False”,并且该步骤继续循环。
    • 可以添加一些日志输出和脚本吗?
    • 您是否还删除了 setvariable 周围的所有引号?请参阅我在答案中的示例。使用引号,它将无法正常工作。
    • 嗨 Pba,我已经用异步执行的命令脚本、异步调用执行以及随后执行的调试脚本更新了上面的问题。感谢您抽出宝贵时间查看此内容。
    • 它不起作用实际上是很合乎逻辑的。您检查环境,但从未真正刷新它。在你的脚本中添加refreshenv,它可能会起作用。现在没有设置来测试它。
    【解决方案2】:

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

    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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-10
      • 1970-01-01
      • 2018-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-27
      相关资源
      最近更新 更多