I am trying to automate some of my build related tasks, which includes creating new release branch and build definitions for each release. I use VSTS TFVC for version management. When i am trying to do it with TFS REST API, i couldn't find any API for creating branch (microsoft documentation).
I can see .NET API available to do this; unable to find one as REST API.
No such a REST API to create branch for now, I have submitted a user voice here to suggest the feature, you can go and vote it up to achieve that in future.
As a workaround you can try below ways to create a branch in code or script:
Just as #Shayki mentioned you can use Client Object Model Reference
if you want to manage the Version Control programmatically.
Just use the "CreateBranch()" method in
Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer
class to create a branch.
Besides, you can also use the Branch Command to create a branch.
tf branch olditem newitem [/version:versionspec] [/noget] [/lock:(none|checkin|checkout)] [/noprompt] [/silent] [/checkin] [/comment:("comment"|#commentfile)] [/author:authorname] [/login:username, [password]] [/recursive]
Just as what you see in "Branches" page, there isn't any way to create branch with the Rest API. And mostly, you can only read/get the information with the Version Control API for now.
If you don't want use C#, you can automate the process with Powerhshell:
param(
)
begin
{
# load the required dll's
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client")
}
process
{
$server = New-Object Microsoft.TeamFoundation.Client.TeamFoundationServer("http://tfsserver:8080/tfs/DefaultCollection")
$vcServer = $server.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]);
$changesetId = $vcServer.CreateBranch('$/Demo/Code/Main', '$/Demo/Code/Dev/Branch', [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest, $null, "New branch from script", $null, $null, $null)
"Branch created with ChangesetID: $changesetId"
}
Related
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.
Most of my builds are from either feature branches or develop, and so I tend to use a known build variable to track the build number such as:
variables:
- group: BuildVars
name: $(BuildPrefix)$(Rev:r)
This works and provides me with a nicely formatted build version that I can then follow through into releases, etc:
However, when we're planning a release, we name our branches after the release, for example: release/1.1, and I'd like to have the build name reference that instead of the hardcoded (previous) version.
I know that I can reference the branch name via the Build.SourceBranch variable, but I don't see an obvious way to read and modify that outside of a build step, by which time I believe it's too late? I don't really want to have to manually change the BuildPrefix variable until the release has been deployed out to production.
Building on from this would then be the ability to append appropriate pre-release tags, etc. based on the branch name...
you can always update the build name during the execution of a build using this:
- pwsh: |
Write-Host "##vso[build.updatebuildnumber]value_goes_here"
so you could calculate the value in the same (or previous step) and update the build name with that value
Is it possible to change the name of a build based on the branch name in Azure Pipelines?
The answer is yes.
The solution we currently use is add a Run Inline Powershell task to update build number based on the Build_SourceBranchName:
$branch = $Env:Build_SourceBranchName
Write-Host "Current branch is $branch"
if ($branch -eq "Dev")
{
Write-Host "##vso[build.updatebuildnumber]$DevBuildNumber"
}
elseif ($branch -eq "Beta")
{
Write-Host "##vso[build.updatebuildnumber]$BetaBuildNumber"
}
elseif ($branch -eq "Test")
{
Write-Host "##vso[build.updatebuildnumber]$TestBuildNumber"
}
Check the Logging Command during the build for some more details.
Hope this helps.
I am trying to write a PowerShell script which help developer to check-in their changes via PowerShell ISE and link the changeset to work item. I have TFS 2017 and TVFC is configured.
This can't be dont through tc vc commands, but since PowerShell can load the TFS Client Object Model, it's easy to use that to set some properties on the pending changes. You can use the VersionControlServer class to access the WorkSpace and associate the workitems using the WorkspaceCheckinParameters.AssociatedWorkItems class.
var checkinParameters = new WorkspaceCheckInParameters(changes, comment)
{
AssociatedWorkItems = ... <- Associate your work items here
};
return _workspace.CheckIn(checkinParameters);
On VSTS, we have some files we want to protect on certain branches. How can one fail a merge/build if a particular file has changed?
First prize is to configure this on the build server, which in this case is VisualStudio.com (VSTS / GIT).
Scenario: we have various release branches v1, v2, v3. We want to protect the packages.json file to prevent anyone updating Nuget packages on these branches. So if the package.json file has changed on a pull request into "v3", don't allow the merge.
For Git, you can protect a certain branch (not a certain file), then all the files exist in the branch will be protected.
You can use Branch security which users/groups can contribute for the branch.
Or you can use Branch Policies to protect not commit changes on the branch directly but use pull request to make changes etc.
Or you can lock a branch to prevent updating.
To expanded Starain's answer:
First create a build definition for the branch you want to protected (such as select V3 branch in get sources step). And add a powershell task with the content below:
$head=$(git rev-parse HEAD)
$parents=$(git rev-list --parents -n 1 $head)
$p1,$p2,$p3=$parents.split(' ')
If ($p1 = $head)
{
$parent1=$p2
$parent2=$p3
}
ElseIf ($p2 = $head)
{
$parent1=$p1
$parent2=$p3
}
Else
{
$parent1=$p1
$parent2=$p2
}
$outp1=$(git diff $head $parent1 --name-only)
$outp2=$(git diff $head $parent2 --name-only)
If ($outp1 -contains 'package.json')
{
echo "changed the specified file on the branch which contains commit $parent1"
exit 1
}
If ($outp2 -contains 'package.json')
{
echo "changed the specified file on the branch which contains commit $parent2"
exit 1
}
So that when the file package.json has been change, powershell script will fail the build result.
Then add the branch policy for the branch which you want to protect.
Add build policy -> select the build definition you just created -> Policy requirement as Required -> Build expiration 0.1 hours (6 min) or other values since it’s every fast to queue a build with a powershell task -> save.
You can try to do it in the build, simple workflow:
Configure branch policy for a succeed build required
Check whether the specific file changed in that build
Fail build if specific file has been changed
You can put a required reviewer for a particular folder/file in VSTS for a particular branch.
In this way the person won't be able to check-in without getting an approval from the required reviewer.
Git doesn't really work that way; individual files don't have any sort of security on them.
You could use a pre-commit hook, but it's important to note that those hooks are client-side, not server-side -- each user would have to set up a pre-commit hook.
VSTS/TFS doesn't support Git server hooks (at least, not to the extent that it can block a push), otherwise a pre-receive or update hook would be exactly what you want.
I am using Visual Studio Online - Package Manager Preview, along with the new build system. The package manager preview adds a number of build steps, including a "NuGet Publisher" step, which should push packages to the private feed hosted by Visual Studio Online.
Now the documentation is a little out of step here. As is the documentation on auth and personal access tokens. There are some indications that you shouldn't need auth between VSO and the Package Manager as long as you have permissions set up (the build service account has permissions to the service endpoint and to the package manager extension). The actual build step asks you to select from your list of Service Endpoints, so that is what I have attempted.
When I place no credentials on the Service Endpoint, I get the error:
Server Key must be set, set the password on the generic service
When I attempt to place the API key against the Service Endpoint it seems to be discarded on save... and the error changes to:
2015-11-18T08:35:24.5678951Z Invoking nuget with push C:\a\1\s\EventViewer\bin\Release\Project.Name.1.1.12.0.nupkg -s https://example.pkgs.visualstudio.com/DefaultCollection/_packaging/example/nuget/v3/index.json usfusmx4ez6mlfqwpp2abzc7e37denfcp7bxsep2hqij3tp4qwvq on C:\a\1\s\EventViewer\bin\Release\Project.Name.1.1.12.0.nupkg
2015-11-18T08:35:24.5688946Z C:\LR\MMS\Services\Mms\TaskAgentProvisioner\Tools\agents\default\agent\worker\tools\NuGet.exe push C:\a\1\s\EventViewer\bin\Release\Project.Name.1.1.12.0.nupkg -s https://example.pkgs.visualstudio.com/DefaultCollection/_packaging/Example/nuget/v3/index.json usfusmx4ez6mlfqwpp2abzc7e37denfcp7bxsep2hqij3tp4qwvq
2015-11-18T08:35:25.3467312Z Please provide credentials for: https://example.pkgs.visualstudio.com/DefaultCollection/_packaging/Example/nuget/v3/index.json
2015-11-18T08:35:25.3667189Z ##[error]Object reference not set to an instance of an object.
2015-11-18T08:35:25.3677179Z UserName: Password:
2015-11-18T08:35:25.4647059Z ##[error]Unexpected exit code 1 returned from tool NuGet.exe
I have also attempted to use a personal access token to no avail.
How do I get the publish step working?
The in-box NuGet Publish task has two options: "external" feeds and "internal" feeds. External feeds are intended for 3rd party services like NuGet.org, Artifactory, and expect a service connection with an API key.
Internal feeds are those hosted by Team Services. Instead of a service connection, you add the URL of the feed's NuGet endpoint. The build system relies on the Project Collection Build Service (for collection-scoped build definitions) or Project Build Service (for "this project"-scoped build defs) being a Reader or Contributor to the feed. Docs for all that are available here.
UPDATE: It is all fixed now so you can use the standard packaging steps in vNext and they work like a charm.
For the time being, I am substituting the NuGet Publisher step with a PowerShell build step.
This slots into the build after the "NuGet Packager" step and allows me to specify all of the credentials by setting up a package source before pusing the package.
$feedUrl = "https://example.pkgs.visualstudio.com/DefaultCollection/_packaging/Example/nuget/v3/index.json"
$packagePath = $ENV:BUILD_REPOSITORY_LOCALPATH + "\YourOrg.YourComponent." + $ENV:BUILD_BUILDNUMBER + ".nupkg"
Write-Host "Adding package Source"
$addSourceCommand = $ENV:BUILD_REPOSITORY_LOCALPATH + "\nuget sources add -name ""Example"" -source " + $feedUrl + " -username ""your.username"" -password ""yourpassword"""
Invoke-Expression -Command $addSourceCommand
Write-Host "Pushing package to NuGet"
$pushCommand = $ENV:BUILD_REPOSITORY_LOCALPATH + "\nuget push $packagePath -Source " + $feedUrl + " -ApiKey Example"
Invoke-Expression -Command $pushCommand
I landed here because I'm researching/configuring an internal deploy -- where I'm running my own NuGet Server (nuget.server, as opposed to visual studio online). The error was the same (or has similar text):
Object reference not set to an instance of an object
My solution, it turned out, was that the URL wasn't right.
The correct version is: http://server-name/NuGet/api/v2/package
For completeness, I had: http://server-name/NuGet/ which was wrong.