The goal
Goal of this post is to add powershell script task into Azure build pipeline, that will test or check custom conditions on your code base every time pull request is made. If those conditions are not met, script will let you know via comment on pull request, then will set build process as failed and cancel pull request autocomplete.
Preparation
You will need to create Personal access token. Almost all actions in our script can use $env:SYSTEM_ACCESSTOKEN
, but to cancel pull request Autocomplete, you need token with more permissions, otherwise you would be greeted with message saying You need the Git 'GenericContribute' permission to perform this action
.
Open up your Azure Devops, click on your profile icon and select Security. Click on New Token
, then enter name e.g. CI_BUILD_PR_CANCEL_AUTOCOMPLETE
, set expiration to 1 year in calendar, scroll down to Code scope, select Read & write
and click on Create
. Copy and save your newly generated token on secure place, e.g. KeePass.
If you want to play around with Azure Devops API, I recomend you to create another token with expiration 1 month and Full access
scope.
Postman
I use Postman to test all requests to AzureDevops API. I’ve exported my postman collection of requests, so all you need to do is, open Postman and click File
– Import...
Functions
First you need decide where you will keep your scripts. The post build script should be indepented from your source code, so I recomend you to create new repository where you will keep all your scripts, helpers and so on. But you have few more options:
Github – You can use gist as I did with CI_Functions_Core.ps1
script or public repo.
Inline PowerShell Script – Altough this is the easiest posible solution I do not recommend it. It’s messy, there is no syntax highlither at this moment and you want your scripts to be reusable. I use inline script only to "glue" all things together.
About the CI_Functions_Core.ps1 script
I’ve prepared script with few basic functions to handle Azure Devops Pull Request. The script itself is well commented so its good idea to just go trough the code and get to know what it does. In a nutshell, the script uses Azure DevOps Services REST API to acomplish all wanted actions. To authorize every request we use $env:SYSTEM_ACCESSTOKEN(more about that later).
Let’s take a look for example at Add-Comment
method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function Add-Comment(){ Param( [parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] [string] $Content ) if($Env:SYSTEM_PULLREQUEST_PULLREQUESTID){ $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/git/repositories/$(Get-RepositoryId)/pullRequests/$Env:SYSTEM_PULLREQUEST_PULLREQUESTID/threads?api-version=5.0" $jsonBody = ConvertTo-Json -InputObject @{ comments = @( @{ parentCommentId = 0 content = $Content } ) status = 1 threadContext = $null pullRequestThreadContext = $null } Invoke-RestMethod -Method 'Post' -Uri $url -Headers (Get-CommonHeaders) -Body $jsonBody } else{ Write-TaskError "Missing PullRequestId" } } |
We want to simply add comment to pull request timeline. Comments are groupped in threads(see the picture below) so, to add a comment we need to know threadId
. To get around that, we create new thread with single comment.
Editing Build Pipeline
Go to you Azure Devops page, select your project and go to builds page. If you already have some build pipeline, select it and click Edit
. Otherwise click on + New
and select New build pipeline
. I will call it CI_PowerShell_Test
.
Select Agent job
tab, click the plus sign, find PowerShell task on right and click Add
.
This is important step! Click on Agent Job
, scroll down to Additional options section, click to open it and check option Allow scripts to access the OAuth token. This will allow us to use $env:SYSTEM_ACCESSTOKEN
variable later in all scripts. More info here.
Now lets put all those peaces together! Select PowerShell task you created earlier, edit Display name
, set Type
to Inline, scroll down to Environment variables
and add variable with name PR_TOKEN
and value set to your personal access token(CI_BUILD_PR_CANCEL_AUTOCOMPLETE
) encoded to base64(don’t forget to prefix your token with colon before encoding it) you created earlier.
In a Script
field put this code:
1 2 3 4 5 6 7 8 9 10 11 12 |
# Include global script CI_Functions_Core.ps1 (new-object Net.WebClient).DownloadString("https://gist.githubusercontent.com/TomasBouda/ab6b5136be29d21aa625c448c6393e53/raw/CI_Functions_Core.ps1") | iex ################################## # Your code goes here... # Test comment functionality Add-Comment "Test comment" # If you defined your test script in Azure Repository you can include it with following command Get-FileFromRepository -RepositoryId "c5357e8b-b9f8-4f71-82a7-5f7512387aa6" -FilePath "Scripts\Functions.ps1" -Branch "develop" | iex # Now you can call all functions, you defined in your Scripts\Functions.ps1 file |
Save the build pipeline.
Quickly about the script
1 |
(new-object Net.WebClient).DownloadString("https://gist.githubusercontent.com/TomasBouda/ab6b5136be29d21aa625c448c6393e53/raw/CI_Functions_Core.ps1") | iex |
This line includes script from my gist. After that you can use all functions defined in it e.g. Add-Comment "Test comment"
. If you keep your scripts in another repository as I described here, you can include your script with this line of code Get-FileFromRepository -RepositoryId "Your repository id" -FilePath "Scripts\Functions.ps1" -Branch "master" | iex
. Just replace "Your repository id" with your actual repositoryId. If you don’t know repositoryId, use Postman and request this method to get list of all your repositories and their ids.
Building the Pull Request
If you’ve already setup branch policies to build every pull request on particular branch, you can skip following part.
Setting up branch policy
Go to your branches list, select branch e.g. master, click on the little tree dots ...
and select branch policies. Click on Add build policy
, select your build pipeline you created earlier CI_PowerShell_Test
, Build expiration
– Never and click Save
. Now every time there is new Pull Request on master branch, your build pipeline will be executed including your PowerShell scripts.
Build it!
Now when you have everything ready, create new pull request. New build should be queued and after it executes PowerShell task, you created earlier, you sholud see new comment in your PR Timeline.
Debuging
Here are few tips to debug Failed build:
- Disable other tasks – In build pipeline, select every task, but your PowerShell one, right click one of them and select
Disable selected task(s)
. - See build output – On pull request page Click the link Build failed. This will bring you to build log. Click on failed task and you will see full script output.
- Show debug output – You should use
Write-Debug
commands in your scripts. By default these messages are hidden. To show them add$DebugPreference = "Continue"
in your Inline PowerShell task. - Queue build again – This functionality is little bit hidden. On pull request page hover your mouse over the build policy(Build succeeded/Build failed), click the three little dots and select
Queue build
That’s it
You’re done. Well, this is just a start. Now it’s your turn. Add your scripts that will test your solution, simplify your workflow, notify you, you name it. Happy coding!