Configurazione del ripristino dei pacchetti con Team Foundation Build

Questo articolo presenta in modo dettagliato le procedure per ripristinare i pacchetti nell'ambito della compilazione di Team Services per il controllo della versione di Git e Team Services.

Anche se questa procedura dettagliata è specifica per lo scenario d'uso di Visual Studio Team Services, i concetti si applicano anche ad altri sistemi di controllo della versione e di compilazione.

Si applica a:

  • Progetti MSBuild personalizzati in esecuzione in qualsiasi versione di TFS
  • Team Foundation Server 2012 o versione precedente
  • Modelli di processi personalizzati di Team Foundation Build di cui è stata eseguita la migrazione a TFS 2013 o versioni successive
  • Modelli del processo di compilazione da cui è stata rimossa la funzionalità di ripristino Nuget

Se si usa Visual Studio Team Services o Team Foundation Server 2013 con i relativi modelli del processo di compilazione, il ripristino automatico dei pacchetti avviene come parte del processo di compilazione.

Approccio generale

Un vantaggio dell'uso di NuGet è la possibilità di usarlo per evitare l'archiviazione dei file binari nel sistema di controllo della versione.

Questo aspetto è particolarmente interessante se si usa un sistema di controllo della versione distribuito come Git, perché gli sviluppatori devono clonare un intero repository, compresa la cronologia completa, prima di poter lavorare in locale. L'archiviazione dei file binari può causare un aumento significativo delle dimensioni del repository, perché i file binari vengono in genere archiviati senza compressione delta.

NuGet supporta il ripristino dei pacchetti come parte della compilazione già da molto tempo. L'implementazione precedente presentava un circolo vizioso per i pacchetti che volevano estendere il processo di compilazione, perché NuGet ripristinava i pacchetti durante la compilazione del progetto. MSBuild non consente tuttavia di estendere la compilazione durante la compilazione. Si potrebbe sostenere che è un problema di MSBuild, ma è in effetti un problema intrinseco. A seconda dell'aspetto che è necessario estendere, potrebbe essere troppo tardi effettuarne la registrazione al momento del ripristino del pacchetto.

Per evitare questo problema, è necessario assicurarsi che i pacchetti vengano ripristinati come primo passaggio del processo di compilazione:

nuget restore path\to\solution.sln

Quando il processo di compilazione ripristina i pacchetti prima di compilare il codice, non è necessario archiviare i file .targets

Nota

I pacchetti devono essere creati in modo da consentire il caricamento in Visual Studio. In caso contrario, può essere ancora necessario archiviare i file .targets in modo che altri sviluppatori possano semplicemente aprire la soluzione senza dover prima ripristinare i pacchetti.

Il progetto dimostrativo seguente illustra come configurare la compilazione in modo che non sia necessario archiviare le cartelle packages e i file .targets. Illustra anche come configurare una compilazione automatizzata in Team Foundation Service per questo progetto di esempio.

Struttura del repository

Il progetto dimostrativo è un semplice strumento da riga di comando che usa l'argomento della riga di comando per eseguire query in Bing. È destinato a .NET Framework 4 e usa molti dei pacchetti BCL (Microsoft.Net.Http, Microsoft.Bcl, Microsoft.Bcl.Async e Microsoft.Bcl.Build).

La struttura del repository è simile alla seguente:

<Project>
    │   .gitignore
    │   .tfignore
    │   build.proj
    │
    ├───src
    │   │   BingSearcher.sln
    │   │
    │   └───BingSearcher
    │       │   App.config
    │       │   BingSearcher.csproj
    │       │   packages.config
    │       │   Program.cs
    │       │
    │       └───Properties
    │               AssemblyInfo.cs
    │
    └───tools
        └───NuGet
                nuget.exe

È possibile notare che non sono stati archiviati né la cartella packages né qualsiasi file .targets.

È stato tuttavia archiviato il file nuget.exe, perché è necessario durante la compilazione. Seguendo una convenzione ampiamente diffusa, il file è stato archiviato in una cartella tools condivisa.

Il codice sorgente è disponibile nella cartella src. Anche se la demo usa una singola soluzione, si può facilmente immaginare che questa cartella possa contenere più di una soluzione.

Ignorare i file

Nota

