Migrating your builds to TeamCity

:right A few years ago, we migrated all our builds out of practical considerations from (at the time) TFS to JetBrains TeamCity. The main reason for us was that the support for XAML based builds in TFS was about to end... The main advantage for us, when using those XAML templates was the (rather) extensive templating support. It allowed you to specify a set of build steps, combined with some logic (if/else- & looping logic), that would execute based on a set of properties which could be set at design time of the "build definition" or when launching the build itself. I'm writing this post to give you more context for another post that I'm about to write.

What/Why?

Since we were already using TeamCity in another context and that this product also offers quite some templating support, this move to TeamCity was the one we made.

The migrated builds had to be based on a (tech specific) template that contained all the needed steps. To give you an idea:

  • import dependencies (Nuget restore),
  • Clean,
  • Build,
  • Test,
  • Package
  • and finally push

On template level, you can specify variables that are to be used in these steps and these variables/properties will be made available to the end user in a centralized location on build definition level. This allows the "end-user" of your build template to "define" a build definition with only a few variables and to get started really quick, without really having to understand how everything works. In small environments, this might be overkill, but in big corporate environments you typically do not want that "research has to be done to get started for a build of a new component". That is why those templates are really important!

Each build definition then in turn is "created" based on such a template This way of working (with templates) can potentially help you at some point in the future when you want (or need) to perform another migration.

Automation

At the time, I took care of the migration, set up the base templates and subsequently developed some tooling to migrate all the actively used builds (>1000) into the prepared templates in TeamCity. Given the amount of builds that needed to be migrated, this was a very smart thing to do.

In this post, I'm only going to write about the "TeamCity" part as that might still be useful in your migration scenario's. The XAML builds are out of support for a while now and I don't see a point in writing about that in this post.

So here we go:

Prerequisites

I typically chose to write my tools in c# (.Net Framework) and in this case that was not different. (You will most likely be able to reproduce this if you want to use another language/framework.) At the time, I did some research and I found that there was a Nuget package that provided with what I needed: TeamCitySharper. So start by creating a solution and (at least) by adding that reference.

Some expectation management: This package is not maintained in any way since 2017! I used it because it covered my needs and also because of the fact that there wasn't too much around otherwise. I recently used it again because I already had the code and it still covered my needs 😃

Apart from the code that you need to import the build information from your source build platform, you are now ready to go!

Connecting to TeamCity

When you start working on the TeamCity-related code, you first need to specify the import statements:

1using TeamCitySharper;
2using TeamCitySharper.DomainEntities;

then it's easy to set up a connection:

1var teamCityClient = new TeamCityClient("<your teamcity host url>");
2teamCityClient.Connect(<your username>, <your password>);

yes, I know that the security might be a bit 'basic', but since this is about a (one time) migration, I wouldn't focus too much on it. (There are ways to mitigate the risks)

Creating a build in TeamCity

In it's most basic form, creating a build definition which is based on a build, is done this way:

1var buildConfig = teamCityClient.BuildConfigs.CreateConfiguration(Projectname, "YourNewBuildName");
2teamCityClient.BuildConfigs.SetBuildTypeTemplate(
3    BuildTypeLocator.WithId(buildConfig.Id), 
4    BuildTypeLocator.WithName("TheNameOfYourteamCityTemplate")
5    );

This gives you your build, based on your name and template. What we still miss though, is the specification of the variables that are needed to execute the different steps in the build. An example of how you can set such a parameter, is shown here:

1teamCityClient.BuildConfigs.SetConfigurationParameter(BuildTypeLocator.WithId(buildConfig.Id), 
2    "<one of the property keys in the template>", 
3    "<the value that you most likely recovered from the original build>");

What's next?

So, now that you have created your first build config in TeamCity, you most likely want to make this part of your automation. Since this is most likely a for loop that processes the information from the source build system, I won't go to deep into this. But, what you want to ensure though, is that you can run this import over and over again, without having to go and cleanup those newly created builds over and over again. The way that I see it, there are two options:

  1. you check if a build config exists, before creating a new one and if it already exists, then you can either
    1. modify it (by using the SetConfigurationParameter that was shown earlier) so that it covers your needs.
    2. delete the build config and start over.
  2. clean everything up and completely start over
    1. useful in the preparation of a big bang migration
    2. useful when you do not yet have other build configs that are being used actively.

What I learned in the past, is that you will need 'early adopters' that help you test your work and because of that I strongly recommend to stick with one of the first main options. (You do not want to lose the support of your early adopters by throwing away their (new) working environment over and over again)

Whatever you choose, you will need a way to import the build information from your new build system. In the library that we used earlier, you have a way to do this. There is no real way to get a build based on it's name or (for example) based on a specific setting. It is because of this, that I found a way around this issue by retrieving all build configs:

1var allbuilds = teamCityClient.BuildConfigs.All();

At first sight, this works great, but the issue with this statement is that it doesn't retrieve everything that you need, only some very basic properties such as, project, Url, buildname, etc. If you want more, then you have to retrieve that info with another (more specific) operation:

1foreach (var config in allbuilds)
2    {
3        var buildconfig = teamCityClient.BuildConfigs.ByConfigurationId(config.Id);
4    }

And with this object, you can do more 'in depth' work, like modifying properties...

It's a bit 'brute force' if you ask me, but it does the job.

Conclusion

In this post, I showed you how to migrate builds into TeamCity. I wrote about this as I'm working on a "bigger" story and this post helps with the "setting of the scene". Stay tuned! (there might come some other "building blocks" first)

I hope you enjoyed it!

Tim

comments powered by Disqus