为什么我的powershell退出代码总是“0”?

我有一个PowerShell脚本如下

##teamcity[progressMessage 'Beginning build'] # If the build computer is not running the appropriate version of .NET, then the build will not run. Throw an error immediately. if( (ls "$env:windir\Microsoft.NET\Framework\v4.0*") -eq $null ) { throw "This project requires .NET 4.0 to compile. Unfortunatly .NET 4.0 doesn't appear to be installed on this machine." ##teamcity[buildStatus status='FAILURE' ] } ##teamcity[progressMessage 'Setting up variables'] # Set up varriables for build script $invocation = (Get-Variable MyInvocation).Value $directorypath = Split-Path $invocation.MyCommand.Path $v4_net_version = (ls "$env:windir\Microsoft.NET\Framework\v4.0*").Name $nl = [Environment]::NewLine Copy-Item -LiteralPath "$directorypath\packages\NUnit.2.6.2\lib\nunit.framework.dll" "$directorypath\Pandell.Tests\bin\debug" -Force ##teamcity[progressMessage 'Using msbuild.exe to build the project'] # Build the project using msbuild.exe. # note, we've already determined that .NET is already installed on this computer. cmd /c C:\Windows\Microsoft.NET\Framework\$v4_net_version\msbuild.exe "$directorypath\Pandell.sln" /p:Configuration=Release cmd /c C:\Windows\Microsoft.NET\Framework\$v4_net_version\msbuild.exe "$directorypath\Pandell.sln" /p:Configuration=Debug # Break if the build throws an error. if(! $?) { throw "Fatal error, project build failed" ##teamcity[buildStatus status='FAILURE' ] } ##teamcity[progressMessage 'Build Passed'] # Good, the build passed Write-Host "$nl project build passed." -ForegroundColor Green ##teamcity[progressMessage 'running tests'] # Run the tests. cmd /c $directorypath\build_tools\nunit\nunit-console.exe $directorypath\Pandell.Tests\bin\debug\Pandell.Tests.dll # Break if the tests throw an error. if(! $?) { throw "Test run failed." ##teamcity[buildStatus status='FAILURE' ] } ##teamcity[progressMessage 'Tests passed'] 

从我导致相信, 一个未被捕获的Throw将导致退出代码为1 ,但不幸的是,TeamCity是说,否则。

 [19:32:20]Test run failed. [19:32:20]At C:\BuildAgent\work\e903de7564e599c8\build.ps1:44 char:2 [19:32:20]+ throw "Test run failed." [19:32:20]+ ~~~~~~~~~~~~~~~~~~~~~~~~ [19:32:20] + CategoryInfo : OperationStopped: (Test run failed.:String) [], [19:32:20] RuntimeException [19:32:20] + FullyQualifiedErrorId : Test run failed. [19:32:20] [19:32:20]Process exited with code 0 [19:32:20]Publishing internal artifacts [19:32:20][Publishing internal artifacts] Sending build.finish.properties.gz file [19:32:20]Build finished 

注意到我的Execution Mode被设置为Execute .ps1 script with "-File" arguement也是很重要的。

我尝试改变它Put script into PowerShell stdin with "-Command -" arguements但它失败了,即使通过testing1退出代码。 我相信,把它运行成-File将是正确的。

如果我打开位于C:\BuildAgent\work\e903de7564e599c8\build.ps1并在CMD中手动运行它,它会执行同样的操作… IE:失败的testing失败,并且%errorlevel%仍然为0

是的,如果我在$LASTEXITCODE运行它,并调用$LASTEXITCODE它每次都返回正确的代码。

这是PowerShell的一个已知问题。 使用-file执行脚本时,不应该返回退出代码0。

