Get Build status by Commit SHA1 - rest

Our team is using TeamCity for continuous integration and Git-TFS (TFS2015 RC) for versioning.
I want to check if a specific Commit has been built (successfully) by TeamCity and thus can be reintegrated.
It is possible to query TeamCity builds by using their REST API, e.g.:
GET http://teamcity:8000/guestAuth/app/rest/builds/18
with the result containing some xml that shows the git commit SHA:
<revisions count="1">
<revision version="96b1db05b64ecc895da070137e93cde3d2cadfa1">
<vcs-root-instance [...]/>
</revision>
</revisions>
The fact, that this information is in theory available, makes me hope that it is possible to query TeamCity builds by this particular information, like:
GET http://teamcity:8000/guestAuth/app/rest/builds/revision:96b1db05b64ecc895da[...]
but this yields a #400 BAD REQUEST response. I was not able to find out in the TeamCity 9 Documentation, whether this is possible or not. I would rather not iterate all builds to check if they contain this specific commit.
NOTE: This feature has now been implemented by JetBrains and is available in TeamCity 9.1 EAP4 now.

I don't believe this can be done without iteration, which is a little annoying
You can view changes by hash
/httpAuth/app/rest/changes?version:SHA_HASH
and you can find changes by build locator
/httpAuth/app/rest/changes?locator=build:(id:BUILD_ID)
but you can't go the other way, otherwise this could be done simply.
The buildLocator doesn't allow you to query using a revision dimension so I can't see any way around this
The following script may be of use to you if you haven't already written it yourself - save this to a file called get-build-status-by-git-commit.ps1 so it works with the sample at the end
# -----------------------------------------------
# Get Build Status By Git Commit
# -----------------------------------------------
#
# Ver Who When What
# 1.0 DevOpsGuys 01-07-15 Initial Version
# Script Input Parameters
param (
[ValidateNotNullOrEmpty()]
[string] $TeamCityServer = $(throw "-TeamCityServer is mandatory, please provide a value."),
[ValidateNotNullOrEmpty()]
[string] $ApiUsername = $(throw "-ApiUsername is mandatory, please provide a value."),
[ValidateNotNullOrEmpty()]
[string] $ApiPassword = $(throw "-ApiPassword is mandatory, please provide a value."),
[ValidateNotNullOrEmpty()]
[string] $GitSha = $(throw "-GitSha is mandatory, please provide a value.")
)
function Main()
{
$CurrentScriptVersion = "1.0"
$ApiCredentials = New-Object System.Management.Automation.PSCredential($ApiUsername, (ConvertTo-SecureString $ApiPassword -AsPlainText -Force))
Write-Host "================== Get Build Status By Git Commit - Version"$CurrentScriptVersion": START =================="
# Log input variables passed in
Log-Variables
Write-Host
# Set initial query url
$queryBuilds = "/httpAuth/app/rest/builds?fields=nextHref,build(id,status,revisions)"
while($queryBuilds)
{
$buildsToCheck = Api-Get "$TeamCityServer$queryBuilds"
$queryBuilds = $buildsToCheck.builds.nextHref;
foreach($build in $buildsToCheck.builds.build)
{
if ($build.revisions.revision.version -eq $GitSha) {
Write-Host "STATUS: "$build.status
Exit 0
}
}
}
Write-Host "================== Get Build Status By Git Commit - Version"$CurrentScriptVersion": END =================="
}
function Log-Variables
{
Write-Host "TeamCityServer: " $TeamCityServer
Write-Host "GitSha: " $GitSha
Write-Host "Computername:" (gc env:computername)
}
function Api-Get($Url)
{
Write-Host $Url
return Invoke-RestMethod -Credential $ApiCredentials -Uri $Url -Method Get -TimeoutSec 20;
}
Main
You can use this in the following way
.\get-build-status-by-git-commit.ps1 "http://teamcity:8000/" username password 96b1db05b64ecc895da070137e93cde3d2cadfa1
This is using httpAuth, but you could easily tailor the script to be using guest. I've used httpAuth in case it's of use to anyone else.
Hope this helps

I was looking for the same thing and stumbled upon this question. I don't know if you still need this but however I found the API in TeamCity v10. But you have to know your BuildTypeID for this
https://${teamcity.domain}/app/rest/buildTypes/id:${BuildTypeID}/builds/revision:${COMMIT_SHA}
You can find your build type id from TeamCity UI by going to your specific build and then
Edit configuration settings >> General Settings
And then the value in the Build configuration ID field.
Hope this helps.

The related feature request in TeamCity issue tracker: https://youtrack.jetbrains.com/issue/TW-40540. Vote for it.
The current workaround is to request builds with their revisions included into the response and then find the necessary builds on the client side.
The request can look like:
.../app/rest/builds?locator=buildType(id:XXX)&fields=build(id,href,revisions(revision))

