I am currently setting up buildscripts in Teamcity for a website I am working on. I use Ctt.exe to transform my web.config files and this program is invoked through PowerShell. But my problem is that that Ctt.exe in some cases has an error or something else is wrong in my ps1-script but the Teamcity buildstep returns "Process exited with code 0" and the build Succeeds instead of failing.
Since this blogpost might be boring and badly written I will start with the solution:
$ErroActionPreference = ‘Stop’
My scenario is that I have a script, ConfigTransform.ps1, which is invoking the ctt.exe file. This script file takes one parameter which is the Environment (Test/Production). If an error occurs in the script I need the hole build to fail.
To force an error while testing I have set my script to invoke the file "DoesNotExist.exe" instead of the Ctt.exe.
My script (ConfigTransform.ps1)
param($Environment)
Write-Host Environment is $Environment
..\DoesNotExist.Exe
In Teamcity there are many different alternatives for you to call the script.
Script=File and Executionmode = -FILE
Since I have a predefined powershell script with all the logic and I need to pass a parameter to it, – I first choose the PowerShell-runner with Script=File and Executionmode to –File. As you can see below the parameter is passed on but this process exits with code 0 and therefore the Build won’t fail.
[13:51:05][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -File (…)
ConfigTransform.ps1 -Environment Test && exit /b %ERRORLEVEL%
[13:51:06][Step 1/5] Environment is Test
[13:51:06][Step 1/5] The term '..\DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:06][Step 1/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:06][Step 1/5] path was included, verify that the path is correct and try again.
[13:51:06][Step 1/5] At C:\TeamCity\buildAgent\work\7100e0a13f6ca7b6\BuildToolsAndScripts\Scripts\Co
[13:51:06][Step 1/5] nfigTransform\ConfigTransform.ps1:4 char:20
[13:51:06][Step 1/5] + ..\DoesNotExist.Exe <<<<
[13:51:06][Step 1/5] + CategoryInfo : ObjectNotFound: (..\DoesNotExist.Exe:String) [],
[13:51:06][Step 1/5] CommandNotFoundException
[13:51:06][Step 1/5] + FullyQualifiedErrorId : CommandNotFoundException
[13:51:06][Step 1/5]
[13:51:06][Step 1/5] Process exited with code 0
Script=File and Executionmode = -Command
Then I try setting the Executionmode to "-Command". The exit code is now correct and the Build is not failing but it is not possible to pass on script parameters.
[13:51:07][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -Command (…)
ConfigTransform.ps1 && exit /b %ERRORLEVEL%
[13:51:07][Step 1/5] Environment is
[13:51:07][Step 2/5] The term '..\DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:07][Step 2/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:07][Step 2/5] path was included, verify that the path is correct and try again.
[13:51:07][Step 2/5] At line:1 char:20
[13:51:07][Step 2/5] + ..\DoesNotExist.Exe <<<<
[13:51:07][Step 2/5] + CategoryInfo : ObjectNotFound: (..\DoesNotExist.Exe:String) [],
[13:51:07][Step 2/5] CommandNotFoundException
[13:51:07][Step 2/5] + FullyQualifiedErrorId : CommandNotFoundException
[13:51:07][Step 2/5]
[13:51:07][Step 2/5] Process exited with code 1
Script=Source code and Executionmode = -Command
Since executionmode –File does not return correct errorcode I need to use "-Command" but also pass on my parameter. This can be done PowerShell-runner with Script=Source code. In the Script Source field I am now adding the full path to my script including my parameter.
The output is:
[13:51:05][Step 1/5] Starting: (…)cmd.exe /c (…)powershell.exe -NonInteractive -Command (…)
powershell3439435836265.ps1 && exit /b %ERRORLEVEL%
[13:51:06][Step 1/5] Environment is Test
[13:51:06][Step 1/5] The term '..\DoesNotExist.Exe' is not recognized as the name of a cmdlet, funct
[13:51:06][Step 1/5] ion, script file, or operable program. Check the spelling of the name, or if a
[13:51:06][Step 1/5] path was included, verify that the path is correct and try again.
[13:51:06][Step 1/5] At C:\TeamCity\buildAgent\work\7100e0a13f6ca7b6\BuildToolsAndScripts\Scripts\Co
[13:51:06][Step 1/5] nfigTransform\ConfigTransform.ps1:4 char:20
[13:51:06][Step 1/5] + ..\DoesNotExist.Exe <<<<
[13:51:06][Step 1/5] + CategoryInfo : ObjectNotFound: (..\DoesNotExist.Exe:String) [],
[13:51:06][Step 1/5] CommandNotFoundException
[13:51:06][Step 1/5] + FullyQualifiedErrorId : CommandNotFoundException
[13:51:06][Step 1/5]
[13:51:06][Step 1/5] Process exited with code 0
My script is runtime wrapped in a temporary ps1-file "powershell3439435836265.ps1 ", the parameter Environment is Test BUT exit code is now 0 again! So what now…
Solution:
Powershell is by default continuing when "minor" error occurs. I guess the Powershell-runner has done something with that and therefore it works using the –Command switch. I also guess they have forgotten that errorhandling when creating the –File switch. When setting Script=SourceCode all the code is wrapped in a temporary script and you have to take care of the errors yourself. One solution that at first glance worked for me was to wrap my script in a try catch and then just have a ‘throw’ in my errorhandling. What was not so good about this is that the exception itself was suppressed and therefore not visible in the build output.
So the best solution I found is to add the following code (see the first line) in Team City’s Source Code-field:
$ErrorActionPreference = ‘Stop’
(…)/ConfigTransform.ps1 –Enviroenment Test
And then finally everything worked as I wanted. I got exit code 1 in the examples above. 