(更新:下面的链接不再起作用,请在https://windowsserver.uservoice.com/forums/301869-powershell查找或报告此问题);

由于使用-command不适合你,你可以尝试在脚本的顶部添加一个陷阱:

 trap { write-output $_ ##teamcity[buildStatus status='FAILURE' ] exit 1 } 

当抛出一个exception时,上面应该导致适当的退出代码。

我在使用-file运行时遇到了这个确切的问题,但由于某些原因,Kevin提供的陷阱语法或“exit”语法在我的场景中不起作用。 不知道为什么,但为了防止其他人遇到同样的问题,我使用了下面的语法,它为我工作:

 try{ #DO SOMETHING HERE } catch { Write-Error $_ ##teamcity[buildStatus status='FAILURE'] [System.Environment]::Exit(1) } 

直到这个(大概)被closures作为我的一个老问题的自我答复 ,我会总结这里最干净的解决scheme:

  • 其他大部分答案都是从PowerShell位向stderr发出一些信息。 这可以通过TeamCity通过格式stderr输出作为选项直接完成(将其设置为错误,而不是默认值,这是警告

  • 然而,关键的是,还有必要在“ 失败条件 ”下打开“失败构build如果:… 错误消息由(原文如此)构build跑步者logging (如果有其他答案适用于您,可能已经打开了,但IME 容易忘记!)

这些选项在我的PowerShell脚本中无论如何都是为我工作的。 我花了几个小时。

对我来说,最好的select是在TeamCity和Powershell之间添加一个图层。 所以我只写了一个调用powershell脚本的ac#console应用程序。

我这样做的方式是在teamcity中调用一个名为RemoteFile.ps1的脚本

使用脚本参数:%system.RemoteServerFQDN%system.RemoteUser%system.RemoteUserPassword%%system.RemoteScriptName%system.RemotePropertiesFile%system.BuildVersion%%system.RunList%

 param ( [Parameter(Mandatory=$true)] $Computername, [Parameter(Mandatory=$true)] $Username, [Parameter(Mandatory=$true)] $Password, [Parameter(Mandatory=$true)] $ScriptName, [Parameter(Mandatory=$true)] $Propfile, [Parameter(Mandatory=$true)] $Version, [Parameter(Mandatory=$true)] [string[]]$DeploymentTypes ) $securePassword = ConvertTo-SecureString -AsPlainText -Force $Password $cred = New-Object System.Management.Automation.PSCredential $Username, $securePassword write-host "Readying to execute invoke-command..." Invoke-Command -ComputerName $Computername -Credential $cred -ScriptBlock { D:\Deployment\PowershellWrapper.exe $using:ScriptName $using:Propfile $using:Version $using:DeploymentTypes } -ArgumentList $ScriptName,$Propfile,$Version,$DeploymentTypes 

远程服务器上存在的指定位置。

然后该文件调用这个:powershellwrapper.exe也在指定的位置(我的脚本有4个parameter passing给PowerShell)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace PowershellWrapper { class Program { static void Main(string[] args) { try { string argFull = @"""{0} {1} {2} {3}"""; string arg0 = args[0]; string arg1 = args[1]; string arg2 = args[2]; string arg3 = args[3]; string argFinal = string.Format(argFull, arg0, arg1, arg2, arg3); ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = @"powershell.exe"; startInfo.Arguments = argFinal; startInfo.RedirectStandardOutput = false; startInfo.RedirectStandardError = false; startInfo.UseShellExecute = false; startInfo.RedirectStandardInput = true; startInfo.CreateNoWindow = false; Process process = new Process(); process.StartInfo = startInfo; process.Start(); } catch (Exception e) { Console.WriteLine("{0} Exception caught.", e); Console.WriteLine("An error occurred in the deployment.", e); Console.WriteLine("Please contact test@test.com if error occurs."); } } } 

}

然后用4个参数调用我的脚本,脚本是第一个参数,加上3个参数。 所以基本上这里发生的是我执行PowershellWrapper.exe而不是PowerShell脚本本身来捕获错误的退出代码0,它仍然报告运行回TeamCity日志的完整脚本。

希望是有道理的,它对我们来说就像一个魅力。