Recent versions of teamcity allow the 'version' locator such that the following will return builds where the version SHA matches the query.
http://teamcity:8111/app/rest/changes?locator=version:SHA_HASH

I'm on TeamCity 2019.2, and I was able to directly search the builds endpoint for a specific commit:
TEAMCITY_URL/app/rest/builds/revision:SHA_HASH.
Not sure what version it was implemented in.

Related

Don't trigger builds for branches that already have a pull request in Azure DevOps

We use Azure DevOps for continuous integration. The pipeline is configured to run a build whenever a change is pushed to a feature branch. This is desired for quick feedback.
Additionally, we have the policy for the master branch that a successful validation build is required before a feature branch can be merged. Azure DevOps now automatically triggers the corresponding validation build when a pull request (PR) is created for a feature branch.
All of this is fine, but there is an adversity: if a PR is already created and the feature branch is updated, two builds are triggered (one for the feature branch alone and one for the outcome of the merge, i.e., the validation build).
I understand that some people might want both builds, but in our case (an probably in every normal case) it would be better if only the validation build was triggered.
Question: Is there a way to tell Azure DevOps that it should ignore branch triggers for any branch that already has a PR? Workarounds with an equivalent outcome are also welcome, of course.
The question has already been posted as an issue here, but I could not find a satisfying answer in the replies (e.g., branch filters and a naming strategy do not solve the problem).
"Out-of-box" - you can not. However as a workaround, you can use rest API to check active pull requests and if they exist just fail your unneeded build:
Get Pull Requests - Targeting a specific branch
Use the system access token from your build pipeline. Access repositories, artifacts, and other resources
Exit with Powershell from a build: exit 1
Because that's the design, thus, both build will be triggered but you could have try with follow method to avoid the build queued at the same time.
In the build validation policy - disable automatic queuing. Or mark the PR as draft, while changes are being worked on. After this change any of the changes will only trigger CI build/pipeline, and when ready just publish the PR or queue the PR manually.
I have solved the issue like suggested by Shamrai. I'm adding this as another answer to share my code.
Add the following PowerShell code to a step at the beginning of the pipeline. It uses the Azure DevOps REST API to check for an existing PR of the current branch. If there is no such PR or if the current build was manually queued, or if the PR is not ready to be merged (e.g. conflicts), the build continues as normal. Otherwise, it is cancelled.
$BaseApiUri_Builds = "https://my.tfs.server.com/MyCollection/MyProject/_apis/build/builds"
$BaseApiUri_GitRepos = "https://my.tfs.server.com/MyCollection/MyProject/_apis/git/repositories"
$AuthenicationHeader = #{ Authorization = "Bearer ${env:System_AccessToken}" }
# Cancels the current build
function Cancel-This-Build()
{
Cancel-Build -BuildId ${env:Build_BuildId}
}
# Cancels the given build
function Cancel-Build([String] $BuildId)
{
Write-Host "Cancelling build ${BuildId}..."
$BuildApiUri = "${BaseApiUri_Builds}/${BuildId}?api-version=5.1"
$CancelBody = #{ status = "cancelling" } | ConvertTo-Json
Invoke-RestMethod -Uri $BuildApiUri -Method PATCH -ContentType application/json -Body $CancelBody -Header $AuthenicationHeader
}
# Detects if a validation build is queued for the given branch. This is the case if an active PR exists that does not have merge conflicts.
function Check-For-Validation-Build([String] $BranchName)
{
Write-Host "Checking for validation builds of branch '${BranchName}' in repository ${env:Build_Repository_ID}..."
$GetPRsApiUri = "${BaseApiUri_GitRepos}/${env:Build_Repository_ID}/pullrequests?api-version=5.1&searchCriteria.sourceRefName=${BranchName}"
$PRs = Invoke-RestMethod -Uri $GetPRsApiUri -Method GET -Header $AuthenicationHeader
ForEach($PR in $PRs.Value)
{
Write-Host "Found PR $($PR.pullRequestId) '$($PR.title)': isDraft=$($PR.isDraft), status=$($PR.status), mergeStatus=$($PR.mergeStatus)"
if (!$PR.isDraft -and ($PR.mergeStatus -eq "succeeded") -and ($PR.status -eq "active"))
{
Write-Host "Validation build is queued for branch '${BranchName}'."
return $true
}
}
Write-Host "No validation build is queued for branch '${BranchName}'."
return $false
}
# Check if a valid PR exists. If so, cancel this build, because a validation build is also queued.
$HasValidationBuild = Check-For-Validation-Build -BranchName ${env:Build_SourceBranch}
if (($HasValidationBuild -eq $true) -and (${env:Build_Reason} -ne "Manual") -and !${env:Build_SourceBranch}.EndsWith('/merge'))
{
Cancel-This-Build
}
Note that the System.AccessToken environment variable must be made visible for the script to work.

