14
votes

We are using Visual Studio Team Systems with Git and Team System Build (former Build vNext). When we conduct a Pull Request, a new Build is triggered that is used to run Unit Tests and deploy to an isolated test system. To perform the deployment to an isolated system I need to get the real source branch name inside the build process. However the Build.SourceBranchName variable is always "merge",

E.g.:

Pull Request from source FOO to target BAR Build.SourceBranch is "refs/pull/1/merge" and therefore Build.SourceBranchName is "merge". But i need to somehow get "FOO" to run my Power Shell script to configure the system.

Is there a way to get the real source branch name inside a Git Pull Request inside VSTS?

6

6 Answers

7
votes

VSTS now has System.PullRequest.SourceBranch and System.PullRequest.TargetBranch variables. That should solve your problem without writing any custom scripts

Build Variables

5
votes

There isn't any variable for this but you can create a power-shell script to get it via Rest API.

[String]$projecturi = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"
[String]$sourcebranch = "$env:BUILD_SOURCEBRANCH"
[String]$repoid = "$env:BUILD_REPOSITORY_ID"

$username="alternativeusername"
$password="alternativepassword"

$basicAuth= ("{0}:{1}"-f $username,$password)
$basicAuth=[System.Text.Encoding]::UTF8.GetBytes($basicAuth)
$basicAuth=[System.Convert]::ToBase64String($basicAuth)
$headers= @{Authorization=("Basic {0}"-f $basicAuth)}

#get pull request ID via regex
$pullrequest = "refs/pull/+(?<pullnumber>\w+?)/merge+"
if($sourcebranch -match $pullrequest){        
        $pullrequestid = $Matches.pullnumber;
    }
else { write-host "Cannot find pull request ID" }

#get pull request information via API
$url= $projecturi + "_apis/git/repositories/" + $repoid + "/pullRequests/" + $pullrequestid + "?api-version=1.0-preview.1"

Write-Host $url

$getpullrequest = Invoke-RestMethod -Uri $url -headers $headers -Method Get

#get sourcebranch and targetbranch
$sourceref = $getpullrequest.sourceRefName
$targetref = $getpullrequest.targetRefName
4
votes

You can create a bash script that assigns the shorter branch name to a variable.

# Bash script
BRANCH_NAME=$(echo "$(System.PullRequest.TargetBranch)" | awk -F/ '{print $NF}')
echo "##vso[task.setvariable variable=PullRequest_Target_Branch;]$BRANCH_NAME"

You can then reference $(PullRequest_Target_Branch) in your pipeline

2
votes

This script will read the parameters from environment variables if used inside a build task or use the supplied parameters if used outside a build task. The variable $sourcebranch will be set to be used in a later build task.

[CmdletBinding()]
param (
    [Parameter(Mandatory=$false)][string]$projectUri = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",
    [Parameter(Mandatory=$false)][string]$branch = "$env:BUILD_branch",
    [Parameter(Mandatory=$false)][string]$repositoryName = "$env:BUILD_REPOSITORY_NAME",
    [Parameter(Mandatory=$false)][string]$projectName = "$env:SYSTEM_TEAMPROJECT",
    [Parameter(Mandatory=$false)][string]$oAuthToken = "$env:SYSTEM_ACCESSTOKEN",
    [Parameter(Mandatory=$false)][string]$username,
    [Parameter(Mandatory=$false)][string]$password
)

#check all parameters
if(!$oAuthToken) {
    if(!$username -or !$password) {
        throw "You must either supply an OAuth Token or a username and a password. You can supply the token via the environment variable SYSTEM_ACCESSTOKEN"
    }

    $basicAuth= ("{0}:{1}"-f $username,$password)
    $basicAuth=[System.Text.Encoding]::UTF8.GetBytes($basicAuth)
    $basicAuth=[System.Convert]::ToBase64String($basicAuth)
    $headers= @{Authorization=("Basic {0}"-f $basicAuth)}
}
else {
    $headers= @{Authorization="Bearer $oAuthToken"}
}

if(!$projectUri) {
    throw "You must supply a project uri or set the Environment variable SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"
}

if(!$branch) {
    throw "You must supply a branch or set the Environment variable BUILD_branch"
}

if(!$repositoryName) {
    throw "You must supply a repository name or set the Environment variable BUILD_REPOSITORY_NAME"
}

if(!$projectName) {
    throw "You must supply a project name or set the Environment variable SYSTEM_TEAMPROJECT"
}

#get pull request ID via regex
$pullrequest = "refs/pull/+(?<pullnumber>\w+?)/merge+"
if($branch -match $pullrequest) {        
    $pullrequestid = $Matches.pullnumber;
    Write-Output "Pull request ID is $pullrequestid"
}
else { 
    Write-Output "Cannot find pull request ID" 
}

#get pull request information via API
$url= $projectUri + "DefaultCollection/$projectName/_apis/git/repositories/$repositoryName/pullRequests/$pullrequestid\?api-version=1.0-preview.1"
Write-Output "Getting info from $url"
$getpullrequest = Invoke-RestMethod -Uri $url -headers $headers -Method Get

#get sourcebranch and targetbranch ref
$sourceref = $getpullrequest.sourceRefName
$targetref = $getpullrequest.targetRefName

#get the branch name via regex
$branchref = "refs/heads/(?<realBranchname>.*)"
if($sourceref -match $branchref) {        
    $sourcebranch = $Matches.realBranchname;
    Write-Output "Real source branch is $sourcebranch"
}
else { 
    Write-Output "Cannot find real source branch" 
}
if($targetref -match $branchref) {        
    $targetbranch = $Matches.realBranchname;
    Write-Output "Real target branch is $targetbranch"
}
else { 
    Write-Output "Cannot find real target branch" 
}

#set a variable "sourcebranch" to use it in another build task
Write-Output "##vso[task.setvariable variable=sourcebranch;]$sourcebranch"
1
votes

You still cant use $(System.PullRequest.SourceBranch) for a BuildNumberFormat in your build definition without this error:

The build number format string $(BuildDefinitionName)-$(System.PullRequest.SourceBranch)-$(Date:yyyyMMdd)$(Rev:.r) generated a build number "MyBuildName-refs/heads/myBranch-20190831.1" which contains invalid character(s), is too long, or ends with '.'.

0
votes

Seems like nowadays you can use Build.SourceBranchName

The name of the branch in the triggering repo the build was queued for. Git repo branch or pull request: The last path segment in the ref. For example, in refs/heads/master this value is master. In refs/heads/feature/tools this value is tools.

Source