J'ai un script PowerShell comme suit
##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']
D'après ce que je suis amené à croire, une Throw
non capturée donnera un code de sortie de 1
, mais malheureusement, TeamCity dit le contraire.
[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
Il peut également être important de noter que mon Execution Mode
est défini sur Execute .ps1 script with "-File" argument
.
J'ai essayé de le changer en Put script into PowerShell stdin with "-Command -" arguments
, mais cela a échoué avec un code de sortie de 1
, même après des tests réussis. Je suis sûr que l'exécuter en tant que -File
sera le bon moyen.
Si j'ouvre le script situé à C:\BuildAgent\work\e903de7564e599c8\build.ps1
et que je l'exécute manuellement dans CMD, il fait la même chose ... c'est-à-dire que les tests échouant échouent et que %errorlevel%
est toujours 0
.
Pourtant, si je l'exécute dans PowerShell et que j'appelle $LASTEXITCODE
, il renvoie le bon code à chaque fois.
Il s'agit d'un problème connu avec PowerShell. L'exécution d'un script avec -file
renvoie un code de sortie de 0 alors que ce n'est pas le cas.
(Mise à jour: les liens ci-dessous ne fonctionnent plus. Recherchez ou signalez ce problème sur PowerShell: Hot (1454 idées) - Windows Server)
Étant donné que l'utilisation de -command
ne fonctionnait pas pour vous, vous pouvez essayer d'ajouter une interruption en haut du script:
trap
{
write-output $_
##teamcity[buildStatus status='FAILURE' ]
exit 1
}
Ce qui précède devrait donner un code de sortie approprié lorsqu'une exception est levée.
J'avais exactement ce problème lorsque je travaillais avec le -file
, mais pour une raison quelconque, la syntaxe d'interruption ou la syntaxe de sortie fournie par Kevin ne fonctionnait pas dans mon scénario.
Je ne sais pas pourquoi, mais juste au cas où quelqu'un d'autre aurait le même problème, j'ai utilisé la syntaxe ci-dessous et cela a fonctionné pour moi:
try{
#DO SOMETHING HERE
}
catch
{
Write-Error $_
##teamcity[buildStatus status='FAILURE']
[System.Environment]::Exit(1)
}
Jusqu'à ce que (vraisemblablement) ce soit fermé comme dup de ma réponse personnelle à une question plus ancienne , je résumerai ici la solution la plus propre:
La plupart des autres réponses impliquent d'émettre quelque chose dans la variable stderr
à partir du bit PowerShell. Cela peut être accompli directement avec TeamCity via l'option Format stderr en tant que (définissez-la sur Error au lieu de la valeur par défaut, qui est Warning).
Cependant, il est également nécessaire d’activer le message "Echec de la construction si: ... Un message d’erreur est consigné par le (sic) constructeur de la construction" sous "Conditions de défaillance" (si l’un des autres les réponses fonctionnent pour vous, cela sera probablement déjà activé, mais IME, il est très facile à oublier!)
Aucune de ces options ne fonctionnait pour moi dans mon script PowerShell pour une raison quelconque. J'ai passé des heures dessus.
Pour moi, la meilleure option consistait à placer une couche entre TeamCity et PowerShell. J'ai donc simplement écrit une application console C # qui appelle le script PowerShell.
Comme je le fais, dans TeamCity, nous appelons un script nommé: RemoteFile.ps1
Avec les arguments de 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
Qui existe sur le serveur distant à l'emplacement spécifié.
Ensuite, ce fichier appelle ceci: powershellwrapper.exe également à l'emplacement spécifié (mon script a quatre paramètres à transmettre au script 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.");
}
}
}
}
Et cela appelle mon script avec quatre paramètres. Le script étant le premier paramètre, plus trois arguments. Donc, essentiellement, ce qui se passe ici, c'est que j'exécute PowershellWrapper.exe au lieu du script PowerShell lui-même pour capturer les codes de sortie erronés 0 et qu'il continue de signaler le script complet en cours d'exécution dans le journal TeamCity.
J'espère que cela à du sens. Cela fonctionne comme un charme pour nous.
L'utilisation de -ErrorAction stop
sur une commande renvoie un code de sortie 1 par défaut et l'affiche également dans TeamCity sans ajouter de condition d'échec. Nous allons maintenant implémenter ce comportement par défaut pour chaque commande PowerShell utilisant $ErrorActionPreference = "Stop";
.