MSBuild in a Powershell Script - How do I know if the build succeeded? - powershell

I am writting a build script with Powershell. The scripts performs various operations such as getting the latest source code off the SVN, backups, etc., and builds the solution using MSBuild:
cmd /c C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe "C:\Dev\Path\MyProjct.sln" /p:Configuration=Release
After this instruction, I only want to execute the rest of the script if the compilation succeeded. How can I check this?
The project is a web project, so it's not so easy to check for an output, but I would guess some variables would contain the compilation result. Also since I call msbuild with cmd /c, am I going to be able to access these variables?

Check the value of $LastExitCode right after the call to MSBUILD. If it is 0, then MSBUILD succeeded, otherwise it failed.
BTW there is no need to use cmd /c. Just call MSBUILD.exe directly. We do that in PowerShell build scripts all the time.

To just check for success/failure, use the automatic variable $?.
PS> help about_Automatic_Variables
$?
Contains the execution status of the last operation. It contains
TRUE if the last operation succeeded and FALSE if it failed.
for example:
msbuild
if (! $?) { throw "msbuild failed" }

Related

VSTS build definition - prevent PowerShell exit behavior causing processes termination

I have a PowerShell task in my definition that calls another script file on its own which takes care of running several things on my build agent (starts several different processes) - emulators, node.js applications, etc.
Everything is fine up until the moment this step is done and the run continues. All of the above mentioned stuff gets closed with most of the underlying processes killed, thus, any further execution (e.g. tests run) is doomed to fail.
My assumption is that these processes are somehow dependent on the outermost (temporary) script that VSTS generates to process the step.
I tried with the -NoExit switch specified in the arguments list of my script, but to no avail. I've also read somewhere a suggestion to set this by default with a registry key for powershell.exe - still nothing.
The very same workflow was okay in Jenkins. How can I fix this?
These are the tasks I have:
The last PowerShell task calls a specified PowerShell file which calls several others on its own. They ensure some local dependencies and processes needed to start executing the tests, e.g. a running Node.js application (started in a separate console for example and running fine).
When the task is done and it is successful, the last one with the tests would fail because the Node.js application has been shut down as well as anything else that was started within the previous step. It just stops everything. That's why I'm currently running the tests within the same task itself until I find out how to overcome this behavior.
I am not sure how you call the dependencies and applications in your PowerShell script. But I tried with the following command in PowerShell script task to run a Node.js application:
invoke-expression 'cmd /c start powershell -Command {node main.js}'
The application keeps running after the PowerShell script task is passed and finished which should meet your requirement. Refer to this question for details: PowerShell launch script in new instance.
But you need to remember to close the process after the test is finished.
There is the Continue on error option (Control Options section). The build process will be continued if it is true (checked), but the build result will be partially succeeded.
You also can output the error or warning by using PowerShell or VSTS task commands (uncheck Fail on Standard Error option in the Advanced section) and terminate the current PowerShell process by using the exit keyword, for example:
Write-Warning “warning”
Write-Error “error”
Write-Host " ##vso[task.logissue type=warning;]this is the warning"
Write-Host " ##vso[task.logissue type=error;sourcepath=consoleapp/main.cs;linenumber=1;columnnumber=1;code=100;]this is an error "
More information about the VSTS task command, you can refer to: Logging Commands

Executing powershell command directly in jenkins pipeline

