TFS 2010 Object Model - Builder

Scritto da  Massimo Bonanni il mercoledì 18 maggio 2011
Linguaggio: VB   •  Framework: 4.0   •  Livello: 200

Download pdf


Introduzione (a cura di Matteo Emili)
La Team Foundation Build è il pilastro dell'infrastruttura di Visual Studio ALM che permette la compilazione autonoma del codice sorgente da parte di un server, eliminando i possibili problemi di conflitto di dipendenze per le applicazioni.
Si basa sull'accoppiata Controller-Agent, il primo è dedicato alla Team Project Collection, ed accetta richieste da parte dei Team Project presenti all'interno. Processa il workflow ed esegue tutto il lavoro accessorio per il completamento di una build (logging, report dello status, applicazione delle label nel Source control, ecc.). Inoltre gestisce un pool di uno o più Build Agent, ossia il componente che esegue materialmente le compilazioni. L'utilizzo di più Build Agent permette di differenziare il carico assegnato all'Agent stesso (molto CPU intensive) e rendere il sistema di Team Foundation Build estremamente scalabile.
In questo articolo prenderemo in esame il modulo di Team Foundation Server che permette l'automazione delle build. Anche in questo caso l'argomento è molto vasto e ci limiteremo ai concetti essenziali ma sufficienti per poter iniziare ad utilizzare le funzionalità di build.

Il servizio IBuildServer
Una delle interfaccie più importanti per la gestione delle build in Team Foundation Server è l'interfaccia IBuildServer.
Possiamo recuperare un'istanza del servizio che implementa questa interfaccia attraverso la classe TeamProjectCollection e il metodo GetService:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MyServer:8080/tfs/MyCollection"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)

 

L'interfaccia IBuildServer mette a disposizione tutta una serie di funzionalità che vanno da quelle per la creazione e cancellazione degli agent e delle definizioni di build fino alle funzionalità di query per recuperare le definizioni presenti nel repository.
Inizieremo la nostra trattazione proprio da quest'ultimo gruppo di funzionalità e vedremo alcune possibili modalità di ricerca.

Le query sul repository
Grazie al metodo QueryBuildDefinitions dell'interfaccia IBuildServer, siamo in grado di eseguire delle query per ricercare le definizioni di Build presenti all'interno di una collection.
Ad esempio, se vogliamo ottenere le definizioni di build disponibili per il progetto "MioProgetto" nella collezione "MiaCollezione", possiamo scrivere:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezioe"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")

 

Il metodo QueryBuildDefinitions restituisce una collezione di istanze di IBuildDefinition che descrivono le build definite per il nostro progetto:

Figura1

Come possiamo vedere, l'interfaccia espone una serie di interessanti proprietà le più importanti delle quali sono:

  • BuildController : espone i dati relativi al build controller relativo alla build definition;
  • ContinuousIntegrationType : espone la tipologia di continuous integration definita per la build (ad esempio Individual continuous integration);
  • Description : descrizione della build (quella che abbiamo inserito in fase di creazione);
  • Id : identificativo della definizione di build;
  • Name : nome della build;
  • Process : espone le proprietà del process template relativo alla build definition;
  • ProcessParameters : espone la definizione XML relativa ai parametri di configurazione del processo di build;
  • Schedules : espone l'elenco di oggetti ISchedule che descivono la schedulazione della build.

In particolare prendiamo in esame ProcessParameters e Schedules.
Un esempio di XML esposto dalla proprietà ProcessParameters è il seguente:

<Dictionary x:TypeArguments="x:String, x:Object" xmlns="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:mtbwa="clr-namespace:Microsoft.TeamFoundation.Build.Workflow.Activities;assembly=Microsoft.TeamFoundation.Build.Workflow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <mtbwa:BuildSettings x:Key="BuildSettings" ProjectsToBuild="">
    <mtbwa:BuildSettings.PlatformConfigurations>
      <mtbwa:PlatformConfigurationList Capacity="1">
        <mtbwa:PlatformConfiguration Configuration="Debug" Platform="Any CPU" />
      </mtbwa:PlatformConfigurationList>
    </mtbwa:BuildSettings.PlatformConfigurations>
  </mtbwa:BuildSettings>
</Dictionary>

 

L'XML contiene le definizioni impostate per la Build in oggetto e può anche essere utilizzato per configurarla.
La proprietà Schedules contiene, invece, le informazioni relative alle schedulazioni delle nostre build ed in particolare è una collezione di oggetti ISchedule:

Figura2

  • DaysToBuild : un enumerazione di tipo flag che permette di definire quali sonoi giorni in cui schedulare la build;
  • StartTime : ora di inizio della build in termini di secondi a partire dalla mezzanotte. Se vogliamo far partire la build alle 9:00 di mattina, allora il valore di StartTime deve essere 32400;
  • TimeZone : contiene il time zone di riferimento (classe TimeZoneInfo) per l'orario di inizio della build;
  • Type : oggetto di classe ScheduleType che indica la tipologia di schedulazione (attualmente è presente solo la Weekly).

