如何确定触发 TFS 发布的构建定义?

How to determine the build definition that triggered a release in TFS?

由于在使用 TFVC 时,在 TFS 中不可能有一个单一的构建定义来处理多个分支中的相同代码,因此我们必须为我们的分支维护多个构建定义。

现在我们已经扩展了 CI/CD 管道以进行自动发布部署。由于我们有多个分支,我们也有多个构建定义,现在我们也有多个发布定义。

使用 TFS 2017.1,现在可以在发布定义中定义多个持续部署触发器(工件源)。如果成功构建了与这些工件源关联的构建定义之一,则可以自动创建和部署新版本。 为了能够摆脱多个版本定义,有必要确定实际触发新版本创建及其部署的构建。

发布定义中可以使用许多变量,但不幸的是,似乎无法获得实际触发发布的 build/artifact(请参阅 https://www.visualstudio.com/en-us/docs/build/concepts/definitions/release/variables#primary-artifact-variables)。

如何确定构建 definition/artifact 触发了发布?

在 TFS 2017 Update1 的发布定义中,如果您将多个构建定义绑定到它并为发布定义中的每个工件添加持续部署触发器。然后每次构建成功时都会触发您的发布定义。

要了解哪个版本触发了您的发布,您可以参考发布摘要页面

可以的,可以使用标记的环境变量

使用此代码段,您可以获取并打印所有环境变量。屏幕截图显示了执行此代码段的结果。

# Get all release environment variables
$environmentVars = get-childitem -path env:*

# Show each environment variable
foreach($var in $environmentVars){
    $keyname = $var.Key
    $keyvalue = $var.Value 
    Write-Verbose "${keyname}: $keyvalue" -Verbose
}

我遇到了同样的问题。我更喜欢一种更直接的方法来找到相应构建的触发“{Artifact 别名}”,但我发现确定哪个构建触发了发布的唯一方法是获取以下环境变量: $环境:RELEASE_RELEASEDESCRIPTION 该变量具有以下计算值: "Triggered by" + {工件别名} + BuildId + "." 希望这可以帮助 最好的祝福 斯蒂芬

我有相同的要求(获取触发构建的工件的路径)但建议的解决方案对我不起作用 - $env:RELEASE_RELEASEDESCRIPTION 为空白。

原因: 这是因为发布是手动触发的。我们不能在这些构建中进行自动部署,必须手动触发部署,因为大多数构建都没有部署。然而,我遗漏的一点是,虽然我们不希望构建中的任何 Environments 自动触发,但 Release[=32] 是可以的=] 自动创建。

解决方法: 一旦我理解了这一点,并在发布定义中的每个工件上启用了 CI 触发器,但将所有环境触发器保留为 "Manual Only",现在填充了 $env:RELEASE_RELEASEDESCRIPTION , "stephgou" 提出的方法将起作用。

如果TFS对此有更好的解决方案就好了,因为解析描述肯定不好!最终我们需要 DropLocation,因此也需要将其添加为变量。

这是我从描述中获取 DropLocation 的 PowerShell 脚本,它可能对某些人有用:

#*================================================================================
#* Summary: This is a workaround for a TFS Limitation.
#*          Determines the Drop Location of a vNext build, as it is NOT provided in a variable for us!
#*
#* Output: values will be available in the following Build Step variables:
#*    $(TriggeringBuildID)
#*    $(TriggeringBuildNumber)
#*    $(TriggeringBuildUri)
#*    $(DropLocation)
#* Or, in a script:
#*    $env:TRIGGERINGBUILDID
#*    $env:TRIGGERINGBUILDNUMBER
#*    $env:TRIGGERINGBUILDURL
#*    $env:DROPLOCATION
#*================================================================================


Param (
    [string] $DeploymentScript = "_internalDeployment\DeployBuild_vNext.ps1"
)


$tfsServer=$env:SYSTEM_TEAMFOUNDATIONSERVERURI
$teamProject = $env:SYSTEM_TEAMPROJECT

# Default these, but as we may have multiple build artifacts associated with the release, need to get the triggering build ID if possible.
$script:buildID = $env:BUILD_BUILDID
$script:buildNumber = $env:BUILD_BUILDNUMBER
$script:buildURI = $env:BUILD_BUILDURI

Write "buildID = $script:buildID"
Write "buildNumber = $script:buildNumber"
Write "buildURI = $script:buildURI"

