我有一个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. Unfortunately .NET 4.0 doesn't appear to be installed on this machine."
##teamcity[buildStatus status='FAILURE' ]
}
##teamcity[progressMessage 'Setting up variables']
# Set up variables for the 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']
据我所知,an uncaught 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" argument
可能也很重要。
我尝试将其更改为Put script into PowerShell stdin with "-Command -" arguments
,但即使通过测试,它也会以退出代码1
失败。我确信以-File
运行它将是正确的方法。
如果我打开位于C:\BuildAgent\work\e903de7564e599c8\build.ps1
的脚本并在CMD中手动运行它,它会做同样的事情......即,失败的测试失败,而%errorlevel%
仍然是{{1 }}
然而,如果我在PowerShell中运行它并调用0
,它每次都会返回正确的代码。
答案 0 :(得分:91)
这是PowerShell的一个已知问题。执行带有-file
的脚本时,不应该返回退出代码0。
(更新:以下链接不再有效。请在 PowerShell: Hot (1454 ideas) – Windows Server 上查找或报告此问题
由于使用-command
不适合您,您可以尝试在脚本顶部添加陷阱:
trap
{
write-output $_
##teamcity[buildStatus status='FAILURE' ]
exit 1
}
当抛出异常时,上面应该会产生正确的退出代码。
答案 1 :(得分:24)
我在使用-file
时遇到了这个问题,但出于某种原因,陷阱语法或退出'凯文提供的语法并没有在我的场景中工作。
我不知道为什么,但是为了防止其他人遇到同样的问题,我使用了以下语法,它对我有用:
try{
#DO SOMETHING HERE
}
catch
{
Write-Error $_
##teamcity[buildStatus status='FAILURE']
[System.Environment]::Exit(1)
}
答案 2 :(得分:13)
在此(大概)以dup of my self-answer of an older question关闭之前,我将在此总结最清晰的解决方案:
大多数其他答案都涉及从PowerShell位向stderr
发送内容。这可以通过格式stderr输出选项直接使用TeamCity完成(将其设置为错误而不是默认值,即警告)< / p>
但是,关键的是,还必须在“失败”下打开“失败构建如果:... 错误消息由(原文如此)构建运行器”记录条件“(如果其他任何答案对你有用,你可能已经开启了这个,但IME很容易忘记
答案 3 :(得分:1)
无论出于何种原因,这些选项都不适用于我的PowerShell脚本。我花了几个小时。
对我来说,最好的选择是在TeamCity和PowerShell之间放置一个层。所以我只是编写了一个调用PowerShell脚本的C#控制台应用程序。
我这样做的方式是,在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也在指定位置(我的脚本有四个参数传递给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.");
}
}
}
}
然后用四个参数调用我的脚本。该脚本是第一个参数,加上三个参数。所以基本上这里发生的是我正在执行PowershellWrapper.exe而不是PowerShell脚本本身来捕获错误的退出代码0,它仍然报告完整的脚本运行回TeamCity日志。
我希望这是有道理的。它对我们来说就像是一种魅力。
答案 4 :(得分:0)
在命令上使用-ErrorAction stop
会默认返回退出代码1,并在TeamCity中也显示该退出代码,而不添加失败条件。现在,默认情况下,我们将使用$ErrorActionPreference = "Stop";
为每个PowerShell命令实现此行为。