How to get the unit test results in variables in Azure DevOps Pipelines? - azure

I have a build pipeline in Azure DevOps and I'm using the .NET Core task for applying unit testing.
I need to get the result of the unit tests in variables. For example, if there are 10 tests cases and two failed, I need to get something like:
failedTestCases = 2
succeededTestCases = 8
This is because I need those values in the next tasks. Is there a way to do that?
To be clear, I don't need to publish the results, they are already being published, I need to get those values in execution time.

Yes, this is possible but in my opinion you need to use REST API. Below you will find part of build definition. There are three steps:
test app
get test details
display test details
For you very important part is to figure out which log id you have for your test. Bascially if your test task is on 5th position on this list (including Initialize job):
You need to add 3 and you have your logId. In my case this is 8.
variables:
devopsAccount : 'thecodemanual'
projectName : 'DevOps Manual'
logId: "8"
- task: DotNetCoreCLI#2
displayName: Test
inputs:
command: test
projects: 'dotnet-core-on-windows/*Tests/*.csproj'
arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage" -- RunConfiguration.DisableAppDomain=true'
workingDirectory: $(rootDirectory)
- task: PowerShell#2
condition: always()
name: testDetails
inputs:
targetType: 'inline'
script: |
# Encode the Personal Access Token (PAT)
$AzureDevOpsAuthenicationHeader = #{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$(System.AccessToken)")) }
# Get a list of releases
$uri = "https://dev.azure.com/$(devopsAccount)/$(projectName)/_apis/build/builds/$(Build.BuildId)/logs/$(logId)?api-version=5.1"
Write-Host $uri
# Invoke the REST call
$result = Invoke-RestMethod -Uri $uri -Method Get -Headers $AzureDevOpsAuthenicationHeader
Write-Host $result
$lines = $result.Split([Environment]::NewLine)
$passed = 0;
$failed = 0;
foreach($line in $lines) {
if ($line -match "Passed:.(\d+)") {
$passed = $matches[1]
}
if ($line -match "Failed:.(\d+)") {
$failed = $matches[1]
}
}
echo $passed
echo $failed
Write-Host "##vso[task.setvariable variable=passed]$passed"
Write-Host "##vso[task.setvariable variable=failed]$failed"
- script: |
echo $(passed)
echo $(failed)
condition: always()
And for this I got:
So it means we have number of passed and failed tests in variables ready to use.

Related

Failing Azure DevOps build pipeline if the build has any warning

Hi There I have been asked to modify all current CI yml pipelines to fail if the C# API or WEB app has any warning, also it needs to be in its own stage in the process.
I have been looking on the net and can't find any code please can someone help with the code needed
thanks
There are two options for your reference.
1.You could add a PowerShell task in the stage, and then run the Rest API: Timeline - Get to traverse the warning messages in the previous task. And use logging commands to control the results.
PowerShell script:
$token = "PAT"
$url="https://dev.azure.com/{OrgName}/{ProjName}/_apis/build/builds/$(build.buildid)/timeline?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$count = 0
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
ForEach( $issues in $response.records.issues )
{
if($issues.type -eq "warning")
{
echo $issues.Message
$count ++
}
}
echo $count
if($count -ne 0 )
{
Write-Host "##vso[task.complete result=Failed;]"
}
Pipeline Output:
2.Using Extension task: Build Quality Checks is another option.
Add this task to your target stage to check the warnings.

How can I know if the pipeline is using a Microsoft-hosted agent or self-hosted agent