$ReleaseDescription = $env:RELEASE_RELEASEDESCRIPTION
if( $ReleaseDescription -and $ReleaseDescription.Length -gt 16 )
{
    Write "$ReleaseDescription = [$ReleaseDescription], Determining the Triggering Build."

    # This is a work-around for the fact TFS Release does not have any variables for the triggering build ID\Number (only has the Primary Artifact).
    # Hack is to get it from the Release's description, which is populated with trigger information IF it has CI-Trigger enabled.

    $buildChanged = $false

    # Parse the ReleaseDescription. Expected format: "Triggered by <ArtifactAlias> <BuildNumber>."
    # TODO a better way to do this? ... regex.
    $a,$b,$c,$d = $ReleaseDescription.split(' ')
    $TriggeringBuildNumber = $d.Remove($d.Length-1)
    Write "TriggeringBuildNumber = $TriggeringBuildNumber"

    if( $TriggeringBuildNumber -ne $script:buildNumber )
    {
        # Get the BuildId:
        $uri = "$tfsServer/$teamProject/_apis/build/builds?api-version=4.1&buildNumber=$TriggeringBuildNumber"
        Write "Invoking Method : GET $uri"
        $Build = Invoke-RestMethod -Method Get -UseDefaultCredentials -Uri "$uri"
        if( $Build[0] -and $Build.value -and $Build.value.Count -eq 1 -and $Build.value[0].id -ne 0)
        {
            $script:BuildId = $Build.value[0].id
            $script:buildNumber = $TriggeringBuildNumber    # = $Build.value[0].buildNumber
            $script:buildURI = $Build.value[0].url
            Write  "Build Found - ID is $BuildId."
            $buildChanged = $true
        }
        else
        {
            throw "Build not found"
        }
    }

    if( $buildChanged ) 
    {
        Write "buildID changed to $script:buildID"
        Write "buildNumber changed to $script:buildNumber"
        Write "buildURI changed to $script:buildURI"
    }
}
else
{
    # Allow for manually triggered build (but it has to be the primary artifact for this to work)
    $TriggeringBuildNumber = $script:buildNumber
}


# Determine the DropLocation from the BuildId.
# This is a work-around for the fact TFS Release does not have a variable for the DropLocation.
$BuildId = $script:buildID
Write "Get the DropLocation for buildId = $BuildId"
$DropLocation = ""
$DropLocationRoot = ""

# Get the build's artifacts
$uri = "$tfsServer/$teamProject/_apis/build/builds/$BuildId/artifacts?api-version=4.1"
Write "Invoking Method : GET $uri"
$artifacts = Invoke-RestMethod -Method Get -UseDefaultCredentials -Uri "$uri"

# Find the drop location (filepath)
foreach( $artifact in $artifacts.value )
{
    if( $artifact.name -eq $TriggeringBuildNumber -and $artifact.resource -and $artifact.resource.type -eq "filepath" -and  $artifact.resource.data )
    {
        $DropLocationRoot = $artifact.resource.data
        Write  "Artifact Found - DropLocationRoot is $DropLocationRoot."
        Break
    }
}
if( $DropLocationRoot -eq "" )
{ 
    throw "DropLocation not found."
}

$DropLocation = "$DropLocationRoot$TriggeringBuildNumber"
Write  "DropLocation = $DropLocation"

if( Test-Path $DropLocation )
{
    Write  "Yay! - Drop folder Found at $DropLocation. :-)"
    $DeploymentScript = "$DropLocation$DeploymentScript"
    if( Test-Path $DeploymentScript )
    {
        Write  "... and Yay, the Deployment Script was Found at $DeploymentScript. :-)"
    }
    else
    {
        throw "... but whoops! - Deployment Script was NOT Found. :-("
    }
}
else
{
    throw "whoops! - DropLocation [$DropLocation] does not exist. :-("
}


# Pass the values back out via $env vars.

# Note: tried using the below to reset the standard TFS System Variables, but it doesn't seem to take effect:
#Write-Host "##vso[task.setvariable variable=BUILD_BUILDID]$script:buildID"
#Write-Host "##vso[task.setvariable variable=BUILD_BUILDNUMBER]$script:buildNumber"
#Write-Host "##vso[task.setvariable variable=BUILD_BUILDURI]$script:buildURI"

Write-Host "##vso[task.setvariable variable=TRIGGERINGBUILDID]$script:buildID"
Write-Host "##vso[task.setvariable variable=TRIGGERINGBUILDNUMBER]$script:buildNumber"
Write-Host "##vso[task.setvariable variable=TRIGGERINGBUILDURI]$script:buildURI"
Write-Host "##vso[task.setvariable variable=DROPLOCATION]$DropLocation"