Access SourceBranchName in Release pipeline

I'm trying to release my buildartifacts to a specific folder based on the name of the sourcebranch which upon creating a pull request triggered the build and therefor the release.
I've managed to so far to get:
write-host $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
$triggerAlias = $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
This alias (from my point of view) is the primary artifcat alias which I need to access
Release.Artifacts.{Primary artifact alias}.SourceBranchName
based on this documentation. So how do I combine the alias to get the sourcebranchname
$env:RELEASE_ARTIFACTS_{$triggerAlias}_SOURCEBRANCHNAME
This doesn't seem to be working and neither does
$env:RELEASE_ARTIFACTS_$($triggerAlias)_SOURCEBRANCHNAME
Any advice is much appreciated.
You can read the variable in this way:
$triggerAlias = $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
$branchNameVariable = "RELEASE_ARTIFACTS_$($triggerAlias)_SOURCEBRANCHNAME"
#Get the value of the environment variable Release.Artifacts.{alias}.SourceBranchName
$branchName = (Get-item env:$branchNameVariable).Value

TFS API: Clone Build Definition Environment

I want to clone an existing environment within a build definition.
This is possible through the TFS GUI, but it doesn't seem like the API natively supports it.
So far, I've tried the following (in PowerShell):
Download build definition and deserialize
[void][System.Reflection.Assembly]::LoadFile("$pwd\Newtonsoft.Json.dll")
$TargetDefinitionName = "Name"
$TargetDefinitionID = ($DefinitionsOverview | Where-Object { $_.name -eq $TargetDefinitionName } | Select-Object -First 1).id
$TargetDefinitionURL = "$TfsUri/$TargetDefinitionID"
$TargetDefinitionJSON = Invoke-WebRequest -Uri "$TargetDefinitionURL" -UseDefaultCredentials
$DeserializedBuildDefinition = [Newtonsoft.Json.JsonConvert]::DeserializeObject($TargetDefinitionJSON.Content)
$DeserializedBuildDefinition.ToString()
Duplicate JSON block that represents environment, change unique properties
$NewEnvironmentString = $DeserializedBuildDefinition.environments[4].ToString()
$DeserializedBuildDefinition.environments.Add(5)
$DeserializedBuildDefinition.environments[5] = $NewEnvironmentString
$DeserializedBuildDefinition.environments[5].name.value = "NewEnvironment"
$DeserializedBuildDefinition.environments[5].rank.value = "6"
$DeserializedBuildDefinition.environments[5].id.value = "1665"
$DeserializedBuildDefinition.revision.value = "20"
Serialize JSON and post back (with new environment)
$SerializedBuildDefinition = [Newtonsoft.Json.JsonConvert]::SerializeObject($DeserializedBuildDefinition)
$PostData = [System.Text.Encoding]::UTF8.GetBytes($SerializedBuildDefinition)
$Headers = #{ "Accept" = "api-version=2.0-preview" }
$Response = Invoke-WebRequest -UseDefaultCredentials -Uri
$TargetDefinitionURL -Headers $Headers `
-Method Put -Body $PostData -ContentType "application/json"
$Response.StatusDescription
PROBLEM: $Response.StatusDescription gives "OK," but no new environment appears in the build definition.
One thought is that, in addition to 'name,' 'ID,' and 'rank, there are other values that need to be unique per environment that I'm missing.
I've also tried cloning the environment manually, saving the JSON representation of the definition, deleting the environment, and posting the JSON back. The new environment still doesn't show up.
Any help would be appreciated.
Future: Yaml based build definitions as code are well on their way, at least to VSTS. This won't help an immediate need but probably offers a better and more lightweight future approach.
When you copy an environment, you need to change "id", "name", and "rank". And the “id” need to be changed to "0". Also, try to use api-version=2.3-preview.1.
Then use the api of update a release definition to update the definition:
PUT http://tfs2015:8080/tfs/teamprojectcollection/teamProject/_apis/release/definitions?api-version=2.3-preview.1
{
……
}
I have tested with TFS 2015 Update 4. Api could clone an environment successfully.
I created a tool for cloning TFS builds, as (with TFS 2015 update 1 at least) the web 'clone' function does not clone the 'Repository' or 'Triggers' tab. Some of our build steps were a little complicated so I also wrote some simple transforms to automatically set the values according to the target branch.
I used the BuildHttpClient object in Microsoft.TeamFoundation.Build2.WebApi assembly to get the original build definition (BuildDefinition object, using GetDefinitionAsync() method), transform any build steps values, then call CreateDefinitionAsync() on BuildHttpClient to create the new build.
Works very reliably and under the hood it appears to use the TFS REST API.
If there was any interest, I'd be happy to create a more in depth tutorial on this.

Accessing AppVeyor Project Environment Variables during manual deploy

I'm new to AppVeyor, I work for a web agency where we have many projects in separate GIT repos.
Each project has a development branch which is what I'm watching in AppVeyor. Since we have a single internal development server running IIS, it was a good candidate to define our development server as an AppVeyor Environment. The development server is running the AppVeyor Agent.
Inside the project specific YAML files I'm specifying the environment name and a custom environment variable which I've defined.
environment:
iis_site_name: project-specific-site-name.com
deploy:
- provider: Environment
name: dev-environment
I've configured the AppVeyor environment to accept the environment variable from the project like this.
AppVeyor Environment - Note: websitebuild is the "deploy name" associated with the artifact.
This work's perfectly on a commit, the project is built and deployed to the Agent in the correct location.
Where this is not working is when I need to kick off a manual deploy. So lets say I want to kick off a manual deploy by going into the AppVeyor interface and selecting Environments > Dev Environment > New Deploy > Select Project
When this deploy runs the environment variable from the YAML file (iis_site_name) is not seen and a new IIS site named 'default' is created and the site is deployed there.
Note that I have also tried adding the environment variable in the project settings via the GUI (as opposed to in the YAML) and it behaves no differently.
I believe it is expected behavior, when build-time environment variables are not available, Appveyor will send to agent default value which you define at Environment level variables. Please look here
At the bottom of that screen we are defining its “default” value, i.e.
the value used when you deploy from Environments and build environment
variables are not present
As I understand this does not fit your scenario, because you need different site name for different projects. What you can do is to create separate Environment for every project (with separate default site names), but replace all their environment access keys with the same you use for you current environment, thus agent will pick up jobs from all of them.
Update:
This is not best solution according to Marty’s comments. Lets use REST API then.
Documentation is here https://www.appveyor.com/docs/api/environments-deployments/#start-deployment
Check sample PowerShell function below. You can just call it with project-specific parameters when needed.
Note that you also have to set websitebuild.hostname to $(iis_site_name) in Deployment provider setting for sites use host header instead of * bindings on IIS, otherwise they will fight over the TCP port ;)
function start-appveyorDeploy
{
param (
[Parameter(Position=0, Mandatory=$true)]
[string]$projectSlug,
[Parameter(Position=1, Mandatory=$true)]
[string]$buildVersion,
[Parameter(Position=2, Mandatory=$true)]
[string]$siteName
)
$apiUrl = 'https://ci.appveyor.com/api'
# Replace this with your values
$token = <your_token>
$environmentName = <your_environmentName>
$accountName = <your_accountName>
$headers = #{
"Authorization" = "Bearer $token"
"Content-type" = "application/json"
}
$body = #{
environmentName=$environmentName
accountName=$accountName
projectSlug=$projectSlug
buildVersion=$buildVersion
environmentVariables =#{
iis_site_name=$siteName
}
}
$jsonBody = $body | ConvertTo-Json -Depth 3
Invoke-RestMethod -Uri "$apiUrl/deployments" -Headers $headers -Body $jsonBody -Method Post
}

TFS2015 Variables Issue (Build.SourceBranch and Build.SourceBranchName)

From: https://connect.microsoft.com/VisualStudio/Feedback/Details/2264644
According to: https://msdn.microsoft.com/en-us/library/vs/alm/build/scripts/variables
Build.SourceBranch should map to $/teamproject/branch and Build.SourceBranchName should map to "branch" where the full path is $/teamproject/branch
I have a branch structure like $/MyProject/Development/MyBranch and when I try to use these variables in PowerShell (i.e. using BUILD_SOURCEBRANCH and BUILD_SOURCEBRANCHNAME), I am getting:
BUILD_SOURCEBRANCH: $/MyProject
BUILD_SOURCEBRANCHNAME: MyProject
From my understanding of the article, the variables should return $/MyProject/Development/MyBranch and MyBranch respectively.
Anyone else able to repro this behaviour with a TFVC repository? If you set up a vNext build definition with just a PowerShell task and add the following snippet, you'll be able to see the available variables in the output:
[CmdletBinding()]
param()
$environmentVars = get-childitem -path env:*
foreach($var in $environmentVars)
{
$keyname = $var.Key
$keyvalue = $var.Value
Write-Output "${keyname}: $keyvalue"
}
I have to modify the answer as I have new find. After double checking the MSDN article, I found for TFVC BUILD_SOURCEBRANCH will be the root server path for the workspace. So I tried mapping the branch path to Server Path, please refer to the screenshot below. After specifying the Server Path to the branch path, you will get correct value of BUILD_SOURCEBRANCH.