Configure a CI build for each commit

February 5, 2018

By default in TFS/VSTS, the CI build configuration is used to trigger a build after each push. Additionally there is a option to “batch changes” (queue a build with all the changes since the last build instead of queue a build for each push).

However there are some cases where you want to build each introduced commit separately. It’s true that there is no way to do it automatically OOB, but in this post I want to share a way to achieve it with a little creativity (and code).

 

The Idea


Image

Create two different build definitions:

– Listener Definition: Catch every push, retrieve the commits related to the push and trigger a build for each one of them.

– Build Definition: contains the build configuration to be executed for each commit.

 

Build Definition


Configure the build definition:

Image

(In my case I created a very basic build definition that print its source commit and its source branch. However you can configure the build to do whatever you want)

 

Listener Definition


1.- Create the build definition

Image

 

2.- Set the build as continuous integration (no batch changes)

Image

 

3.- Set the branch trigger filter (dev branch in my case)

Image

 

4.- Create the “Credential” parameter (as secret parameter)

Image

 

5.- Create the “TargetBuildDefinitionName” parameter

Image

 

6.- Add the listener job script

Image

(Note the parameters passed to the script as arguments)

 

7.- Save the definition

 

Listener Job


(You can find the listener job script in GitHub)

The first step in the script is get the push that contains the commit that triggered the build:

$PushId = Get-PushIdByCommitId -BranchName $BranchName -CommitId $SourceVerison -CollectionUrl $CollectionUrl -TeamProject $TeamProject -GitRepository $GitRepository -Credentials $Credentials

 

Then, all the commit associated to the push are retrieved:

$Commits = Get-CommitsInPush -PushId $PushId -CollectionUrl $CollectionUrl -TeamProject $TeamProject -GitRepository $GitRepository -Credentials $Credentials

 

Next, get all the history for the branch (to know which commits inside the push are related to the branch):

$CommitsnBranch = (Get-CommitsByBranch -BranchName $BranchName -CollectionUrl $CollectionUrl -TeamProject $TeamProject -GitRepository $GitRepository -Credentials $Credentials).commitId

 

Then, create a list with all the commits introduced in the push that are related to the branch:

$CommitsInfo = @()

# Fill the list with all the commits introduced in the push
foreach($CommitId in ($Commits | Select commitId).commitId)
{
    # Filter only the commits for the branch
    if($CommitsnBranch -contains $CommitId)
    {
        $CommitInfo = Get-CommitById -CommitId $CommitId -CollectionUrl $CollectionUrl -TeamProject $TeamProject -GitRepository $GitRepository -Credentials $Credentials
        $CommitsInfo += $CommitInfo
    }
}

 

Now that we have the commits that we want to use, we need to order them in chronological order:

$CommitsInfo = $CommitsInfo | Select commitId,committer

foreach($value in $CommitsInfo) 
{ 
    $value.committer = $value.committer.date 
}   

$OrderedCommits = $CommitsInfo | Sort committer

 

Finally a build is triggered for each commit in the list:

foreach($Commit in $OrderedCommits)
{
    # Create queue body (build parameters) and trigger build definition
    $CommitSha1 = $Commit.commitId
    $TargetDefinitionId = Get-BuildDefinitionId -BuildDefinitionName $TargetBuildDefinitionName -CollectionUrl $CollectionUrl -TeamProject $TeamProject -Credentials $Credentials
    $QueueBody = @{ definition = @{id = $TargetDefinitionId}; sourceBranch = "refs/heads/$BranchName"; sourceVersion = $CommitSha1 }
    $Response = Start-BuildCustomBody -Body $QueueBody -CollectionUrl $CollectionUrl -TeamProject $TeamProject -Credentials $Credentials
         
    # Print build trigger request response
    if($Response -ne $null)
    {
        $BuildNumber = $Response.buildNumber
        Write-Host "Build [$BuildNumber] successfully trigger from commit [$CommitSha1]"
        Start-Sleep -Seconds 5
    }
    else 
    { 
        Write-Host "Error Triggering build from commit [$CommitSha1]" 
    }
}

 

Putting All Together


 

1.- Checkout the dev branch

$ git checkout dev

 

2.- Create new commits

$ echo content >> file.txt && git add -A && git commit -m "commit 1"
$ echo content >> file.txt && git add -A && git commit -m "commit 2"
$ echo content >> file.txt && git add -A && git commit -m "commit 3"
$ echo content >> file.txt && git add -A && git commit -m "commit 4"
$ echo content >> file.txt && git add -A && git commit -m "commit 5"

 

3.- Get commit id’s of created commits

$ git log --oneline -n 5

Image

 

4.- Push the new commits

$ git push

 

5.- Check the listener job log

Image

 

6.- Verify the builds were triggered

Image

 
 

Add comment
facebook linkedin twitter email

Leave a Reply