L'interfaccia IBuildDefinition contiene le informazioni più importanti relative ad una build definita all'interno di un progetto. Se vogliamo delle informazioni di dettaglio su una build specifica, l'interfaccia IBuildDefinition ci mette a disposizione il metodo QueryBuilds che restituisce un oggetto che implementa l'interfaccia IBuildDetail:

Figura3

 Come possiamo vedere, questa interfaccia espone una maggiore quantità di proprietà che descrivono completamente la nostra build.
Un esempio di recupero di tutte le BuildDetail delle build presenti all'interno di un progetto è il seguente:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione))Mio Progetto")
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildQuery = buildServer.QueryBuildDefinitions("
For Each build In buildQuery
    Dim buildDefinition = build.QueryBuilds()
Next

 

L'interfaccia IBuildServer espone tutta una serie di metodi per interrogare il repository relativo alle build ed in particolare:

  • QueryBuildAgents : permette di recuperare i build agents;
  • QueryBuildControllers : permette di recuperare  i build controllers;
  • QueryBuild : permette di recuperare le build eseguite per un progetto (vedremo in dettaglio di seguito);
  • QueryProcessTemplates : permette di recuperare i template di processo definiti all'interno di un progetto.

Creare nuove Build Definition
L'interfaccia IBuildserver ci mette a disposizione una serie di metodi che consentono la creazione di definizioni di build.
Il procedimento con cui creare una nuova build è il seguente:

  1. Si ottiene un oggetto che implementa IBuildDefinition utilizzando il metodo CreateBuildDefinition del servizio IBuildServer;
  2. Si impostano le opportune proprietà dell'oggetto appena creato (nome, descrizione, processo e via dicendo);
  3. Si esegue il metodo Save della IBuildDefinition per rendere permanenti sul server le modifiche.

Un esempio è il seguente:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")
 
Dim build = (From b In buildQuery
           Where String.Compare(b.Name, "Build definition Numero 1", True) = 0
           Select b).FirstOrDefault()
 
If build IsNot Nothing Then
    Dim newBuild = buildServer.CreateBuildDefinition("MioProgetto")
    newBuild.Name = "Build definition numero 2"
    newBuild.BuildController = build.BuildController
    newBuild.Process = build.Process
    Dim schedule = newBuild.AddSchedule()
    schedule.DaysToBuild = ScheduleDays.Monday Or ScheduleDays.Wednesday
    schedule.StartTime = 36000
    newBuild.Save()
End If

 

In questo caso viene create una nuova definizione di build per il progetto "MioProgetto"a partire dalla definizione "Build definition numero 1" (cioè con lo stesso build controller e processo) con uno schedule alle ore 10:00 del lunedì e del mercoledì.
Le proprietà BuildController e Process sono obbligatorie.
Se il metodo Save non può essere completato, viene sollevata una eccezione.

Eliminazione degli elementi sul server
L'interfaccia IBuildServer espone una serie di metodi che ci consentono di eliminare elementi presenti sul server e legati alla gestione delle build:

  • DeleteBuildAgents : elimina dei build agents dal server;
  • DeleteBuildControllers : permette di eliminare uno o più build controllers;
  • DeleteBuildDefinitions : permette di eliminare delle definizioni di build;
  • DeleteBuilds : permette di eliminare delle build (elimina gli artefatti ma non i record di build, per eliminare questi è necessario utilizzare DestroyBuilds);
  • DeleteProcessTemplate : permette di eliminare dei process templates.

Alcune delle interfacce che definiscono i singoli oggetti (ad esempio IBuildDefinition per le definizioni di build o IprocessTemplate per i template di process) mettono a disposizione anche dei metodi specifici per l'eliminazione.
Ad esempio, per eliminare una specifica definizione di build possiamo scrivere: 

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")
 
Dim build = (From b In buildQuery
           Where String.Compare(b.Name, "Build definition numero 2", True) = 0
           Select b).FirstOrDefault()
 
If build IsNot Nothing Then
    build.Delete()
End If

 


Gestire le build
Nei precedenti paragrafi abbiamo visto come recuperare e gestire le definizioni di build. Vediamo ora come eseguire una build in base ad una definizione e come recuperare l'elenco delle build per un determinato progetto.
Per eseguire entrambi i compiti possiamo utilizzare l'istanza di build definition (interfaccia IBuildDefinition) recuperata a partire da un'istanza di IBuildServer.
Per poter creare, da codice una nuova build a partire da una build definition, abbiamo due strade:

  1. Utilizzare l'interfaccia IBuildServer ed il metodo QueueBuild;
  2. Creare una nuova richiesta di build utilizzando l'interfaccia IBuildDefinition (metodo CreateBuildRequest) e utilizzare il metodo QueueBuild dell'interfaccia IBuildServer per mettere in coda la generazione effettiva della build.

Vediamo il primo caso:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")
 
Dim build = (From b In buildQuery
           Where String.Compare(b.Name, "Build definition numero 1", True) = 0
           Select b).FirstOrDefault()
 
If build IsNot Nothing Then
    Dim queueBuild = buildServer.QueueBuild(build)
End If

 

Per verificare che la build sia stata messa in coda (ed eseguita) possiamo aprire la finestra "Build Explorer" (tasto destro sulla cartella Builds del Team Explore e menù View Builds):

Figura4

In alternativa, il secondo caso:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")
 
Dim build = (From b In buildQuery
           Where String.Compare(b.Name, "Build definition numero 1", True) = 0
           Select b).FirstOrDefault()
If build IsNot Nothing Then
    Dim queueReq = build.CreateBuildRequest()
    Dim queuedBuild = buildServer.QueueBuild(queueReq)
End If

 

In entrambi i casi, il metodo QueueBuild restituisce un oggetto che implementa l'interfaccia IQueuedBuild che contiene tutte le informazioni relative al job di build in coda:

Figura5

 L'interfaccia ci consente, oltre che sapere dove sarà salvata la build (DropLocation), lo stato del job (Status) ed altre utili informazioni, anche di controllare il job di build. Ad esempio possiamo metterci in attesa dello start del processo grazie al metodo WaitForBuildStart oppure cancellare la build con il metodo Cancel (una build può essere cancellata solo se è ancora in coda oppure se è stata postposta).
Nel seguente esempio creaiamo una richiesta di build, la aggiungiamo alla coda, attendiamo l'avvio della compilazione e monitoriamo (con un normale polling) fino alla chiusura del job:

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildQuery = buildServer.QueryBuildDefinitions("MioProgetto")
 
Dim build = (From b In buildQuery
           Where String.Compare(b.Name, "Build definition numero 1", True) = 0
           Select b).FirstOrDefault()
 
If build IsNot Nothing Then
    Dim queueReq = build.CreateBuildRequest()
    Dim queuedBuild = buildServer.QueueBuild(queueReq)
    queuedBuild.WaitForBuildStart()
    While queuedBuild.Status = QueueStatus.InProgress
        Threading.Thread.Sleep(5000)
        queuedBuild.Refresh(QueryOptions.All)
    End While
End If

 

 I dati esposti dall'interfaccia IQueuedBuild non sono aggiornati in real time ma deve essere eseguito un Refresh per aggiornarli allo stato corrente.
Per recuperare l'elenco delle build eseguite per un determinato progetto possiamo utilizzare il metodo QueryBuilds messoci a disposizione dall'interfaccia IBuildServer. Il metodo prevede diversi overload ed il più semplice è quello che, dato il nome di un team project, restituisce un array di oggetti che implementano l'interfaccia IBuildDetail.

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
Dim builds = buildServer.QueryBuilds("MioProgetto")

 

Tutti I metodi di query dell'interfaccia IBuildServer prevedono la possibilità di specificare dettagliatamente l'oggetto della query. Ad esempio, il metodo QueryBuilds prevede un overload che accetta un oggetto che implementa IBuildDetailSpec che permette di eseguire la query con opportuni filtri.

Dim tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri("http://MioServer:8080/tfs/MiaCollezione"))
Dim buildServer = CType(tpc.GetService(Of IBuildServer)(), IBuildServer)
 
Dim buildDettSpec = buildServer.CreateBuildDetailSpec("MioProgetto")
buildDettSpec.MinFinishTime = DateTime.Now.Date.AddHours(13)
buildDettSpec.MaxFinishTime = DateTime.Now.Date.AddHours(14)
 
Dim builds = buildServer.QueryBuilds(buildDettSpec)

 

 In questo esempio si cercano tutte le build del progetto "MioProgetto" eseguite tra le 13 e le 14 della data corrente.
Quando si esegue una query con un overload che accetta una interfaccia di specificazione (vedi la IbuildDetailSpec), il risultato è un oggetto che implementa l'interfaccia IBuildQueryResult.

 

Riferimenti

[1] Team Foundation Server Architecture: http://msdn.microsoft.com/en-us/library/ms252473.aspx
[2] Extending Team Foundation: http://msdn.microsoft.com/en-us/library/bb130146.aspx
[3] Team Foundation Server 2010 SDK: http://code.msdn.microsoft.com/TfsSdk
[4] Team Foundation Server Team Blog: http://blogs.msdn.com/b/team_foundation/


Tags: TFS,Team Foundation,Team Foundation Server,tfs build

 
x