Fail a merge or build if a particular file has changed? - azure-devops

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.

Related

How to get source branch from google cloud build trigger on push when merging

I am merging 'feature-branch' onto 'dev-branch' from a github pull request. I am using a google cloud build trigger that is triggered on push to 'dev-branch'. From what I can tell in the documentation of substitution variables, there are no substitution variables to get the name of the branch that I am merging from - 'feature-branch' and only the branch that I merging to - 'dev-branch'. Is there a way or a workaround to get information (name, sha, id, etc.) of the branch that is being merged from on a google cloud build trigger from a push to branch event?
Presumably you can have some naming conventions on the first line of the commit message (happens at merge pull request "event"), so that this line includes the source (or head) branch name (the source of the merge - in your words - 'feature-branch').
Then, you can create a substitution variable:
substitutions:
_COMMIT_MESSAGE: $(commit.commit.message)
here is a documentation link: Creating substitutions using payload bindings
And use that variable in some build step to get the the head branch name:
mapfile -t commit_lines <<< "${_COMMIT_MESSAGE}"
source_branch="$(echo ${commit_lines[0]} | <<add your command here following naming convention for the commit message>> )"
echo "=> My source branch name: ${source_branch}"
After that you can use the source branch name.

Does Azure YAML pipelne support wildcards in path filter in trigger?

I have this structure of projects (folders) in git repository:
/src
/src/Sample.Backend.Common
/src/Sample.Backend.Common.Tests
/src/Sample.Backend.Common.Domain
/src/Sample.Backend.Common.Domain.Tests
/src/Sample.Backend.Pricing.Abstractions
/src/Sample.Backend.Pricing.Domain
/src/Sample.Backend.Pricing.Domain.Tests
/src/Sample.Backend.Pricing.Persistence
/src/Sample.Backend.Pricing.Persistence.Tests
/src/Sample.Backend.Accounting.Abstractions
/src/Sample.Backend.Accounting.Domain
/src/Sample.Backend.Accounting.Domain.Tests
/src/Sample.Backend.Accounting.Persistence
/src/Sample.Backend.Accounting.Persistence.Tests
/src/Sample.Backend.Api
/src/Sample.Common
/src/Sample.Frontend.Common
/src/Sample.Frontend.Web
/src/Sample.Tests.Common
(The sample is simplified, in real there are much more projects/folders.)
I want different pipelines for different parts. For example a pipeline to be triggered whenever any file is commited in master branch in any Backend project. Something like this:
trigger:
branches:
include:
- master
paths:
include:
- src/Sample.Backend.*
- src/Sample.Common
- src/Sample.Tests.Common
The problem is, that filter src/Sample.Backend.* is not working. I have to add exact name of each Backend folder to get it working. I could use exclude but I have the same problem - there are many other projects and I would have to name them all.
I found that wildcards are not supported: https://github.com/MicrosoftDocs/azure-devops-docs/issues/397#issuecomment-422958966
Is there any other way to achieve the same result?
Does Azure YAML pipelne support wildcards in path filter in trigger?
This is a known request on our main forum for product:
Support wildcards (*) in Trigger > Path Filters
This feature has not yet been implemented; you could add your comment and vote this on user voice.
As a workaround for us, we add an inline PowerShell task as the first task to execute the git command line git diff HEAD HEAD~ --name-only then get the modified file names and filter the files name in the latest submit, and use Logging Command to sets variables which are then referenced in custom conditions in the next steps in the build pipeline:
and(succeeded(), eq(variables['CustomVar'], 'True'))
Our inline PowerShell script:
cd $(System.DefaultWorkingDirectory)
$editedFiles = git diff HEAD HEAD~ --name-only
echo "$($editedFiles.Length) files modified:"
$editedFiles | ForEach-Object {
echo $_
Switch -Wildcard ($_ ) {
'XXXX/Src/Sample.Backend.*' {
Write-Host ("##vso[task.setvariable variable=CustomVar]True")
}
'XXXX/Src/Sample.Common*' {
Write-Host ("##vso[task.setvariable variable=CustomVar]True")}
'XXXX/Src/Sample.Tests.Common' {
Write-Host ("##vso[task.setvariable variable=CustomVar]True")}
}
}
Then add the condition for all remaining tasks:
In this case, if the changed files do not meet our filters, then all remaining tasks will be skipped.
UPDATE: 09/09/2021
This is possible now as it is written here
Wild cards can be used when specifying inclusion and exclusion branches for CI or PR triggers in a pipeline YAML file. However, they cannot be used when specifying path filters. For instance, you cannot include all paths that match src/app//myapp*. This has been pointed out as an inconvenience by several customers. This update fills this gap. Now, you can use wild card characters (, *, or ?) when specifying path filters.
Note: documentation seems to be not updated yet.
Old answer:
No this is not possible at the moment. You have even feature request here and I would recommend to upvote it. (I already did this) Rick in above mentioned topic shared his idea how to overcome the issue:
I currently achieve this by having 3 files:
azure-pipelines.yml ( This calls some python on each commit )
azure-pipelines.py (This checks for changed folders and has some parameters to ignore certain folders, then calls the API directly)
azure-pipelines-trigger.yml ( This is called by the python based on the changed folders )
It works well enough, but it is unfortunate for the need to go through these loops.
But it needs an extra work.
This feature will roll out over the next two to three weeks according to the latest release notes
Update on this.
It took a few weeks but the change mentioned by pavlo in the comments above finally got rolled out and path triggers are now supported in YAML.

