2012-02-16 14 views
5

È possibile accodare le compilazioni in modo sincrono?API TFS 2010: la coda viene generata in modo sincrono e ottiene lo stato di ogni build accodata: "Esegui agente (in attesa di creazione agente)"

Ho provato qualcosa di simile:

CodeActivity:

[BuildActivity(HostEnvironmentOption.Agent)] 
public sealed class QueueNewBuild : CodeActivity<BuildResult> 
{ 
    // The Team Project that the build definition belongs to. 
    [RequiredArgument] 
    public InArgument<IBuildDetail> BuildDetail { get; set; } 

    // The build definition to queue 
    [RequiredArgument] 
    public InArgument<String> BuildDefinition { get; set; } 

    protected override BuildResult Execute(CodeActivityContext context) 
    { 
     // Obtain the runtime value of the input arguments 
     var buildDefinitionName = context.GetValue(BuildDefinition); 
     var buildDetail = context.GetValue(BuildDetail); 

     // Obtain the Team Project for the current build definition. 
     var tfsProject = buildDetail.BuildDefinition.TeamProject; 

     var configurationServerUri = buildDetail.BuildServer.TeamProjectCollection.Uri.ToString(); 

     var server = new TfsTeamProjectCollection(new Uri(configurationServerUri)); 
     server.EnsureAuthenticated(); 
     var buildServer = server.GetService<IBuildServer>(); 
     var buildDefinition = buildServer.GetBuildDefinition(tfsProject, buildDefinitionName); 

     var queuedBuild = buildServer.QueueBuild(buildDefinition); 

     var buildStatusWatcher = new BuildStatusWatcher(queuedBuild.Id); 
     buildStatusWatcher.Connect(buildServer, tfsProject); 

     do 
     { 
     } while (buildStatusWatcher.Status != QueueStatus.Completed && buildStatusWatcher.Status != QueueStatus.Canceled); 

     buildStatusWatcher.Disconnect(); 

     return new BuildResult 
     { 
      WasSuccessfully = buildStatusWatcher.Build.CompilationStatus == BuildPhaseStatus.Succeeded, 
      BuildDetail = buildStatusWatcher.Build 
     }; 
    } 
} 

BuildResult:

public class BuildResult 
{ 
    public bool WasSuccessfully { get; set; } 
    public IBuildDetail BuildDetail { get; set; } 
} 

BuildStatusWatcher:

public class BuildStatusWatcher 
{ 
    private IQueuedBuildsView _queuedBuildsView; 
    private readonly int _queueBuildId; 
    private QueueStatus _status; 
    private IBuildDetail _build; 

    public BuildStatusWatcher(int queueBuildId) 
    { 
     _queueBuildId = queueBuildId; 
    } 

    public IBuildDetail Build 
    { 
     get { return _build; } 
    } 

    public QueueStatus Status 
    { 
     get { return _status; } 
    } 

    public void Connect(IBuildServer buildServer, string tfsProject) 
    { 
     _queuedBuildsView = buildServer.CreateQueuedBuildsView(tfsProject); 
     _queuedBuildsView.StatusChanged += QueuedBuildsViewStatusChanged; 
     _queuedBuildsView.Connect(10000, null); 
    } 

    public void Disconnect() 
    { 
     _queuedBuildsView.Disconnect(); 
    } 

    private void QueuedBuildsViewStatusChanged(object sender, StatusChangedEventArgs e) 
    { 
     if (e.Changed) 
     { 
      var queuedBuild = _queuedBuildsView.QueuedBuilds.FirstOrDefault(x => x.Id == _queueBuildId); 
      if (queuedBuild != null) 
      { 
       _status = queuedBuild.Status; 
       _build = queuedBuild.Build; 
      } 
     } 
    } 
} 

Quindi sto cercando di attendere fino a quando la compilazione è completata o annullata, ma questo non funziona, perché l'agente di compilazione del sotto build è in attesa per tutto il tempo.

Ho un processo di master build (eseguito sull'agente 1) che richiama 13 processi di sub-build (tutti eseguiti sull'agente 2). E voglio aspettare ogni processo di sub-build in modo che possa abortire il processo di master build quando un processo di sub-build fallisce.

Qualche idea?

UPDATE:

