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

11

29

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?

Patterman answered 25/7, 2012 at 10:44 Comment(0)
P
38

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:

  1. At the top level in the Build Failure Conditions, switch on an error message is logged by build runner:
  2. 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):

  1. create a PowerShell Build Step with the default options
  2. change the dropdown to say Script: Source code
  3. Add a trap { Write-Error "Exception $_" ; exit 98 } at the top of the script
  4. (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

  5. (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:

  1. Parse errors [bubble up as exceptions and stop execution immediately]
  2. Exceptions [thrown directly or indirectly in your PS code show and trigger an exit code for TC to stop the build]
  3. An explicit exit n in the script propagates out to the build (and fails it if non-zero)
Patterman answered 3/7, 2015 at 12:30 Comment(1)
This will still run subsequent build steps unless you set their execute step to Only if build status is successfulTitulary
C
8

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.

Cyprio answered 2/4, 2013 at 15:19 Comment(2)
For now, I'm using my approach as it aint broken (though only a mother could love it). To be honest, @Bevan's approach, having less moving parts (as in my build config doesnt have two things for me to get right) is more attractive. +1 for a working hack though!Patterman
I used found another solution I prefer to this but it definitely does a jobPatterman
M
5

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.

Mayest answered 24/8, 2012 at 3:55 Comment(3)
OK thanks; will investigate. Pretty sure I tried that. I agree the %system.teamcity.build.workingDir% may be gratuitous, but for me the key here is - do I get red when stuff is broken or do I have to click in on the output to see a message.Patterman
+1 thanks for the follow up.Still think it needs $errorActionPreference='Stop'; to trap parse errors and it already had an exit to achieve the exit code propagation. I'd love to use PSake as its obv a far better tool but we have have standardised on another one and ultimately I'm trying to run a PowerShell script in the PowerShell runner for TeamCity-a tool I find the rest of faultless and a continual joy. I do understand a lot of this stuff BTW see https://mcmap.net/q/263411/-powershell-exit-code-calling-from-msbuild. However I know it can be simpler or I wouldnt be asking - it just needs to cover each and every casePatterman
As noted in @Jamal Mavadat's answer in 9.1 stuff seems to work OOTB if you leverage 2 options, (especially for Source code as I recently used and battle tested)Patterman
O
3

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)
}
Oliphant answered 4/9, 2015 at 10:43 Comment(3)
But doesn't your code always return 69 (finally gets always executed), no matter if it caught exceptions or not? thus meaning that the script will always fail?Percentile
This demo script will always fail. I just put the exit(1) into the catch and deleted both the throw and finally block.Tshombe
Yes this demo script will always fail. It is supposed to. The script is demonstrating that is possible to have an exception thrown AND to return an exit code.Oliphant
B
2

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.

Bust answered 15/12, 2014 at 4:53 Comment(2)
Thanks for the heads up; sadly even TC itself is some distance from me at the moment :D Hopefully others will chime in...Patterman
Turns out after much ado, you were right on the money. However the fact that your answer didnt call out that changing the setting of which you speak only gains material effect to resolve the issue iff you turn on the top level "fail on runner errors" option prevents me from accepting this - I provided a more complete answer of my own which may well be what you meantPatterman
G
2

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 }"
Glyco answered 3/7, 2015 at 12:50 Comment(3)
Yes, that'd work if it wasnt for the shiny workarounds 9.x now gives one :) (similar to what I mention in https://mcmap.net/q/263411/-powershell-exit-code-calling-from-msbuild). I assume you'd still take Jenkins over TeamBuild though as I had the misfortune to re-suffer recently :DPatterman
ooo, I wouldn't make that assumption... Jenkins is a pig. Will cry about it over a beer sometime... Maybe I'm suffering from FAFAG (Far Away Fields are Greener)..Glyco
I hope to never have to test my assumption; I sure didn't enjoy the 6 months back in TeamBuild land I've had to endure this decade (relative to the first world problem that is fighting TeamCity) :) Thankfully they've finally given up on their WF pipedreams though.Patterman
P
1

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!

Patterman answered 25/7, 2012 at 10:44 Comment(0)
W
0

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
    }
Wedded answered 19/2, 2019 at 9:56 Comment(3)
Don't you think this is a little invasive? Do you think this addresses the OP ?Patterman
In my case I was trying to execute a SQL DB script using TeamCity and did this to determine whether the SQL script ran successfully or failed. Tried other options and it didn't work.Wedded
OK you're not wrong that such a check needs to happen if you're using Powershell and TeamCity, but that's arguably obvious. There are 57 things that can go wrong with the propagation of errors from Powershell in TeamCity other than this bit; this post is about those other 57 things. I won't be downvoting, especially as you've taken the time to explain, but I also won't be upvoting. I suggest editing your answer to s/Please do the following after every command that you are executing./Semi relevant for overall checklist: don't forget PS doesn't react to exit codes (at all) unless you make it/Patterman
S
0

i've inserted

exit $LASTEXITCODE

at the end of my scripts (powershell + cmd) so there are npm errors propageted

Soft answered 10/5, 2023 at 14:11 Comment(0)
R
-1

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/

Rafael answered 24/3, 2014 at 11:48 Comment(1)
+0 You'll see I have a comment there. The problem is how does one go from your link, and a 'pick it out of that' to something that actually solves the problem and handles all the edge conditions across OS and TC versions.Patterman
S
-1

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

Siphonostele answered 28/4, 2014 at 10:5 Comment(1)
-1 Sorry to -1 but I'm aware of this technique (and @Bevan's answer already addresses this mechanism). Note also this is about PowerShell and TeamCity's various limitations and/or bugs around running them, not CMD scriptsPatterman

© 2022 - 2024 — McMap. All rights reserved.