VStest code coverage report in jenkins - jenkins

I am setting CI for .Net project using Jenkins.
I used MSTest Plugin and VStestrunner plugin to run test.
Now I have .trx file and .Coverage file
I am facing problem in displaying code coverage report
Please help me is you know any plugin to do this.

I have struggled this for a long time, finally I found we can use "CodeCoverage.exe" "ReportGenarator.exe" and "Cobertura plugin" to show perfect coverage report.
"ReportGenarator.exe" can be get from https://github.com/danielpalme/ReportGenerator/releases
first use "CodeCoverage.exe" translate .coverage file to .xml file
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Dynamic Code Coverage Tools\CodeCoverage.exe" analyze -output:./TestResults/coverage.xml ./TestResults/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.coverage"
second use ReportGenarator.exe translate vstest xml format to Cobertura xml format
"ReportGenerator_4.4.7\net47\ReportGenerator.exe" -reports:./TestResults/coverage.xml -targetdir:./TestResults -reporttypes:cobertura
finally install cobertura plugin use it to collect xml file, here give a pipeline useage example
post {
always {
cobertura coberturaReportFile: './TestResults/Cobertura.xml'
}
}
the result just like this

To display the coverage report you need to convert it in XML format and use MSTest Plugin to publish the report. MSTest Plugin recommends (https://wiki.jenkins-ci.org/display/JENKINS/MSTest+Plugin) to use third party application to convert in XML format and powershell (you will need to install pugin for it) to run it.
However you can convert it with PowerShell only. There is example of script:
$coverageFile = $(get-ChildItem -Path .\TestResults -Recurse -Include *coverage)[0]
$xmlCoverageFile = ".\TestResults\vstest.coveragexml"
Add-Type -path "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Coverage.Analysis.dll"
[string[]] $executablePaths = #($coverageFile)
[string[]] $symbolPaths = #()
$info = [Microsoft.VisualStudio.Coverage.Analysis.CoverageInfo]::CreateFromFile($coverageFile, $executablePaths, $symbolPaths);
$data = $info.BuildDataSet()
$data.WriteXml($xmlCoverageFile)
You maybe will need to fix the path to Microsoft.VisualStudio.Coverage.Analysis.dll according to your VS version.

Following ghking's answer, cobertura complains that the xml is not found although it's on the disk. I have to remove './' from the path, so that cobertura is able to find the file.
post {
always {
cobertura coberturaReportFile: 'TestResults/Cobertura.xml'
}
}

The complete script to do this is:
<#
.SYNOPSIS
Script to convert code coverage report into xml format that can then be published by external tools.
.DESCRIPTION
Covering code coverage statistics as part of quality improvement initiatives.
#>
Param(
[String] $InputCoveragePath =#("..\GeneratedFiles\Docs\Reports"),
[String] $OutputCoverageFileExtension =#(".coveragexml"),
[String] $CoverageAnalysisAssembly =#("Microsoft.VisualStudio.Coverage.Analysis.dll"),
[String[]] $ExecutablePaths =#(""),
[String[]] $SymbolPaths =#("")
)
$ScriptLocation = Split-Path $script:MyInvocation.MyCommand.Path -Parent
Write-Host $ScriptLocation
$RunAs32Bit = {
Param(
[String] $InputCoveragePath =#("..\GeneratedFiles\Docs\Reports"),
[String] $OutputCoverageFileExtension =#(".coveragexml"),
[String] $CoverageAnalysisAssembly =#("Microsoft.VisualStudio.Coverage.Analysis.dll"),
[String[]] $ExecutablePaths =#(""),
[String[]] $SymbolPaths =#(""),
[String] $ScriptLocation =#(".")
)
Write-Host "[CoverageConverter][Begin]: Coverage conversion started..."
Write-Host "[CoverageConverter][InputCoveragePath]: $InputCoveragePath"
Write-Host "[CoverageConverter][OutputCoverageFileExtension]: $OutputCoverageFileExtension"
Write-Host "[CoverageConverter][CoverageAnalysisAssembly]: $CoverageAnalysisAssembly"
Write-Host "[CoverageConverter][ExecutablePaths]: $ExecutablePaths"
Write-Host "[CoverageConverter][SymbolPaths]: $SymbolPaths"
Write-Host "[CoverageConverter][ScriptLocation]: $ScriptLocation"
Add-Type -path "$CoverageAnalysisAssembly"
$Result = 0
if($InputCoveragePath -and (Test-Path "$InputCoveragePath") )
{
[string[]] $coverageFiles = $(Get-ChildItem -Path $InputCoveragePath -Recurse -Include *coverage)
#($coverageFiles) | ForEach-Object {
$coverageFile = $_
$coverageFileOut = (Join-Path -Path $(Split-Path $_ -Parent) -ChildPath ($(Get-Item $_).BaseName + "$OutputCoverageFileExtension"))
Write-Host "If all OK the xml will be written to: $coverageFileOut"
$info = [Microsoft.VisualStudio.Coverage.Analysis.CoverageInfo]::CreateFromFile($coverageFile, $ExecutablePaths, $SymbolPaths);
if($info){
$data = $info.BuildDataSet()
$data.WriteXml($coverageFileOut)
}
}
}
else
{
Write-Host "Please specify a valid input coverage file."
$Result = 1
}
Write-Host "[CoverageConverter][End]: Coverage conversion completed with result $Result"
return $Result
}
#Run the code in 32bit mode if PowerShell isn't already running in 32bit mode
If($env:PROCESSOR_ARCHITECTURE -ne "x86"){
Write-Warning "Non-32bit architecture detected, processing original request in separate 32bit process."
$Job = Start-Job $RunAs32Bit -RunAs32 -ArgumentList ($InputCoveragePath, $OutputCoverageFileExtension, $CoverageAnalysisAssembly, $ExecutablePaths, $SymbolPaths, $ScriptLocation)
$Result = $Job | Wait-Job | Receive-Job
}Else{
$Result = Invoke-Command -ScriptBlock $RunAs32Bit -ArgumentList ($InputCoveragePath, $OutputCoverageFileExtension, $CoverageAnalysisAssembly, $ExecutablePaths, $SymbolPaths, $ScriptLocation)
}

