2013-04-03 1 views
66

У меня Powershell скрипта следующегоПочему мои коды выхода из командной строки всегда «0»?

##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'] 

Из того, что я ведущий верить, 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" arguement.

Я попытался изменить его на Put script into PowerShell stdin with "-Command -" arguements, но затем он не смог с кодом выхода 1 даже с прохождением тестов. Я уверен, что запуск его как -File будет правильным.

Если я открываю скрипт, расположенный на C:\BuildAgent\work\e903de7564e599c8\build.ps1 и запустить его вручную в CMD, он делает то же самое ... IE: Неисправные тесты потерпеть неудачу, и %errorlevel% еще 0.

YET, если я запустил его в PowerShell и позвонил $LASTEXITCODE, он каждый раз возвращает правильный код.

+0

Я даже попытался добавить '[Environment] :: Exit (1)' сразу после каждого 'throw', но он все равно не работает. –

+4

Любой код после 'throw' не выполняется. –

+1

Если вы измените уровень ошибок для шага сборки с «предупреждения» на «ошибка», это имеет значение? – devlord

ответ

86

Это известная проблема с PowerShell. Выполнение скрипта с -file возвращает код выхода 0, если он не должен.

(Update:. Ссылки ниже больше не будет работать Пожалуйста, искать, или сообщить, эту проблему на https://windowsserver.uservoice.com/forums/301869-powershell)

С помощью -command не работает для вас, вы можете попробовать добавить ловушку в верхней части скрипта:

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

Приведенное выше должно привести к правильному коду выхода при возникновении исключения.

+1

Можете ли вы объяснить «почему» эта ловушка будет работать? Я проверю это завтра, когда вернусь к проекту. –

+4

Поскольку завершение сценария с помощью 'throw' не позволяет получить правильный код выхода, нам нужно поймать исключения и выйти вручную. 'trap' является оригинальным механизмом обработки исключений PowerShell. ('try' /' catch' появился в V2.) Подумайте об этом как о «catch» для любого исключения, созданного в той же области, для которой определен 'trap'. Хорошая информация о 'trap': http://huddledmasses.org/trap-exception-in-powershell/ и http://blogs.msdn.com/b/powershell/archive/2009/06/17/traps-vs- try-catch.aspx –

+0

безупречный, тот работал. Интересно, что это то, что происходит. На самом деле это расстраивает. Я довольно много искал, прежде чем задавать этот вопрос, хотя я «предположил», что это была проблема с моей командой teamcity, а не с ошибкой в ​​powershell. –

23

У меня была эта точная проблема во время работы с -file, но по какой-то причине синтаксис ловушки или синтаксис «выхода», предоставленный Кевином, не работал в моем сценарии. Не знаю почему, но на всякий случай, если кто-то попадает в ту же проблему, я использовал ниже синтаксис, и это работает для меня:

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

Я не пробовал ловушку, но повторный бросок с простым «выходом -1» исправил проблему для меня. –

1

Ни один из этих вариантов не работал для меня в моем Powershell скрипт для какой-либо причине. Я потратил на это много часов.

Для меня лучшим вариантом было установить слой между TeamCity и Powershell. Поэтому я просто написал консольное приложение C#, которое вызывает сценарий powershell.

Путь я сделать это, в 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 параметра, чтобы пройти к 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."); 
     } 

    } 
} 

}

И что вызывает мой сценарий с 4 параметрами , причем сценарий является первым параметром плюс 3 аргумента. Поэтому, по сути, здесь происходит то, что я запускаю PowershellWrapper.exe вместо сценария powershell, чтобы захватить ошибочный код выхода 0, и он все еще сообщает, что полный скрипт возвращается в журнал TeamCity.

Надеюсь, что это имеет смысл, он работает как очарование для нас.

+0

Sholdnt необходимо [мой ответ] (http://stackoverflow.com/a/37025444/11635), похоже, работает эквивалентно, не требуя бородавок в сценарии и/или других помощниках - его, к сожалению, не очень хорошо документированы в TeamCity. См. Http://stackoverflow.com/questions/11647987/how-do-i-get-errors-to-propagate-in-the-teamcity-powershell-runner –

9

До этого (предположительно) закрывается, как of my self-answer of an older question дублировать, я кратко чистейшее решение здесь:

  • Большинство других ответов вовлекает излучающее что-то к stderr от бита PowerShell. Это может быть достигнуто непосредственно с TeamCity через выход Stderr Format в качестве опции (установите его в Error вместо значения по умолчанию, который является Предупреждение)

  • Однако критически, это также необходимо включить «Сбой сборки, если: ... Сообщение об ошибке регистрируется (sic) build runner« под »Условия сбоя» (если какой-либо из других ответов вам подходит, скорее всего, вы включили его уже, но IME это очень легко забыть!)

+2

Это, безусловно, самое чистое решение на этой странице – lantrix