TFS 2010 Object Model - Builder
Scritto da
Massimo Bonanni
il
mercoledì 18 maggio 2011
Linguaggio:
•
Framework:
•
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:

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:

- 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:

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:
- Si ottiene un oggetto che implementa IBuildDefinition
utilizzando il metodo CreateBuildDefinition del servizio
IBuildServer;
- Si impostano le opportune proprietà dell'oggetto appena creato
(nome, descrizione, processo e via dicendo);
- 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:
- Utilizzare l'interfaccia IBuildServer ed il metodo
QueueBuild;
- 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):

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:

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