I did not found any suitable tool that can be used to load dependencies when building .NET project in a TeamCity environment (using NAnt scripts).
Until now all dependencies where stored in VCS together with the source, not so sophisticated.
All our TeamCity projects contains one SDK-configuration that is a zipped file with all output assemblies and executables.
The TeamCity offers URL patterns to access build artifacts. For example:
These two facts trigged me to build a custom NAnt task.
It simply downloads SDK-artifacts from the TeamCity repository, and unpacks the assemblies in the current build environment before the build is started.
I added a “pom”-file to all TeamCity configuration projects that defines the dependencies:
<project> <dependencyManagement> <repositories> <repository>http://teamcity.mycompany.com/guestAuth/repository/download/</repository> </repositories> <dependencies> <dependency> <groupId>AGroup</groupId> <artifactId>AProduct</artifactId> <version>1.1.124.0</version> </dependency> <dependency> <groupId>AGroup</groupId> <artifactId>BProduct</artifactId> <version>5.4.42.0</version> </dependency> <dependency> <groupId>BGroup</groupId> <artifactId>CProduct</artifactId> <version>2.5.24.0</version> </dependency> </dependencies> </dependencyManagement> </project>
The NAnt task is executed in the build file:
<target name="loadDependencies"> <loaddependencies filename="${Build.Base}\dependencies.xml" target="${Build.Base}\Dependencies"/> </target>
And here is the source if someone is interested:
using System; using System.Collections.Generic; namespace MyNAnt.Build.Tasks { using System.IO; using System.Net; using System.Xml; using global::NAnt.Core; using global::NAnt.Core.Attributes; using ICSharpCode.SharpZipLib.Zip; [TaskName("loaddependencies")] public class LoadDependenciesTask : Task { // NAnt parameters [TaskAttribute("filename", Required = true)] [StringValidator(AllowEmpty = false)] public string DependencyFile { get; set; } [TaskAttribute("target", Required = true)] [StringValidator(AllowEmpty = false)] public string Target { get; set; } // Lokal parameters private List<Dependency> Dependencies { get; set; } private List<string> Repositories { get; set; } /// <summary> /// Executes the NAnt task /// </summary> protected override void ExecuteTask() { // load all dependency information LoadDependencies(); // download dependencies from TeamCity foreach (var dependency in Dependencies) { DownloadArtifact(Repositories, dependency.Group, dependency.Name, dependency.Version, Target); } } /// <summary> /// Reads the dependency list from configuration file /// </summary> private void LoadDependencies() { var doc = new XmlDocument(); Log(Level.Info, "Loading dependencies from '{0}'.", DependencyFile); doc.Load(DependencyFile); // load all repositories to search for assemblies var repList = new List<string>(); var repElemList = doc.GetElementsByTagName("repository"); foreach (XmlNode repository in repElemList) { repList.Add(repository.InnerText); } Repositories = repList; // load all dependency assemblies var depList = new List<Dependency>(); var depElemList = doc.GetElementsByTagName("dependency"); foreach (XmlNode dependency in depElemList) { var item = new Dependency(); if (dependency != null) { item.Group = dependency["groupId"].InnerText; item.Name = dependency["artifactId"].InnerText; item.Version = dependency["version"].InnerText; } depList.Add(item); } Dependencies = depList; } /// <summary> /// Downloads and unzip artifact from TeamCity repository /// </summary> /// <param name="repositoryList"></param> /// <param name="group"></param> /// <param name="name"></param> /// <param name="version"></param> /// <param name="destination"></param> private void DownloadArtifact(List<string> repositoryList, string group, string name, string version, string destination) { Log(Level.Info, "Destination folder: '{0}'.", destination); // create destination folder if it not exist Directory.CreateDirectory(destination); foreach (var repository in repositoryList) { // URL-example // http://teamcity.mycompany.com/guestAuth/repository/download/AGroup::AProduct/1.1.124/SDK/AProduct_SDK-1.1.124.zip var address = repository + group + "::" + name + "/" + version + "/SDK/" + name + "_SDK-" + version + ".zip"; using (var wc = new WebClient()) { try { using (var streamRemote = wc.OpenRead(new Uri(address))) { Log(Level.Info, "Found artifact '{0}'.", address); var zis = new ZipInputStream(streamRemote); ZipEntry ze; while ((ze = zis.GetNextEntry()) != null) { if (ze.IsDirectory) { Directory.CreateDirectory(ze.Name); } else { var buffer = new byte[2048]; var fileName = Path.GetFileName(ze.Name); Log(Level.Info, "Unzipping '{0}'.", fileName); using ( Stream outstream = new FileStream( destination + "\\" + fileName, FileMode.Create)) { while (true) { var bytes = zis.Read(buffer, 0, 2048); if (bytes > 0) outstream.Write(buffer, 0, bytes); else break; } } } } return; } } catch (Exception) { // ignore exceptions } } } Log(Level.Error, "ERROR: Artifact '{0}::{1}' with version '{2}' not found!.", group, name, version); } } }