How do I detect if the test run was successful in a Team Build 2013 Post-Test script? - powershell

I have a build configuration in TFS 2013 that produces versioned build artifacts. This build uses the out of the box process template workflow. I want to destroy the build artifacts in the event that unit tests fail leaving only the log files. I have a Post-Test powershell script. How do I detect the test failure in this script?
Here is the relevant cleanup method in my post-test script:
function Clean-Files($dir){
if (Test-Path -path $dir) { rmdir $dir\* -recurse -force -exclude logs,"$NewVersion" }
if(0 -eq 1) { rmdir $dir\* -recurse -force -exclude logs }
}
Clean-Files "$Env:TF_BUILD_BINARIESDIRECTORY\"
How do I tests for test success in the function?

(Updated based on more information)
The way to do this is to use environment variables and read them in your PowerShell script. Unfortunately the powershell scripts are run in a new process each time so you can't rely on the environment variables being populated.
That said, there is a workaround so you can still get those values. It involves calling a small utility at the start of your powershell script as described in this blog post: http://blogs.msmvps.com/vstsblog/2014/05/20/getting-the-compile-and-test-status-as-environment-variables-when-extending-tf-build-using-scripts/

This isn't a direct answer, but... We just set the retention policy to only keep x number of builds. If tests fail, the artifacts aren't pushed out to the next step.
With our Jenkins setup, it wipes the artifacts every new build anyway, so that isn't a problem. Only the passing builds fire the step to push the artifacts to the Octopus NuGet server.

The simplest possible way (without customizing the build template, etc.) is do something like this in your post-test script:
$testRunSucceeded = (sqlcmd -S .\sqlexpress -U sqlloginname -P passw0rd -d Tfs_DefaultCollection -Q "select State from tbl_TestRun where BuildNumber='$Env:TF_BUILD_BUILDURI'" -h-1)[0].Trim() -eq "3"
Let's pull this apart:
sqlcmd.exe is required; it's installed with SQL Server and is in the path by default. If you're doing builds on a machine without SQL Server, install the Command Line Utilities for SQL Server.
-S parameter is server + instance name of your TFS server, e.g. "sqlexpress" instance on the local machine
Either use a SQL login name/password combo like my example, or give the TFS build account an account on SQL Server (preferred). Grant the account read-only access to the TFS instance database.
The TFS instance database is named something like "Tfs_DefaultCollection".
The "-h-1" part at the end of the sqlcmd statement tells sqlcmd to output the results of the query without headers; the [0] selects the first result; Trim() is required to remove leading spaces; State of "3" indicates all tests passed.
Maybe someday Microsoft will publish a nice REST API that will offer access to test run/result info. Don't hold your breath though -- I've been waiting six years so far. In the meantime, hitting up the TFS DB directly is a safe and reliable way to do it.
Hope this is of some use.

Related

squirrel.windows 1.7.8-1.9.0 Cannot produce Setup.exe and RELEASES file in -releaseDir

