Почему мои коды выхода из командной строки всегда "0"?
У меня есть powershell script следующим образом
##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']
Из того, что мне повезло, unsaught 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
, но после этого он завершился с кодом выхода 1
даже при прохождении тестов. Я уверен, что запуск его как -File
будет правильным.
Если я открою script, расположенный в C:\BuildAgent\work\e903de7564e599c8\build.ps1
, и запустим его вручную в CMD, он сделает то же самое... IE: неудачные тесты завершаются ошибкой, а %errorlevel%
по-прежнему 0
.
YET, если я запустил его в powershell и вызвал $LASTEXITCODE
, он каждый раз возвращает правильный код.
Ответы
Ответ 1
Это известная проблема с PowerShell. Выполнение script с помощью -file
возвращает код выхода 0, если он не должен.
(Обновление: ссылки ниже не работают. Пожалуйста, найдите или сообщите об этой проблеме на https://windowsserver.uservoice.com/forums/301869-powershell)
Так как использование -command
не работает для вас, вы можете попробовать добавить ловушку в верхней части script:
trap
{
write-output $_
##teamcity[buildStatus status='FAILURE' ]
exit 1
}
Приведенное выше должно привести к правильному коду выхода при вызове исключения.
Ответ 2
У меня была эта точная проблема во время работы с -file, но по какой-то причине синтаксис ловушки или синтаксис "exit", предоставленный Кевином, не работал в моем сценарии. Не уверен, почему, но на всякий случай кто-то другой сталкивается с той же проблемой, я использовал синтаксис ниже, и это сработало для меня:
try{
#DO SOMETHING HERE
}
catch
{
Write-Error $_
##teamcity[buildStatus status='FAILURE']
[System.Environment]::Exit(1)
}
Ответ 3
До тех пор, пока это (предположительно) не будет закрыто как dup моего автоответчика более старого вопроса, я подытожу самое чистое решение здесь:
-
Большинство других ответов включают в себя что-то передать в stderr
из бита PowerShell. Это можно выполнить непосредственно с помощью TeamCity с помощью Формат stderr-вывода как (установите его Ошибка вместо значения по умолчанию, которое является предупреждением)
-
Однако, критически, также необходимо включить "Сбой сборки", если:... Сообщение об ошибке записывается в файл (sic) build runner "под" Сбой Условия "(если какой-либо из других ответов будет работать для вас, скорее всего, это будет включено, но IME очень легко забыть!)
Ответ 4
Ни один из этих вариантов не работал у меня в моей powershell script по какой-либо причине. Я потратил на это несколько часов.
Для меня лучшим вариантом было установить слой между TeamCity и Powershell. Поэтому я просто написал консольное приложение С#, которое вызывает powershell script.
Как я это делаю, в teamcity мы называем script named: RemoteFile.ps1
С script Аргументы:% 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 также в указанном месте (My script имеет 4 параметра для передачи в 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 [email protected] if error occurs.");
}
}
}
}
И это вызывает мой script с 4 параметрами, причем script является первым параметром плюс 3 аргумента. Таким образом, по сути, здесь происходит то, что я запускаю PowershellWrapper.exe вместо самой powershell script для захвата ошибочного кода выхода 0, и он все еще сообщает о полном script, возвращающемся в журнал TeamCity.
Надеюсь, что это имеет смысл, это работает как шарм для нас.