Reputation: 13603
I'm using Jenkins PowerShell plugin to build a project.
However, I found that Jenkins always considers my build successful no matter what I type inside Windows PowerShell
command.
Here's an example:
As you can see, asdf
isn't a legal command. Jenkins should give me FAILURE
after the build.
But the console output gives me:
Started by user admin
Building in workspace C:\Users\Administrator\.jenkins\jobs\Test\workspace
[workspace] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Users\ADMINI~1\AppData\Local\Temp\hudson2092642221832331776.ps1'"
The term 'asdf' 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.
At C:\Users\ADMINI~1\AppData\Local\Temp\hudson2092642221832331776.ps1:1 char:5
+ asdf <<<<
+ CategoryInfo : ObjectNotFound: (asdf:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Finished: SUCCESS
I think the execution result of PowerShell should depend on $lastexitcode
.
Is this a bug of PowerShell plugin?
Upvotes: 27
Views: 30651
Reputation: 912
TLDR
From the Jenkins blog about the Powershell plugin, Powershell code (not exes) will only fail under these conditions
For executables, Jenkins will only fail if the exit code at the end of running a Powershell/Cmd step is failure.
Long Version
Continuing from above, suppose you have two exes in a step, the first fails and the second succeeds. Jenkins will mark that as a success. Same problem occurs if you iterate over the same exe, the last exit code wins no matter how many failures came before it.
The following example shows that Powershell cmdlets do not set $LASTEXITCODE
. Therefore, it is only useful if you're running an executable.
$LASTEXITCODE = 0 # Success
try {
CommandDoesntExist
} catch {
# Exit code is unchanged
Write-Error $Error[0]
} finally {
Write-Host "Exit Code $LASTEXITCODE"
}
>> Error Message...
>> Exit Code 0
For pure Powershell you need to handle the error and provide the exit code yourself. To avoid nesting the entire script in a try/catch you can use a trap instead. Local try/catch can still be used for handling blocks you expect to fail.
There is a "Gotcha" if ErrorActionPreference = 'Stop'
is set and you use Write-Error
instead of Write-Output
the trap would stop before getting to the exit. You fix this setting the flag to continue.
# Global error handling avoids nesting all
# code in a try/catch/finally.
trap { Write-Error $_ -ErrorAction Continue; exit 2 }
# Or if you don't care about the exit code
# PSv5+ throw statement will set exit code to 1
# trap { throw $_ }
# Block specific error handling
try {
throw "Worst case scenario"
} catch {
write-host "Handled error doesn't trigger trap"
}
# Finally example
try {
throw "Worst case scenario"
} finally {
write-host "I still cleanup"
}
Upvotes: 0
Reputation: 520
For me, I wanted the script to stop and fail in Jenkins soon as it hit an error. This was accomplished by adding this to the start of the script:
$ErrorActionPreference = "Stop"
This is discussed here: [How to stop a PowerShell script on the first error?][1]
[1]: How to stop a PowerShell script on the first error?. ..................
Upvotes: 10
Reputation: 843
I want to add here that I just ran into a quirk: you must have the powershell script end with exit
and not return
.
My jenkins pipe looked like:
script {
result = powershell(returnStatus: true, script: '''...if(error condition) { return 1 }''')
if(result) { error }
}
I was using if(error condition) { return 1 }
and while the 1 was showing up as the return value in the jenkins console, it was not failing the build. When i used if(error condition) { exit 1 }
, the build failed as expected.
I think this is a helpful addition to this thread - the need to use exit
and not return
. But I don't understand this part: the pipe is checking for result
to be non-zero. What is the difference between exit
and return
in a powershell directive that makes if(result) { error }
not work as expected when using return
?
Update Feb-16-2021: Coming back to this to add some more notes from my experience: I think what I was doing and what a lot of people do that confuses things, is to use returnStdout
or returnStatus
and not check them and/or not fully understand what's coming back.
If you use either of those params, Jenkins will not do anything for you based on their value. You have to check them yourself and act accordingly. On the other hand, if you don't use them, Jenkins will recognize a failure code and fail the pipe if one comes back.
Think about it: If you set returnStatus
, you're getting the exit code back from the step as a return value and not as something for Jenkins itself to worry about. If you set returnStdout
, you're getting the stdout
stream - and not any error codes or anything from stderr
. So you must check the return for what you want or you will not get the behavior you are expecting.
What I've been doing for a while is actually not setting either of those params and making sure to set $ErrorActionPreference = 'Stop'
at the start of any and all PowerShell scripts running in my pipes. That way any powershell failures automatically fail the pipe as expected, without having to check it.
Upvotes: 1
Reputation: 2431
Ultimately, I had to resort to the following configuration in Jenkins as none of the solutions here worked for me. Chris Nelson's answer got me on the right track. We're invoking chef-client remotely so we had to do a little magic to get the remote PS session to talk the local and then pass status on to Jenkins.
Of course, you'll have to supply your own evironment variables! :)
Write-host "Deploying $env:Computer with $env:Databag data bag... "
$secstr = ConvertTo-SecureString $env:Password -AsPlainText -Force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $env:User, $secstr
$s = New-PSSession -ComputerName $env:Computer -Credential $cred
$res = Invoke-Command -Session $s -ScriptBlock { try {chef-client} catch {exit 1}}
$lastsuccess = Invoke-Command -Session $s -ScriptBlock {$?}
Remove-PSSession $s
write-host " --- "
write-host $res
write-host " --- "
if($lastsuccess)
{
write-host "chef deployment completed"
exit 0
}
write-host "chef deployment had errors"
exit 1
Upvotes: 0
Reputation: 9
This is how I implemented RRIROWER's solution. Hope it helps.
<yourscript>.ps1; exit $lastexitcode
Make sure your powershell scripts does exit with the desired value.
Run "exit <value>"
as the last line.
Upvotes: -1
Reputation: 3707
As of 1.3, the plugin will not handle exceptions such as those from missing commands. You can do this yourself with try/catch
:
try
{
asdf
}
catch
{
write-host "Caught an exception"
exit 1
}
See MSDN for more.
Upvotes: 17
Reputation: 4590
Per the latest version of the plugin (Version 1.3 Sept 18 2015), you must use $LastExitCode to fail a build.
Version 1.3 (Sept 18 2015)
- PowerShell now runs in Non-Interactive mode to prevent interactive prompts from hanging the build
- PowerShell now runs with ExcecutionPolicy set to "Bypass" to avoid execution policy issues
- Scripts now exit with $LastExitCode, causing non-zero exit codes to mark a build as failed
- Added help and list of available environment variables (including English and French translations)
Upvotes: 5