I am using the Azure DevOps Release pipeline to deploy a WPF application.
In the CI I have a copy files task with a target to build.artifactstagingdirectory
In the contents I include:
- src\SolutionDirectory\ProjectDirectory\bin\$(BuildConfiguration)**
- src\SolutionDirectory\ProjectDirectory\Release.nuspec
- packages\squirrel.windows.1.8.0**
In the CD i have two PowerShell scrips. One to the package operation and another for releasifying
Package
$exePath = "$(System.DefaultWorkingDirectory)/_ArtifactName/drop/src/SolutionDirectory/ProjectDirectory/bin/Release/Project.exe"
$version =$([System.Diagnostics.FileVersionInfo]::GetVersionInfo($exePath).FileVersion)
nuget pack .\Release.nuspec
-Version $version
-Properties Configuration=Release
-OutputDirectory .\bin\Release\
-BasePath .\bin\Release\
Releasify
Set-Alias squirrel "$(System.DefaultWorkingDirectory)/_ArtifactName/drop/packages/squirrel.windows.1.**/tools/squirrel.exe"
$exePath = "$(System.DefaultWorkingDirectory)/_ArtifactName/drop/src/SolutionDirectory/ProjectDirectory/bin/Release/Project.exe"
$version =$([System.Diagnostics.FileVersionInfo]::GetVersionInfo($exePath).FileVersion )
squirrel --releasify "$(System.DefaultWorkingDirectory)/_ArtifactName/drop/src/SolutionDirectory/ProjectDirectory/bin/Release/Project.$version.nupkg" --no-msi --releaseDir $(DevDeployDir)
The DevDeployDir is a variable pointing to a local server deployment directory.
I am only able to releasify .nupkg.
the RELEASES and Setup.exe are missing, i also tried to add a Start-Sleep command at the end because i was thinking the releasifying process might be stopping too early.No go.
Its like the powershell task on Azure DevOps is starting to run the Squirrel releasify in the backround but since squirrel is asynchronous(I was told so and looking through some bit of code it seems to be that way) it exits straight away and only a **.nupkg. and a **-full.nupkg are created. So i feel like its starting to transoform the package into a squirrel versioned one, but its stopping after the powershell command exits.
This does not happen when I manually releasify through powershell on the server driveI can see the files inside my working directory being generated in the following order
**.nupkg
**-full.nupkg
deletes **.nupkg
creates Setup,exe
optional Setup.msi
If anyone needs more info I would be glad to share. Does someone know if this is achievable?
Link to GitHub open issue
#Mahdi Khalili
Yeah, it needs to be a semantic version and this is not during installation but during releasifying so the squirrel log will not work here I don't think.
We just used the wait and pass thru parameters to the powershell wrapper around the releasifying process. It all worked afterwards.

Bamboo Powershell Task fails after first run

I'm completely new to Bamboo, so thank you in advance for the help.
I'm trying to create a Bamboo Run that zips files from a git repo and uploads it to Artifactory. Currently my build contains 2 tasks - source code checkout and a simple powershell script. The first time I run it it builds perfectly fine, but without any modifications any consecutive runs fail.
The error I'm getting in the log is the following:
Failing task since return code of [powershell -ExecutionPolicy bypass -Command /bin/sh /opt/bamboo/agent/temp/OR-J8U-JOB1-4-ScriptBuildTask-539645121146088515.ps1] was -1 while expected 0
Replacing the powershell script with empty space does not resolve the issue - only removing the script completely allows the build to succeed, but I cannot reinsert a new script or it will fail. I read other online questions suggesting that I "merge the user-level PATH environment information in to the system-level PATH" but I cannot find the user-level environment information, my environmental variables section is completely empty.
Like Vlad, I found that it was more efficient to implement my powershell script with batch.

TFS Build vNext - start process after successful build