While running an azure build pipeline (yml version) is there any programmatic way (inside the pipeline itself) to know if the current pipeline is running on the ms-hosted agent or self-hosted agent?
We have one pre-defined variable called 'Agent.Name' which gives the agent name.
But it keeps changing the agent name (Hosted, Azure)
Agent.Name=Hosted, Agent.Name=Azure
Is there any way to determine the type of agent at the pipeline run time.
- task: Bash#3
displayName: Show Agent Name
inputs:
targetType: 'inline'
script: |
echo $(Agent.Name)
No built-in feature to achieve your requirements.
But we can 'for-each' the 'azure pipeline agent pool' to get all of the Microsoft-agent names in it. And then compare.
trigger:
- none
# pool:
# name: VMAS
# 1
stages:
- stage: s1
displayName: get the Microsoft host agents' names
jobs:
- job: testJob
steps:
- task: PowerShell#2
name: setvar
inputs:
targetType: 'inline'
script: |
# logic here. For example you get the vars and put it into this format:
$PAT = "<Your Personal Access Token>"
$org_name = "<Your Organization Name>"
$pool_id = <The microsoft hosted agent pool id>
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic "+$PAT)
$url = "https://dev.azure.com/"+$org_name+"/_apis/distributedtask/pools/"+$pool_id+"/agents?api-version=6.0"
$response = Invoke-RestMethod $url -Method 'GET' -Headers $headers
$response | ConvertTo-Json
$str = "";
foreach ($item in $response.value) {
$str += $item.name
$str += ","
}
Write-Host $str
Write-Host "##vso[task.setvariable variable=outputvars;isOutput=true]$str"
# 2
- stage: s2
displayName: check whether current agent name is one of the above
dependsOn: s1
variables:
vars: $[ stageDependencies.s1.testJob.outputs['setvar.outputvars'] ]
jobs:
- job:
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
$varsArr = "$(vars)".Split(',')
$microsofthostagent = 0
foreach ($var in $varsArr)
{
if ($var -eq "$(Agent.Name)")
{
$microsofthostagent = 1
}
else
{
}
}
if( $microsofthostagent -eq 1){
Write-Host "This pipeline is based on Microsoft Host Agent."
}else{
Write-Host "This pipeline is based on Self Host Agent."
}
By default, the self host agent will not have the same name as the Microsoft host agent.
You just need to be careful not to name the self host agent the same as the agent in the Microsoft agent pool (eg "Hosted Agent", "Azure Pipelines ")
On my side, it works:
Microsoft hosted agent
Self hosted agent
As we are using both the ms-hosted & self-hosted so as a solution I started verifying the names of my self-hosted agents (these names are already known to us) & based on this I am able to pick the MS-hosted agents

How to get build name on Azure?

My goal is to get the build name from Azure YAML pipeline code.
For example, here it is #20220803.9 JBS-2413 Do not approve yet, this is an experiment of automating the tra...
I looked here but no luck
https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml
I tried Build.DefinitionName it returns PR
I tried Build.BuildNumber it returns 20220803.9 only
I tried Build.SourceVersionMessage it returns Merge
No built-in predefined variables or other things to get the "build name" you want. If you intercept the network traffic analysis, you will also find that the "build name" you want is not a whole, it is a combination of two data.
You need to design your own code to get it. For example, if you are based on Azure Git Repo, below pipeline YAML can help you get the "build name" you want when creating a pull request to trigger the pipeline:
trigger:
- none
# 1
stages:
- stage: s1
displayName: Get the lastest comment
jobs:
- job: testJob
steps:
- task: PowerShell#2
name: setvar
inputs:
targetType: 'inline'
script: |
$PAT = "<Your Personal Access Token>"
$org_name = "<Organization Name>"
$project_name = "<Project Name>"
$branch_name = "<Current Branch Name>"
$def_id = "<Pipeline definition ID>"
$run_id = "$(Build.BuildId)"
Write-Host $run_id
if("$(Build.Reason)" -eq "PullRequest") {
$headers_run = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers_run.Add("Authorization", "Basic "+$PAT)
$run_url = "https://dev.azure.com/"+$org_name+"/"+$project_name+"/_apis/pipelines/"+$def_id+"/runs/"+$run_id+"?api-version=6.0-preview.1"
$response_run = Invoke-RestMethod $run_url -Method 'GET' -Headers $headers_run
$response_run | ConvertTo-Json
$pull_request_id = $response_run.variables."system.pullRequest.pullRequestId".value
$headers_pr = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers_pr.Add("Authorization", "Basic "+$PAT)
$pr_url = "https://dev.azure.com/"+$org_name+"/"+$project_name+"/_apis/git/repositories/ShowBuildName/pullrequests/"+$pull_request_id+"?api-version=6.0"
$response_pr = Invoke-RestMethod $pr_url -Method 'GET' -Headers $headers_pr
$response_pr | ConvertTo-Json
Write-Host $response_pr.title
$str = $response_pr.title
Write-Host "##vso[task.setvariable variable=outputvars;isOutput=true]$str"
}
else {
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Basic "+$PAT)
$url = "https://dev.azure.com/"+$org_name+"/"+$project_name+"/_apis/git/repositories/ShowBuildName/commits?searchCriteria.itemVersion.version="+$branch_name+"&api-version=6.0"
$response = Invoke-RestMethod $url -Method 'GET' -Headers $headers
$response | ConvertTo-Json
$str = $response.value[0].comment
Write-Host "##vso[task.setvariable variable=outputvars;isOutput=true]$str"
}
# 2
- stage: s2
displayName: Get the "build name"
dependsOn: s1
variables:
vars: $[ stageDependencies.s1.testJob.outputs['setvar.outputvars'] ]
jobs:
- job:
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
Write-Host "$(Build.BuildNumber) $(vars)"
Pipeline run:
Successfully get the "build name":

