Ich habe ein PowerShell-Skript wie folgt
##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']
Soweit ich glaube, führt ein UngefangenerThrow
zu einem Exit-Code von 1
, aber leider sagt TeamCity etwas anderes.
[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
Es kann auch wichtig sein zu beachten, dass my auf eingestellt Execution Mode
ist Execute .ps1 script with "-File" argument
.
Ich habe versucht, es zu ändern Put script into PowerShell stdin with "-Command -" arguments
, aber dann ist es mit einem Exit-Code von 1
sogar beim Bestehen von Tests fehlgeschlagen . Ich bin mir sicher, dass es -File
der richtige Weg sein wird , es so zu betreiben .
Wenn ich das Skript in öffne C:\BuildAgent\work\e903de7564e599c8\build.ps1
und es manuell in CMD ausführe, funktioniert es genauso ... Das heißt, die fehlgeschlagenen Tests schlagen fehl und das %errorlevel%
ist immer noch so 0
.
Wenn ich es jedoch in PowerShell ausführe und aufrufe $LASTEXITCODE
, wird jedes Mal der richtige Code zurückgegeben.
quelle
[Environment]::Exit(1)
gleich nach jedem hinzuzufügenthrow
, aber es hat immer noch nicht funktioniert.throw
wird nicht ausgeführt.Antworten:
Dies ist ein bekanntes Problem mit PowerShell. Das Ausführen eines Skripts mit
-file
gibt den Exit-Code 0 zurück, wenn dies nicht der Fall sein sollte.(Update: Die folgenden Links funktionieren nicht mehr. Bitte suchen oder melden Sie dieses Problem in PowerShell: Hot (1454 Ideen) - Windows Server. )
https://connect.microsoft.com/PowerShell/feedback/details/777375/powershell-exe-does-not-set-an-exit-code-when-file-is-used
https://connect.microsoft.com/PowerShell/feedback/details/750653/powershell-exe-doesn-t-return-correct-exit-codes-when-using-the-file-option
Da die Verwendung
-command
bei Ihnen nicht funktioniert hat, können Sie versuchen, oben im Skript eine Falle hinzuzufügen:trap { write-output $_ ##teamcity[buildStatus status='FAILURE' ] exit 1 }
Das Obige sollte zu einem korrekten Exit-Code führen, wenn eine Ausnahme ausgelöst wird.
quelle
write-output $_
funktioniert,Write-Error -ErrorRecord $_
erzeugt ausgefallene Fehlerausgabe genau wie PowerShell selbstWrite-Error
wie von @Mike vorgeschlagen verwenden, stellen Sie sicher, dass Ihr$ErrorActionPreference
nicht auf "Stop" eingestellt ist. Andernfalls wird das Skript mit Code 0 beendet undexit 1
nie erreicht.Ich hatte genau dieses Problem beim Ausführen von
-file
, aber aus irgendeinem Grund funktionierte die Trap-Syntax oder die von Kevin bereitgestellte Exit-Syntax in meinem Szenario nicht.Ich bin mir nicht sicher warum, aber nur für den Fall, dass jemand anderes das gleiche Problem hat, habe ich die folgende Syntax verwendet und es hat bei mir funktioniert:
try{ #DO SOMETHING HERE } catch { Write-Error $_ ##teamcity[buildStatus status='FAILURE'] [System.Environment]::Exit(1) }
quelle
Bis dies (vermutlich) als Dup meiner Selbstantwort auf eine ältere Frage geschlossen wird, fasse ich hier die sauberste Lösung zusammen:
Bei den meisten anderen Antworten wird etwas
stderr
aus dem PowerShell-Bit ausgegeben. Dies kann direkt mit TeamCity über die Option Format stderr output als Option erreicht werden (setzen Sie sie auf Error anstelle der Standardeinstellung Warning ).Entscheidend ist jedoch auch, dass unter " Fehlerbedingungen " die Option "Build fehlschlagen, wenn: ... eine Fehlermeldung vom (sic) Build Runner protokolliert wird " aktiviert wird (wenn eine der anderen Antworten für Sie funktioniert) wahrscheinlich haben Sie dies bereits eingeschaltet, aber IME ist es sehr leicht zu vergessen!)
quelle
Die Verwendung
-ErrorAction stop
eines Befehls gibt standardmäßig einen Exit-Code 1 zurück und zeigt ihn auch in TeamCity an, ohne eine Fehlerbedingung hinzuzufügen. Dieses Verhalten wird jetzt standardmäßig für jeden verwendeten PowerShell-Befehl implementiert$ErrorActionPreference = "Stop";
.quelle
(Get-Content -path package.json -Raw) -replace '"version": "0.0.0"','"version": "0.0.1"' | Set-Content -Path package.json
keine Ausnahme macht? Wie auch immer, ich habe stattdessen nur die obige -ErrorAction verwendet, und jetzt schlägt der Schritt fehl, z. B. wenn die Datei fehlt. Ich habe es sowohl zum Get-Content als auch zum Set-ContentKeine dieser Optionen hat in meinem PowerShell-Skript aus irgendeinem Grund funktioniert. Ich habe Stunden damit verbracht.
Für mich war die beste Option, eine Ebene zwischen TeamCity und PowerShell zu legen. Also habe ich einfach eine C # -Konsolenanwendung geschrieben, die das PowerShell-Skript aufruft.
So wie ich es mache, nennen wir in TeamCity ein Skript mit dem Namen:
RemoteFile.ps1
Mit Skriptargumenten:% 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
Welches auf dem Remote-Server am angegebenen Speicherort vorhanden ist.
Diese Datei ruft dann Folgendes auf: Powershellwrapper.exe auch am angegebenen Speicherort (mein Skript verfügt über vier Parameter, die an das PowerShell-Skript übergeben werden müssen).
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."); } } } }
Und das ruft mein Skript mit vier Parametern auf. Das Skript ist der erste Parameter plus drei Argumente. Im Wesentlichen wird hier also die Datei PowershellWrapper.exe anstelle des PowerShell-Skripts selbst ausgeführt, um die fehlerhaften Exit-Code-Nullen zu erfassen, und das vollständige Skript wird weiterhin im TeamCity-Protokoll angezeigt.
Ich hoffe das ergibt Sinn. Es funktioniert wie ein Zauber für uns.
quelle