On our build server, we have a running process that, among other things, are necessary for running integration tests, to gain access to a database.
This piece of software can be changed in our repository, and I have therefore created a CI build that builds the changes. What I would then like to do, is to restart the process with the new compiled version upon successful build, but that part I can not seem to get to work.
I have no issue killing the running process, and deploying the result to specific location on the build server, but seemingly no matter what I try, the process I spawn is killed as soon as the running build ends.
I have tried the following:
With PowerShell
Start-Process <path to file>
With CMD
with 'cmd' as tool and the following arguments:
<path to file>
start <path to file>
/c start <path to file>
cmd <path to file>
cmd start <path to file>
cmd /c start <path to file>
I have also tried simply supplying the .exe path as the tool name, and no arguments, no luck either.
How well did it work?
Well, for most of the above approaches, an extra step with the PS command Get-Process <exe name>* I got the result that the process was running. The same step yielded no results after a step that stopped the process, so a new updated one could be started. So I am positive it works, vNext build simply kills it all after the build ends.
Other solutions
I have got 2 solutions left that I can think of should work, but both solutions are too complicated for my liking, in that I am introducing quite the complexity to the build process that then might go wrong, but here goes:
Setup the build server as a valid target for deployments. Then I am guessing that I can use the "PowerShell on Target Machines" step, even though I am targeting itself. I would assume that it would then act as separate processes. That requires all sorts of configurations to get that in place though, plus writing the remote PowerShell script for the task.
Writing a small windows service that is callable with as e.g. REST, which could then start the process, so the new windows service becomes the owning thread. - that just introduces a new layer that also might need to be updated. This could then perhaps be updated manually instead of automatic.
Again, I would rather not use any of the 2 solutions, if a better exists. :)
At the moment I ended up fixing it by installing AutoIt, and added this simple script to the "deploy" folder:
While 1 ; Opens up a WHILE loop, with 1 as a constant, so it is infinite
If Not ProcessExists("DelphiDebug.exe") Then Run("DelphiDebug.exe") ; if the process of DelphiDebug.exe doesn't exist, it starts it
Sleep (10) ; Puts the script to sleep for 10 milliseconds so it doesn't chew CPU power
WEnd ; Closes the loop, tells it to go back to the beginning
Credit goes to this forum entry where a notepad example was made.
I have then made a PowerShell script that renames the current version, copies the newly build version to "deploy" folder, stops the running instance, and deletes the renamed version:
# CONSTANTS AND CALCULATED VARIABLES
[string]$deploymentFolder = 'C:\Deployment-folder-on-local-machine'
[string]$deploymentPath = "$deploymentFolder\my-program.exe"
[string]$searchPathExistingVersion = $deploymentPath + '*' # The star is important so Get-Item does not throw an error
[string]$toDeleteExeName = 'please-delete.exe'
[string]$newCompiledVersionFullPath = "$env:BUILD_SOURCESDIRECTORY\sub-path-to-bin-folder\my-program.exe"
[string]$processName = 'my-program'
[string]$searchPathToDeleteVersion = "$deploymentFolder\$toDeleteExeName*" # The star is important so Get-Item does not throw an error
# EXECUTION
Write-Verbose "Search path: $searchPathExistingVersion"
$existingVersion = Get-Item $searchPathExistingVersion;
if ($existingVersion) {
Write-Debug "Match found: $existingVersion"
Rename-Item $existingVersion.FullName $toDeleteExeName
} else {
Write-Debug 'No existing file found'
}
Write-Verbose "Copy new version from path: $newCompiledVersionFullPath"
Copy-Item $newCompiledVersionFullPath $deploymentPath
Write-Verbose 'Stopping running processes'
Get-Process ($processName + '*') | Stop-Process # The new version is auto started with a running AutoIt script in the deployment folder.
Write-Verbose "Deleting old versions with search path: $searchPathToDeleteVersion"
$toDeleteVersion = Get-Item $searchPathToDeleteVersion
if ($toDeleteVersion) {
Write-Debug "Match found: $toDeleteVersion"
Remove-Item $toDeleteVersion -ErrorAction SilentlyContinue # Deletion is not critical. Next time the build runs, it will attempt another cleanup.
} else {
Write-Debug 'No file found to delete'
}
Again, this solution added another complexity to the server, so I am keeping the question open for a few days, to see if there are simpler solutions. :)
Instead of starting a process during the build I think you need to install the process as a Windows Service and start it. It makes sense that any process you start in the context of the build would stop at the completion of the build. You can use the commandline to install/update/start a Windows service:
sc create [service name] [binPath= ]
https://ozansafi.wordpress.com/2009/01/14/create-delete-start-stop-a-service-from-command-line/

TFS2015 Release Management Execute Powershell on Remote Machine