Integrate CI and CD together Azure Devops

we need your support on enabling continues deployment on our release pipeline .
Environment :
CI or Build Pipeline is on Azure Devops Services
CD or Release pipeline is on Azure Devops Server
We want to Integrate CI and CD together right now after Build release is not kicking automatically.(I have to manually execute the release )
[![enter image description here][1]][1]
[![enter image description here][2]][2]
[![enter image description here][3]][3]
Service connection between azure devops service and azure devops server
[![enter image description here][4]][4]
# Trigger Release pipeline
- task: PowerShell#2
displayName: 'Trigger Release pipeline'
inputs:
targetType: 'inline'
powershell: |
$url = "https://vsrm.dev.azure.com/{OrganizationName}/{ProjectName}/_apis/release/releases?api-version=6.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($env:TOKEN)"))
$JSON = #'
{
"definitionId": 38,
"variables": {
"Version": {
"value": "$(build.buildnumber)"
}
}
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -ContentType application/json -body $JSON
displayName: 'PowerShell Script'
env:
TOKEN: $(token)```
[1]: https://i.stack.imgur.com/g4J8I.png
[2]: https://i.stack.imgur.com/njsVU.png
[3]: https://i.stack.imgur.com/MIaJJ.png
[4]: https://i.stack.imgur.com/20wk9.png
We want to Integrate CI and CD together right now after Build release is not kicking automatically.
Since the azure devops service is on the cloud side and the azure devops server is local, there is no out-of-the-box feature that can Integrate CI/CD.
But you could use PowerShell task to run the Rest API in Azure Devops Service to trigger the Release on Azure Devops Server . Releases - Create
Here is an example:
You can add the Powershell Task to the end of the build, then you could add the following script in the powershell task:
$token = "PAT"
$url = "https://{instance}/{collection}/{project}/_apis/release/releases?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$JSON = #'
{
"definitionId": DefinitionID(e.g. 15),
"description": "Creating Sample release",
"artifacts": [],
"isDraft": false,
"reason": "none",
"manualEnvironments": null
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -ContentType application/json -body $JSON
If your CI/Build pipeline is running on self-hosted agent, you can directly add the powershell task at the same agent job.
If your build pipeline is running on Microsoft-hosted agent, you need to create a self-hosted agent and add additional agent job to run powershell script.
In this case, you also need to set the Dependencies.
Note: When running the rest api to trigger the azure devops server release, you need to ensure that they are in the same network range. So it needs self-hosted agent.
Update:
To define a stage, you could refer to the following doc and sample:
stages:
- stage: A
jobs:
- job: A1
pool:
name: Default
steps:
- script: echo
- stage: B
pool:
name: Default
jobs:
- job: B1
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
$token = "PAT"
$url = "https://{instance}/{collection}/{project}/_apis/release/releases?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$JSON = #'
{
"definitionId": ID,
"variables": {
"Version": {
"value": "$(Build.buildnumber)"
}
}
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -ContentType application/json -body $JSON
Update2:
In order to achieve a function similar to the system.accesstoken variable, you can try the following settings.
Step1: Create a variable in Azure Devops Service Build Pipeline and set it as variable:
Step2: PowerShell Task script:
- powershell: |
$url = "https://{instance}/{collection}/{project}/_apis/release/releases?api-version=5.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($env:TOKEN)"))
$JSON = #'
{
"definitionId": 38,
"variables": {
"Version": {
"value": "$(build.buildnumber)"
}
}
}
'#
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Post -ContentType application/json -body $JSON
displayName: 'PowerShell Script'
env:
TOKEN: $(token)

Trigger Azure DevOps pipeline from another pipeline

I was looking at the azure triggers documentation and still not able to find an appropriate solution.
How during the execution of pipeline 1 can you trigger pipeline 2, wait for it to successfully finish or fail, and based on pipeline 2 results either continue execution of pipeline 1 or fail?
How during the execution of pipeline 1 can you trigger pipeline 2, wait for it to successfully finish or fail, and based on pipeline 2 results either continue execution of pipeline 1 or fail?
Trigger one pipeline after another, it will run your pipeline upon the successful completion of the triggering pipeline. We cannot use it to trigger pipeline 1 in the execution of pipeline 1.
As a workaround:
a. We can add task power shell and add script to call the REST API to queue the build.
$connectionToken="PAT"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$PipelineUrl = "https://dev.azure.com/{Org name}/{project name}/_apis/pipelines/{Pipeline ID}/runs?api-version=6.0-preview.1"
$body ="{
`"resources`":{
`"repositories`":{
`"self`":{`"refName`":`"refs/heads/master`"
}
}
}
}"
$Pipelines = Invoke-RestMethod -Uri $PipelineUrl -ContentType "application/json" -Body $body -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method POST
b. Add task power shell and enter the code Start-Sleep -Seconds 1000 to sleep the pipeline 1
c. Add the task power shell in the pipeline 1 to get the pipeline 2 build result via the REST API, and set the result as env variable.
d. Configure the condition in the next task to check the env variable value. If the value is succeeded, continue run the pipeline 1
You are probably looking for something like this.
# this is being defined in app-ci pipeline
resources:
pipelines:
- pipeline: securitylib # Name of the pipeline resource
source: security-lib-ci # Name of the pipeline referenced by the pipeline resource
trigger:
branches:
- releases/*
- master
its right there in the link, you have linked, but in the sibling section of the docs. I am surprised you missed it.
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/pipeline-triggers?view=azure-devops&tabs=yaml
So here is my solution based on the suggestion above:
- task: PowerShell#2
displayName: Running second pipeline
inputs:
targetType: 'inline'
script: |
Write-Host "Triggering pipeline..."
$connectionToken= "$(PAT)"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$PipelineUrl = "https://dev.azure.com/YourOrganization/yourProject/_apis/pipelines/${{ parameters.pipelineId }}/runs?api-version=6.0-preview.1"
Write-Host "Pipeline url: $PipelineUrl"
$body ="{
`"resources`":{
`"repositories`":{
`"self`":{`"refName`":`"refs/heads/${{ parameters.branch }}`"
}
}
}
}"
$response = Invoke-RestMethod -Uri $PipelineUrl -ContentType "application/json" -Body $body -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method POST
Write-Host "Response: $response"
$BuildUrl = "https://dev.azure.com/YourOrganization/yourProject/_apis/build/builds/$($response.Id)?api-version=6.1-preview.6"
Write-Host $BuildUrl
$TimeoutAfter = New-TimeSpan -Minutes 15
$WaitBetweenPolling = New-TimeSpan -Seconds 10
$Timeout = (Get-Date).Add($TimeoutAfter)
do
{
$Response = Invoke-RestMethod -Uri $BuildUrl -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
Write-Host $Response.status
Start-Sleep -Seconds $WaitBetweenPolling.Seconds
}
while ((($Response.status -eq "notStarted") -or ($Response.status -eq "inProgress")) -and ((Get-Date) -lt $Timeout))
if ($Response.result -ne "succeeded")
{
Write-Host $Response.result
exit 1
}
parameter for pipeline id: pipelineId: $(resources.pipeline.resource.pipelineId)
If you're ok with using extensions, the Trigger Build Task you can get in the marketplace should support all of your requirements.
It lets you trigger another pipeline, with an option to wait for it, and options about how to treat failures of that pipeline if you do wait. So you can use that to trigger a build, wait for it, and succeed / fail based on if the build succeeds / fails.

Resources