Service 'XXX - Agente1' aveva un'eccezione: Messaggio eccezione: L'operazione non è stata completata entro il timeout assegnato di 00:00:30. Il tempo assegnato a questa operazione potrebbe essere una parte di un timeout più lungo di . (Tipo FaultException`1)

Eccezione dello stack: a Microsoft.TeamFoundation.Build.Machine.BuildAgentService.TerminateWorkflow (TerminatingException ex)

Il flusso di lavoro:

enter image description here

+1

Quindi, stai dicendo che TUTTA la tua richiesta di Build programmata non è stata avviata? O che non puoi ordinare più Build? – pantelif

+1

Si blocca alla prima sub-build accodata. "Esegui su agente (in attesa dell'agente di creazione) .L'agente di generazione per la creazione secondaria ha lo stato:" Esecuzione del flusso di lavoro ". Lo stato dell'agente di generazione per la build principale è:" Pronto " – Rookian

+1

Ho capito che l'agente non l'agente di costruzione "si blocca" sporadicamente – Rookian

risposta

1

Dal momento che tutto ciò che si ha è un addizionale Build Agent, penso che non ti porti molto a impiegare un modulo così complesso.

Implementare due attività distinte:
Un'attività che accetta come input un BuildDetail e un BuildDefinition come uscite una volta che una nuova build è stata accodata.
Vorrei richiamare questa attività all'interno di un ciclo in XAML. Questo farebbe la coda di tutte le build nell'agente di build n. 2.

La seconda attività verifica lo stato dell'agente di build n. 2 & attendi che l'agente diventi di nuovo inattivo.
Una volta eseguito, vorrei verificare ogni definizione di build che si suppone sia stata eseguita correttamente nell'Agente n. 2 con qualcosa del genere:
if(buildDefinition.LastGoodBuildUri != buildDefinition.LastBuildUri)

Ciò che potrebbe sembrare uno svantaggio con questo approccio, è il fatto che la compilazione NON fallirà/fermati alle primissime build "da bambino" che si rompono.
A mio parere, questo è in realtà un vantaggio: se più di uno fallisce, lo saprai immediatamente.

+0

Ho trovato il problema L'agente di compilazione errato è stato scelto dal mio master build, ma non so perché questo è il caso, perché ho impostato la definizione di master build da usare un agente specifico – Rookian

1

Ho creato un modello speciale per avviare le build in ordine. Il modello di base:

Prendi la build, QueueBuild, MonitorBuild

Prendi il Corporatura è l'attività che è nel modello predefinito. È la prima attività del mio modello e viene chiamata solo una volta all'inizio.

QueueBuild è l'attività ottenuta da TFSExtensions su codeplex. Creo una variabile che contiene un oggetto IQueueBuild, che è il risultato dell'attività di QueueBuild per ogni build che ho intenzione di avviare. Ho impostato il risultato su questa variabile. Ho chiamato il mio CurrentQueueBuild. Dopo che ogni attività di QueueBuild ha dato il via a una build, questa variabile viene aggiornata alla coda di build corrente dall'attività.

MonitorBuild è una sequenza che ho fatto che fa la maggior parte della 'sincronizzazione':

primo è un'attività che controlla se CurrentQueueBuild è nullo (CurrentQueueBuild è niente). Se lo è, lancio un'eccezione perché non posso averlo.

Il secondo è un'attività While (chiamata 'While building'). La sua condizione è 'CurrentQueueBuild.Status = BuildStatus.InProgress'. Nel corpo di The While ho un'altra sequenza che contiene un'attività InvokeMethod (TargetObject = CurrentQueueBuild, MethodName = Refresh e ho aggiunto il parametro In di tipo QueryOptions impostato su QueryOptions.All). Seguo InvokeMethod con un'attività di ritardo impostata per attendere 5 secondi.

Infine, scrivo un messaggio di build per registrare lo stato. Questo ovviamente è facoltativo.

Quindi, per concludere, ho 5 costruisce ho kick off e per ciascuno ho un'attività QueueBuild seguito da un monitor costruire attività messo insieme come descritto sopra.

Spero che questo aiuti qualcuno.

+1

Come è questa NON la risposta accettata? Questa era una soluzione molto semplice a quello che potrebbe essere un brutto problema. – JohnZaj