Is it possible to call a PowerShell command directly in the pipelines groovy script? While using custom jobs in Jenkins I am able to call the command with the PowerShell Plugin. But there is no snippet to use this in the groovy script.
I also tried sh() but it seems that this command does not allow multiple lines and comments inside the command.
To call a PowerShell script from the Groovy-Script:
you have to use the bat command.
After that, you have to be sure that the Error Code (errorlevel) variable will be correctly returned (EXIT 1 should resulting in a FAILED job).
Last, to be compatible with the PowerShell-Plugin, you have to be sure that $LastExitCode will be considered.
I have notice that the 'powershell' is now available in pipeline, but since it have several issues I prefer this variant. Still waiting it works stabil. I actually have an issue with the 'dontKillMe' behavior.
Since Jenkins 2.207 with Powershell plugin 1.4, I have replace all my calls with the official powershell pipeline command. I do now recommend to use it.
Note that you must predent \$ErrorActionPreference='Stop'; to your Script if you want it to abort on Write-Error because of an Issue with the powershell plugin.
For that porpuse I have written a little groovy method which could be integrate in any pipeline-script:
def PowerShell(psCmd) {
psCmd=psCmd.replaceAll("%", "%%")
bat "powershell.exe -NonInteractive -ExecutionPolicy Bypass -Command \"\$ErrorActionPreference='Stop';[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;$psCmd;EXIT \$global:LastExitCode\""
}
[EDIT] I have added the UTF8 OutputEncoding: works great with Server 2016 and Win10.[/EDIT]
[EDIT] I have added the '%' mask[/EDIT]
In your Pipeline-Script you could then call your Script like this:
stage ('Call Powershell Script')
{
node ('MyWindowsSlave') {
PowerShell(". '.\\disk-usage.ps1'")
}
}
The best thing with that method, is that you may call CmdLet without having to do this in the Script, which is best-praxis.
Call ps1 to define CmdLet, an then call the CmdLet
PowerShell(". '.\\disk-usage.ps1'; du -Verbose")
Do not forget to use withEnv() an then you are better than fully compatible with the Powershell plugin.
postpone your Script with . to be sure your step failed when the script return an error code (should be preferred), use & if you don't care about it.
Calling PowerShell scripts is now supported with powershell step as announced on Jenkins blog.
The documentation mentions it supports multiple lines scripts.
From version 2.28 of Pipeline Nodes and Processes Plugin, we can directly use 'powershell'.
Eg: powershell(". '.Test.ps1'")
You can use the sh command like this:
sh """
echo 'foo'
# bar
echo 'hello'
"""
Comments are supported in here.

How do I get errors to propagate in the TeamCity PowerShell runner