VSTS REST API for creating TFVC Branch

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"
}

Referencing current branch in github readme.md

In my github repo's readme.md file I have a Travis-CI badge. I use the following link:
https://travis-ci.org/joegattnet/joegattnet_v3.png?branch=staging
The obvious problem is that the branch is hardcoded. Is it possible to use some sort of variable so that the branch is the one currently being viewed?
Not that I know of.
GitHub support confirms (through OP Joe Gatt 's comment)
The only way to do this would be to pass the link through my own service which would use the github's http referrer header to determine which branch is being referenced and then fetch the appropriate image from Travis CI
I would rather make one Travis-CI badge per branch, for the reader to choose or consider the appropriate when seeing the README.md.
Update 2016 (3 years later): while nothing has changed on the GitHub side, fedorqui reports in the workaround mentioned in "Get Travis Shield on Github to Reflect Selected Branch Status" by Andrie.
Simply display all the branches and their respective TravisCI badges.
If you have only two or three branches, that could be enough.
I worked around this issue with a git pre-commit hook that re-writes the Travis line in the README.md with the current branch. An example of usage and pre-commit (Python) code (for the question as asked) are below.
Usage
dandye$ git checkout -b feature123 origin/master
Branch feature123 set up to track remote branch master from origin.
Switched to a new branch 'feature123'
dandye$ echo "* Feature123" >> README.md
dandye$ git add README.md
dandye$ git commit -m "Added Feature123"
Starting pre-commit hook...
Replacing:
[![Build Status](https://travis-ci.org/joegattnet/joegattnet_v3.png?branch=master)][travis]
with:
[![Build Status](https://travis-ci.org/joegattnet/joegattnet_v3.png?branch=feature123)][travis]
pre-commit hook complete.
[feature123 54897ee] Added Feature123
1 file changed, 2 insertions(+), 1 deletion(-)
dandye$ cat README.md |grep "Build Status"
[![Build Status](https://travis-ci.org/joegattnet/joegattnet_v3.png?branch=feature123)][travis]
dandye$
Python code for the pre-commit code
dandye$ cat .git/hooks/pre-commit
#!/usr/bin/python
"""
Referencing current branch in github readme.md[1]
This pre-commit hook[2] updates the README.md file's
Travis badge with the current branch. Gist at[4].
[1] http://stackoverflow.com/questions/18673694/referencing-current-branch-in-github-readme-md
[2] http://www.git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
[3] https://docs.travis-ci.com/user/status-images/
[4] https://gist.github.com/dandye/dfe0870a6a1151c89ed9
"""
import subprocess
# Hard-Coded for your repo (ToDo: get from remote?)
GITHUB_USER="joegattnet"
REPO="joegattnet_v3"
print "Starting pre-commit hook..."
BRANCH=subprocess.check_output(["git",
"rev-parse",
"--abbrev-ref",
"HEAD"]).strip()
# String with hard-coded values
# See Embedding Status Images[3] for alternate formats (private repos, svg, etc)
# [![Build Status](https://travis-ci.org/
# joegattnet/joegattnet_v3.png?
# branch=staging)][travis]
# Output String with Variable substitution
travis="[![Build Status](https://travis-ci.org/" \
"{GITHUB_USER}/{REPO}.png?" \
"branch={BRANCH})][travis]\n".format(BRANCH=BRANCH,
GITHUB_USER=GITHUB_USER,
REPO=REPO)
sentinel_str="[![Build Status]"
readmelines=open("README.md").readlines()
with open("README.md", "w") as fh:
for aline in readmelines:
if sentinel_str in aline and travis != aline:
print "Replacing:\n\t{aline}\nwith:\n\t{travis}".format(
aline=aline,
travis=travis)
fh.write(travis)
else:
fh.write(aline)
subprocess.check_output(["git", "add", "README.md" ])
print "pre-commit hook complete."
I updated the work of Dan Dye so it's now able to change any git variable into a readme. It also works now with python 3. For example, handling badges by branch for Github actions:
[![Integration Tests](https://github.com/{{ repository.name }}/actions/workflows/integration-tests.yaml/badge.svg?branch={{ current.branch }})](https://github.com/{{ repository.name }}/actions/workflows/integration-tests.yaml?query=branch%3A{{ current.branch }})
And in your pre-commit file add:
.githooks/replace_by_git_vars.py readme.md README.md -v
-v displays the available variables and more
https://gist.github.com/jclaveau/af2271b9fdf05f7f1983f492af5592f8
Thanks a lot for the solution and inspiration!
The best solution for me was to create a server where I send a query with username and repo's name and get a svg image with the build status for all branches.

What is the meaning of an asterisk appended to a TFS changeset number

I am looking at candidate changesets for a merge and I get some output I don't understand:
tf merge /candidate /recursive $/Acme/Branches/Release/3.5 $/Acme/Trunk
Changeset Author Date
--------- -------------------------------- ----------
47829* nate:14 4/16/2009
What does the * at the end of the changeset number mean?
It indicates a partial merge was done.
To elaborate on Randy's answer: a partial merge means that some of the changes in #47829 have already been merged but others have not.
tf merges uses the same syntax. Starting in 2008 (or maybe it was 2005 SP1?) there's also a /format:detailed parameter that will enumerate the merge history item-by-item.
In my experience it means those changesets have already been merged from the source branch to the target branch locally, but the changes have not been checked in on the target branch yet. If you check in pending changes on the target branch and then run "tf /merge /candidate ..." you shouldn't have any items with an asterisk.