Attualmente è presente un [known bug in the NuGet client](https://nuget.codeplex.com/workitem/4072) oggetto che fa sì che il client aggiunga ancora la packages cartella al controllo della versione. La soluzione alternativa consiste nel disabilitare l'integrazione del controllo del codice sorgente. A tale scopo, è necessario un Nuget.Config file nella .nuget cartella parallela alla soluzione. Se questa cartella non esiste ancora, è necessario crearla. In Nuget.Config aggiungere il contenuto seguente:

<configuration>
    <solution>
        <add key="disableSourceControlIntegration" value="true" />
    </solution>
</configuration>

Per comunicare al sistema di controllo della versione che non si intende archiviare il contenuto delle cartelle packages, sono stati anche aggiunti i file da ignorare sia per Git (.gitignore) che per il controllo della versione di Team Foundation (.tfignore). Questi file descrivono i modelli di file che non si vuole archiviare.

Il file .gitignore è simile al seguente:

syntax: glob
*.user
*.suo
bin
obj
packages
*.nupkg
project.lock.json
project.assets.json

Il file .gitignore offre molte potenzialità. Ad esempio, se in genere non si vuole archiviare il contenuto della cartella packages, ma si vogliono rispettare le indicazioni precedenti di archiviare i file .targets, è possibile usare la regola seguente:

packages
!packages/**/*.targets

Questa regola escluderà tutte le cartelle packages ma includerà di nuovo tutti i file .targets contenuti. È possibile trovare un modello per i file .gitignore su misura per le esigenze degli sviluppatori di Visual Studio qui.

Il controllo della versione di Team Foundation supporta un meccanismo molto simile tramite il file tfignore. La sintassi è sostanzialmente la stessa:

*.user
*.suo
bin
obj
packages
*.nupkg
project.lock.json
project.assets.json

build.proj

Per questa demo, il processo di compilazione viene mantenuto piuttosto semplice. Si creerà un progetto MSBuild che compila tutte le soluzioni assicurandosi che i pacchetti vengano ripristinati prima di compilare le soluzioni.

Questo progetto includerà le tre destinazioni convenzionali Clean, Build e Rebuild, oltre a una nuova destinazione RestorePackages.

  • Le destinazioni Build e Rebuild dipendono entrambe da RestorePackages. Ciò assicura di poter eseguire sia Build che Rebuild, nonché di essere certi del ripristino dei pacchetti.
  • Clean, Build e Rebuild richiamano la destinazione MSBuild corrispondente in tutti i file di soluzione.
  • La destinazione RestorePackages richiama nuget.exe per ogni file di soluzione. Questa operazione viene eseguita tramite la funzionalità di invio in batch di MSBuild.

Il risultato è il seguente:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
            DefaultTargets="Build"
            xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
    <OutDir Condition=" '$(OutDir)'=='' ">$(MSBuildThisFileDirectory)bin\</OutDir>
    <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
    <SourceHome Condition=" '$(SourceHome)'=='' ">$(MSBuildThisFileDirectory)src\</SourceHome>
    <ToolsHome Condition=" '$(ToolsHome)'=='' ">$(MSBuildThisFileDirectory)tools\</ToolsHome>
    </PropertyGroup>

    <ItemGroup>
    <Solution Include="$(SourceHome)*.sln">
        <AdditionalProperties>OutDir=$(OutDir);Configuration=$(Configuration)</AdditionalProperties>
    </Solution>
    </ItemGroup>

    <Target Name="RestorePackages">
    <Exec Command="&quot;$(ToolsHome)NuGet\nuget.exe&quot; restore &quot;%(Solution.Identity)&quot;" />
    </Target>

    <Target Name="Clean">
    <MSBuild Targets="Clean"
                Projects="@(Solution)" />
    </Target>

    <Target Name="Build" DependsOnTargets="RestorePackages">
    <MSBuild Targets="Build"
                Projects="@(Solution)" />
    </Target>

    <Target Name="Rebuild" DependsOnTargets="RestorePackages">
    <MSBuild Targets="Rebuild"
                Projects="@(Solution)" />
    </Target>
</Project>

Configurazione di Team Build

Team Build offre vari modelli di processo. Per questa dimostrazione, si usa Team Foundation Service. Le installazioni locali di TFS saranno comunque molto simili.

Per Git e il controllo della versione di Team Foundation sono disponibili modelli diversi di Team Build, pertanto le operazioni seguenti varieranno in base al sistema di controllo della versione in uso. In entrambi i casi, è sufficiente selezionare il file build.proj come progetto da compilare.

Prima di tutto verrà esaminato il modello di processo per Git. Nel modello basato su Git, la compilazione viene selezionata tramite la proprietà Solution to build:

Build Process for git

Si noti che questa proprietà è un percorso nel repository. Dato che il file build.proj del progetto di esempio si trova nella radice, è stato usato semplicemente build.proj. Se si posizionasse il file di compilazione in una cartella denominata tools, il valore sarebbe tools\build.proj.

Nel modello del controllo della versione di Team Foundation, il progetto viene selezionato tramite la proprietà Projects:

Build Process for TFVC

A differenza del modello basato su Git, il controllo della versione di Team Foundation supporta le selezioni (il pulsante sul lato destro con tre puntini). Per evitare errori di digitazione, è quindi consigliabile usarle per selezionare il progetto.