Related

Failed to create Release artifact directory error after cancelling Release and starting new one

This seems to only happen if I cancel a release deployment and then start a new one. It forces me to go into the agents and manually restart them.
The actual error is..
"Failed to create Release artifact directory 'C:\agent_work\r3\a'. ---> System.IO.IOException: The process cannot access the file '\?\C:\agent_work\r3\a' because it is being used by another process."
Is there a way in TFS to clean up any of these potential issues when creating a new release after a cancelled one? If I let it fully run its course, the new release runs fine no problem. This only happens when I cancel and attempt to start a new one.
You can use utility like handle to write a script that releases locked files or folders.
For example:
$pathToRelease = $env:System.DefaultWorkingDirectory
Write-Host "$PathToRelease is locked! trying to kill the process..."
$processes = path\to\handle64.exe -nobanner -accepteula $PathToRelease
# Remove empty lines
$processes = $processes | Where-Object {$_ -ne ""}
Write-Host $processes.ForEach({ Write-Host $_ })
if($processes -notmatch "No matching handles found.")
{
foreach($process in $processes)
{
# Some excluded processes, you can decide what which you want
if($process -match "explorer.exe" -or $process -match "powershell.exe"
{
continue
}
$pidNumber = $process.Substring(($process.IndexOf("pid") + 5),6)
$isProcessStillAlive = Get-Process | Where-Object {$_.id -eq $pidNumber}
if($Null -ne $isProcessStillAlive)
{
Stop-Process -Id $pidNumber -Force
Start-Sleep -Seconds 1
}
}
}
else
{
exit 0
}
Configure the script to run even the release is canceled.

How to clone code from multiple sources in TFS 2017?

My code is in TFS repository but due to some reason few files are in Sharepoint/MS Teams, how can we clone code from both the sources in the build definition.
Get Sources task is the default which clones the specified TFS repository, is there a way to add or edit this task to clone code from Sharepoint at the same time.
You cannot edit the Get Sources task to clone code from sharepoint.
However, you can use a powershell task to download the files from the sharepoint.
For example, add a powershell task in your pipeline to run below inline scripts:
Using WebClient
$SharePointFile = "https://the.server/path/to/the/file.txt"
$Path = "$(Build.SourcesDirectory)\file.txt"
#User Information
$Username = "userName"
$Password = "password"
#Download Files
$client = New-Object System.Net.WebClient
$client.Credentials = New-Object System.Net.Networkcredential($UserName, $Password)
$client.DownloadFile($SharePoint, $Path)
$client.Dispose()
Using Invoke-WebRequest
$User = "userName"
$PWord = ConvertTo-SecureString -String "password" -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
$url = 'https://the.server/path/to/the/file.txt'
$outfile = "$(Build.SourcesDirectory)\file.txt"
Invoke-WebRequest -Uri $url -OutFile $outfile -Credential $Credential
Above script will download the file from your sharepoint server to the source code folder $(Build.SourcesDirectory) on the agent machine (ie. c:\agent_work\1\s)
You can also use SharePoint Pnp PowerShell Framework to download the files in powershell task. See example in this blog.

Is there a PowerShell variable that can be checked to see if you the script is running in the Azure cloud shell?

Something similar to $IsLinux or $IsWindows.
From your description, I am not sure how did you run your script. If you use Start-Job to run the PowerShell script as a background job, you could use Get-Job to see the State.
Start-Job -FilePath ./GetDate.ps1
Get-Job
This is an old, but I had the same question and found this environment variable that works for my purposes. I'm not sure if there's another "official" way though.
PS> $env:AZUREPS_HOST_ENVIRONMENT
cloud-shell/1.0
I would suggest running a command in a try/catch that is specific to cloud shell, if that is what you are looking for?
try {
$sessioninfo = Get-CloudDrive
if ($sessioninfo) {
Write-Host "Running in Cloud Shell mode..." -ForegroundColor Green
$path = ($home + "/clouddrive/" + "report" + "-$(get-date -Format yyyyddMM_hhmmtt).csv")
}
}
catch {
Write-Host "Running in local PowerShell session mode..." -ForegroundColor Yellow
$path = "C:\localpath\report"+"-$(get-date -Format yyyyddMM_hhmmtt).csv"
}

Jenkins Groovy Script not recognizing windows Drive

Using the following groovy script in my Jenkinsfile to do some file operations for preparing my build package:
pipeline {
agent any
stages {
stage('package-windows') {
when {
expression { isUnix() == false && env.JOB_NAME == 'my-job-webapi'}
}
steps {
bat label: 'unzip all files', script: 'FOR /R .\\archive %%I IN (*.zip) DO "C:\\Program Files\\7-Zip\\7z.exe" x "%%I" -aou -o"%%~dpI\\*"'
}
}
}
}
When i run the job its failing with the following error:
\Program was unexpected at this time.
C:\Program Files (x86)\Jenkins\workspace\my-job-webapi>FOR /R .\archive \Program Files\7-Zip\7z.exe" x "~dpI\*"[Pipeline] }
For some reason its unable to recognize the drive letter C: in the path "C:\\Program Files\\7-Zip\\7z.exe". What is the right way to provide the path with windows drive letter in Groovy script ? Or is there a different way this needs to be handled ?
Just needed to use / instead of \\. C:/Program Files/7-Zip/7z.exe worked

Declarative Jenkins with WithCredentials and Powershell

stage('Deployment') {
steps {
withCredentials([string(credentialsId: 'Test', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
powershell '$pass = ConvertTo-SecureString -AsPlainText "${PASSWORD}" -Force'
powershell '$SecureString = "${pass}"'
powershell '$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "${USERNAME}","${SecureString}"'
powershell 'New-PSSession -ComputerName 192.123.123.123 -Credential "${MySecureCreds}"'
}
powershell 'Copy-Item "${ARTIFACT_PATH}" -Destination "${DESTINATION_PATH}" -ToSession -Recurse -Force'
powershell 'Start-Process "iisreset.exe" -NoNewWindow -Wait'
powershell 'Remove-Website -Name WebCareRecord'
powershell 'Remove-WebAppPool WebCareRecord'
powershell 'Get-WebBinding -Port 85 -Name WebCareRecord | Remove-WebBinding'
powershell 'Start-Process "iisreset.exe" -NoNewWindow -Wait'
powershell 'New-WebAppPool -Name WebCareRecord'
powershell 'Set-ItemProperty "${POOL_PATH}" managedPipelineMode 0'
powershell 'Set-ItemProperty "${POOL_PATH}" managedRuntimeVersion ""'
powershell 'New-WebSite -Name WebCareRecord -Port 85 -PhysicalPath "${PHYSICAL_PATH}" -ApplicationPool WebCareRecord'
powershell 'Start-Process "iisreset.exe" -NoNewWindow -Wait'
}
}
I am trying to get the Jenkins credentials ID, secure it and use the same credentials to login into the remote server. After login to the remote server, copy the artifact from jenkins server to remote server. For this I am getting error
org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException: Credentials 'Test' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected.
There might be multiple problems, I've going through similar process now and feeling the pain to get it correctly in powershell within groovy, so this is what I've noticed so far:
You are creating a $pass variable in one powershell step, and then trying to access it in another powershell step, I don't think it will work that way, as another step might launch in different powershell session and that powershell variable is no longer there.?
I would try something like this:
stage('Deployment') {
steps {
withCredentials([string(credentialsId: 'Test', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
powershell """
\$pass = ConvertTo-SecureString -AsPlainText $PASSWORD -Force
\$SecureString = \$pass
"""
}
}
}
So first of all you use multiline syntax """ so the different powershell statements are in the same session, and those variables are available across powershell commands.
Second, you escape powershell variables \$pass and \$SecureString, so groovy does not try to expand them, and you don't escape variables where you are actually referring to groovy variables like $PASSWORD.
Note that $PASSWORD does not have to be in quotes, since powershell parameters can accept strings without quotes, but if this was used in a method you should put it into quotes SomePowershellMethod("$GROOVYVAR").
In general I suggest to echo every variable while troubleshooting, to see if you are getting what you are expecting.
I like short and precise answer.
Use following :
stage('Deployment') {
steps {
withCredentials([usernamePassword(credentialsId: 'UatServer', passwordVariable: 'passVar', usernameVariable: 'userVar')]) {
powershell '''
$passVar = ConvertTo-SecureString "$($ENV:passVar)" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ("$ENV:userVar", $passVar)
$session = New-PSSession -ComputerName x.x.x.x -Credential $credential
Invoke-Command -Session $session -ScriptBlock {hostname}
'''
}
}
}

Resources