Evening,
I have recently installed TFS2015 and investigating the Release Management integrated solution, but have come across a huge blocker that I just cannot make sense of.
I currently have a RM2013 build working with TFS, RM Server 2013, and Powershell DSC and have setup a new deployment in RM2015, it has a single task in it 'Execute Powershell on Remote Machine' - with a very simple powershell script just writing out a string to the verbose listener.
I have verified that the file is transferred to the Agent working directory as part of the artifact transfer process, and if I call Import-Module "path to script" (Which is what the PowerShellonTargetMachines script seems to do under the hood) in the ISE of the remote server, my script runs perfectly fine - but no matter what I do, in TFS release 2015 I get this error without fail:
[error]The term 'path to script\test.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. For more info please refer to http://aka.ms/powershellontargetmachinesreadme
Now just to double confirm, the path to the scrpt is 100% correct, I have pasted it into a local ISE on the remote server, and it executes perfectly fine - but from TFS2015 Execute Powershell on Remote Server - it simply fails to run, in fact any script I point at fails to run with the exact same error (I initially thought it might be a DSC component install failure, but even with a simple test script the same issue occurs without fail!
My path in the tasks Deployment>Powershell Script parameter input is:
c:\test_scripts\test.ps1
I have tried with quotes, without quotes, dot sourcing - nothing makes a difference which is making me think something fundamentally is either broken with my installation, or I am simply doing this wrong.
Any ideas gratefully received!!!
The script has to already be on the machine. You can push the script using the "Windows Machine File Copy" task.
Fixed this... make sure you execute the PS1 file on the release agent itself unless copying the powershell files to the remote node via file copy first as indicated below

Azure Cloud Service Startup task that needs to run a PowerShell script

All,
Note: I have updated the question after some feedback.
Thanks to #jisaak for his help so far.
I have the need to run a PowerShell script that adds TCP bindings and some other stuff when I deploy my Cloud Service.
Here is my Cloud Service Project:
Here is my Cloud Service Project and Webrole project:
Here is my task in ServiceDefinition.csdef:
And here is the PowerShell script I want to run:
here is my attempt at the Startup.cmd:
When I deploy I get this in the Azure log:
And this in the powershell log:
Any help would be very much appreciated.
I think I am nearly there but following other people syntax on the web doesn't seem to get me there.
thanks
Russ
I think the issue is that the working directory of the batch command interpreter when it runs Startup.cmd runs is not as expected.
The Startup.cmd is located in the \approot\bin\Startup directory but the working directory is \approot\bin.
Therefore the command .\RoleStartup.ps1 is not able to find the RoleStartup.ps1 as it is looking in the bin directory not in the bin\Startup directory.
Solutions I know to this are:
Solution 1:
Use ..\Startup\RoleStartup.ps1 to call the RoleStartup.ps1 from Startup.cmd.
Soltuion 2:
Change the current working directory in Startup.cmd so that the relative path .\RoleStartup.ps1 is found. I do this by CHDIR %~dp0 (see here) to change into the directory that contains Startup.cmd.
Solution 3:
As Don Lockhart's answer suggested, do not copy the Startup directory to the output, instead leave it set as "Content" in the Visual Studio project. This means the files within it will exist in the \approot\Startup directory on the Azure instance. (You would then want to make sure that the Startup folder is not publically accessible via IIS!). Then update the reference to Startup.cmd in ServiceDefinition.csdef to ..\Startup\Startup.cmd, and update the reference to RoleStartup.ps1 in Startup.cmd to ..\Startup\RoleStartup.ps1. This works on the fact that the working directory is bin and uses ..\Startup to always locate the Startup directory relative to it.
You don't need to set the executionpolicy within your cmd - just call the script. Also, you should use a relative path because you can't rely that there is C disk.
Change your batch to:
powershell -executionpolicy unrestricted -file .\RoleStartup.ps1
Right click on the RoleStartup.ps1 and Startup.cmdin Visual Studio and ensure that the Copy to Output directory is set to copy always.
If this still doesn't work, remove the startup call in your csdef, deploy the service, rdp into it and try to invoke the script by yourself to retrieve any errors.
Edit:
Try to adopt your script as below:
Import-Module WebAdministration
$site = $null
do # gets the first website until the result is not $null
{
$site = Get-WebSite | select -first 1
Sleep 1
}
until ($site)
# get the appcmd path
$appcmd = Join-Path ([System.Environment]::GetFolderPath('System')) 'inetsrv\appcmd.exe'
# ensure the appcmd.exe is present
if (-not (Test-Path $appcmd))
{
throw "appcmd.exe not found in '$appcmd'"
}
# The rest of your script ....
I've found it easier in the past to not copy the content to the output directory. I have approot\bin as the working directory. My startUp task element's commandLine attribute uses a relative reference to the .cmd file like so:
The .cmd file references the PowerShell script relatively from the working directory as well:
PowerShell -ExecutionPolicy Unrestricted -f ..\StartUp\RoleStartup.ps1
Ok,
So I am coming back to this after many different attempts to make it work.
I have tried using:
Startup config in the ServiceDefinition.csdef
I have tried registering a scheduled task on the server that scans the Windows Azure log looking for [System[Provider[#Name='Windows Azure Runtime 2.6.0.0'] and EventID=10004]]
Nothing worked either due to security or the timing of events and IIS not being fully setup yet.
So I finally bit the bullet and used my Webrole.cs => public override bool OnStart() method:
Combined with this in the ServiceDefinition.csdef:
Now it all works. This was not the most satisfying result as some of the other ways to do it felt more elegant. Also, many others posted that they got the other ways of doing it to work. Maybe I would have got there eventually but my time was restricted.
thanks
Russ