I have a TeamCity 7 Build Configuration which is pretty much only an invocation of a .ps1 script using various TeamCity Parameters.
I was hoping that might be a simple matter of setting:
Script
File
Script File
%system.teamcity.build.workingDir%/Script.ps1
Script execution mode
Execute .ps1 script with "-File" argument
Script arguments
%system.teamcity.build.workingDir% -OptionB %BuildConfigArgument% %BuildConfigArg2%
And then I would expect:
if I mess up my arguments and the script won't start, the Build fails
if my Script.ps1 script throws, the Build fails
If the script exits with a non-0 Error Level I want the Build to Fail (maybe this is not idiomatic PS error management - should a .ps1 only report success by the absence of exceptions?)
The question: It just doesn't work. How is it supposed to work? Is there something I'm doing drastically wrong that I can fix by choosing different options?
As doc'd in the friendly TeamCity manual:
Setting Error Output to Error and adding build failure condition
In case syntax errors and exceptions are present, PowerShell writes them to stderr. To make TeamCity fail the build, set Error Output option to Error and add a build failure condition that will fail the build on any error output.
The keys to making this work is to change two defaults:
At the top level in the Build Failure Conditions, switch on an error message is logged by build runner:
In the [PowerShell] Build Step, Show advanced options and set Error output: Error
In 9.1 the following works (I wouldn't be surprised if it works for earlier versions too):
create a PowerShell Build Step with the default options
change the dropdown to say Script: Source code
Add a trap { Write-Error "Exception $_" ; exit 98 } at the top of the script
(Optional but more correct IMO for the kind of scripting that's appropriate for within TeamCity build scripts)
Show advanced options and switch on Options: Add -NoProfile argument
(Optional, but for me this should be the default as it renders more clearly as suggested by #Jamal Mavadat)
Show advanced options and switch on Error output: Error
(ASIDE #JetBrains: if the label was "Format stderr output as" it would be less misleading)
This covers the following cases:
Parse errors [bubble up as exceptions and stop execution immediately]
Exceptions [thrown directly or indirectly in your PS code show and trigger an exit code for TC to stop the build]
An explicit exit n in the script propagates out to the build (and fails it if non-zero)
There is an known bug in TeamCity that causes the behavior that the original poster noticed.
It is easy to work around, however.
At the end of your PowerShell script, add output indicating that the end of the script has been reached:
Echo "Packaging complete (end of script reached)"
Then, set up a new Build Failure Condition on your build to fail if the text you are echoing is NOT present in the output.
You're over-thinking things. Try this:
Script
File
Script File
Script.ps1
You don't need to give this a path - by default, it's relative to the checkout directory.
Script execution mode
Put script into PowerShell stdin with "-Command -" arguments
This is exactly what I use to run a bunch of powershell scripts within Teamcity.
Update
I missed the bit in the original post about having failures in the powershell script fail the build. Apologies!
I've solved that part of the puzzle in two different ways.
For regular powershell scripts
Wrap the main code block in a try...catch; if an exception occurs, return a non-zero integer value. For successful execution, return 0.
This convention of returning zero for success dates back a very long way in history - it's used by Unix/Linux systems, DOS, CP/M and more.
For PSake build scripts
Use a wrapper powershell script to invoke psake and directly set the result of the teamcity build by writing a response message to stdout.
At the start of the script, define a status message that represents failure:
$global:buildResult = "#teamcity[buildStatus status='FAILURE' text='It died.']
Within the psake script, update $global:buildResult to indicate success in a dedicated task that's run last of all.
$global:buildResult = "#teamcity[buildStatus status='SUCCESS' text='It lives.']
At the end of the wrapper script, output the status message
write-host $global:buildResult
If anything in the build script fails, that last task won't run, and the default message (indicating failure) will be output.
Either way, Teamcity will pick up on the message and set the build status appropriately.
An alternative to the accepted answer that works for me On TeamCity 9 (I don't like changing the 'Build Failure Conditions' option as it affects all build steps):-
I wanted PowerShell errors to fail the build step, but with a pretty message.
So I wanted to throw an error message AND return an error code.... try / catch / finally to the rescue.
EDIT for clarity: This script is supposed to fail. It is demonstrating that it is possible to throw an exception AND to return an exit code.
So you get both, the exit code for TeamCity to deal with, and, in my case, a nice clear debug message that shows where the issue was.
My demo script:
Try {
Write-Host "Demoing the finally bit"
# Make sure if anything goes wrong in the script we get an exception
$ErrorActionPreference = "Stop"
# This will fail and throw an exception (unless you add the file)
Get-Content IDontExist.txt
}
Catch
{
# Throwing like this gives a pretty error message in the build log
throw $_
# These give less pretty/helpful error messages
# Write-Host $_
# Write-Host $_.Exception.Message
}
Finally
{
# 69 because it's more funny than 1
exit(69)
}
If newer TeamCity versions are within your reach then it is worth checking out some of PowerShell build runner improvements.
In particular, changing Error Output from default warning to error may interest you.
Just praise the lawd you don't need to work with Jenkins.. Can't use the PowerShell step for similar issues. Instead we need to use a "cmd" step that calls the following magic wrapper that will fail the step on exception and exit codes:
%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -WindowStyle Hidden -Command "& { $ErrorActionPreference = 'Stop'; & 'path\to\script.ps1 -arg1 -arg2; EXIT $LASTEXITCODE }"
This has been superseded by options afforded by 9.x, but I'll leave it here as it definitely was bullet proof at the time and I couldn't find any other solution I liked better.
You could just do something that works. The following has been tested with
errors in the script bit
missing files
script exiting with non-0 ERRORLEVEL
In the TeamCity Powershell runner options, set it as follows:
Script File
Source code
Script source
$ErrorActionPreference='Stop'
powershell -NoProfile "Invoke-Command -ScriptBlock { `$errorActionPreference='Stop'; %system.teamcity.build.workingDir%/Script.ps1 %system.teamcity.build.workingDir% --OptionB %BuildConfigArgument% %BuildConfigArg2%; exit `$LastExitCode }"
(unwrapped version: powershell -NoProfile "Invoke-Command -ScriptBlock { $errorActionPreference='Stop'; %system.teamcity.build.workingDir%/Script.ps1 %system.teamcity.build.workingDir% --OptionB %BuildConfigArgument% %BuildConfigArg2%; exit$LastExitCode }"
Script execution mode
Put script into PowerShell stdin with "-Command -" arguments
Additional command line parameters
-NoProfile
I'm hoping against hope this isn't the best answer!
This checks the last error code and exits if is not 0.
If ($LASTEXITCODE -ne 0) {
Write-Host "The result was of the previous script was " $LASTEXITCODE
Exit $LASTEXITCODE
}
Powershell v2 atleast had an issue with the -File option messing up error codes. There is some details about how it messes up and stuff.
The answer is either switch to -Command option and/or wrap it with a bat file that checks the LastErrorCode and returns 1+ if its supposed to fail.
http://zduck.com/2012/powershell-batch-files-exit-codes/
Use something like this:
echo "##teamcity[buildStatus status='FAILURE' text='Something went wrong while executing the script...']";
For reference, take a look on Reporting Build Status from Teamcity

Automatically stop powershell script on bat errors

Generally one can use $ErrorActionPreference to stop the execution of a PS script on error as in the example below that uses an invalid subversion command. The script exits after the first command.
$ErrorActionPreference='Stop'
svn foo
svn foo
Trying the same thing with the maven command (mvn.bat) executes both commands.
$ErrorActionPreference='Stop'
mvn foo
mvn foo
Initially I suspected that mvn.bat does not set an error code and PS just doesn't see the error. However, $? is set properly as demonstrated by the following, when PS exits after the first error:
mvn foo
if (-not $?) {exit 1}
mvn foo
So here's my question: Given that both svn.exe and mvn.bat set an error code on failure, why does the PS script not stop after the mvn error. I find it a lot more convenient to set "$ErrorActionPreference=Stop" globally rather than doing "if (-not $?) {exit 1}" after each command to terminate on error.
Not all command line programs provide errors in the same way. Some set an exit code. Some don't. Some use the error stream, some don't. I've seen some command line programs actually output everything to error, and always output non-zero return codes.
So there's not a real safe guess one could ever make as to it having run successfully, and therefore it's next to impossible to codify that behavior into PowerShell.
$errorActionPreference will actually stop a script whenever a .exe writes to the error stream, but many .exes will write regular output to the console and never populate the error stream.
But it will not reliably work. Neither, for that matter, will $?. If an exe returns 0 and doesn't write to error $? will be true.
While it might be a pain in the butt to deal with each of these individual .exes and their errors in PowerShell, it's a great example of why PowerShell's highly structured Output/Error/Verbose/Warning/Debug/Progress streams and consistent error behavior in Cmdlets beats out plain old .EXE tools.
Hope this Helps
$ErrorActionPreference controls PowerShell's behavior with regard to commandlets and PowerShell functions -- it is not sensitive to exit codes of "legacy" commands. I'm still looking for a work-around for this design decision myself -- for this issue, refer to this thread: How to stop a PowerShell script on the first error?. You'll probably conclude that this custom "exec" function is the best solution: http://jameskovacs.com/2010/02/25/the-exec-problem/
With regard to the stop-on-stderr behavior, I can't reproduce this behavior in PowerShell 3:
Invoke-Command
{
$ErrorActionPreference='Stop'
cmd /c "echo hi >&2" # Output to stderr (this does not cause termination)
.\DoesNotExist.exe # Try to run a program that doesn't exist (this throws)
echo bye # This never executes
}
Update: The stop-on-stderr behavior appears to take effect when using PS remoting.

MSBuild fails when run via Powershell

I have an MSBuild script that I want to call from a PowerShell script as part of a deployment process. If I call the build script via a bat file all works well. If I do the exact same thing in PowerShell I get CS1668 error looking for wierd and wonderful paths that dont exist on my machine.
I know I am getting into the MSBuild script as these errors are occuring from within the MSBuild script targets (logging output is showing this).
The bat file and the PowerShell script reside in the same place, right next to the build script.
Errors:
error CS1668 : Warning as error :
Invalid search path 'C:\Program
Files\Microsoft Visual Studio
8\VC\AtlMfc\Lib' specified in 'LIB
environment variable' -- 'The system
cannot find the path specified. '
error CS1668 : Warning as error :
Invalid search path 'C:\Program
Files\Microsoft Visual Studio
8\VC\PlatformSDK\Lib' specified in
'LIB environment variable'-- 'The
system cannot find the path specified.
'
I have checked and neiter of these paths do NOT exist on my machine.
Why would running from PowerShell change what paths MSBuild look for?
Thanks in advance
RhysC
EDIT- adding in code: NB the name of the MSBuild script is AutomatedDebug.build
POWERSHELL SCRIPT:
#Begining of script
$CurrentPath = Split-Path (Split-Path $myinvocation.mycommand.path )
#Assign the Build script paths. 1 is for building and testsing, 1 is for deployment (to keep things clean)
$MSBuildScriptBuildAndTestPath = $CurrentPath + "\Tools\AutomatedDebug.build"
$MSBuildScriptDeployPath = $CurrentPath + "\Tools\Deploy.build"
#Run the automated build with tests
Write-Host "*** Run the automated build with tests ***"
C:\Windows\Microsoft.NET\Framework\v3.5\MSbuild.exe $MSBuildScriptBuildAndTestPath "/t:AllTests" "/l:FileLogger,Microsoft.Build.Engine;logfile=AllTests.log"
if($LastExitCode -ne 0)
{
throw "AllTests failed"
}
Write-Host "*** FINISHED: Run the automated build with tests ***"
Bat File
#C:\Windows\Microsoft.NET\Framework\v3.5\MSbuild.exe AutomatedDebug.build /t:AllTests /l:FileLogger,Microsoft.Build.Engine;logfile="AllTests.log"
#pause
enter code here
Ok, what is actually happening is that I have dowloaded the Powershell Community Extensions (http://pscx.codeplex.com) many moons ago and there is a Environment.VisualStudio2005.ps1 file in there that adds EnvVars that dont exist. This is (I assume) becuase i dont use VS2005. I have commmented this out and all is well. I have tried to look for the 2008 equivilent but dint have too much luck. To be honest i dont use powershell to its full potential so i doubt i even need the PSCX on my machine anyway.
Thanks guys for your help.
It is most likely due to how you are passing parameters into the MSBuild script. There are some key parameter parsing differences between CMD and PowerShell and by using a batch file, you are implicitly using CMD's parameter parsing engine to hand off the parameters to MSBuild. If you post the command line you're using, one of the PowerShell gurus here can probably help debug where the parameters are getting confused.
I am wondering, basically you get "Warning as error" here. You can either try turning off treating warnings as errors (I mean, having a lib path which doesn't exist doesn't sound that bad to me) or you'll let your scripts print out the respective environment variables (using echo %LIB% and $Env:LIB respectively) from the scripts and look for differences.
However, since the envvars for VS aren't set globally by default (that's why there are the three Visual Studio command prompts) you have to set all variables somewhere. If you're doing this within the scripts you may have a typo somewhere?