Deploying to Multiple Targets With Ease

Have you ever been faced with a situation where you needed to deploy your system to many different environments? For many of us, this is probably just staging or production. When using TeamCity to do this, we just create a build chain as follows:

But what if the number of deployment targets is higher, like ten or more? 

In this case, we might end up with a build chain ending with a set of Publish to env* deployment build configurations:

In such cases, it’s always better to have a combining build configuration that has dependencies on all the Publish tasks:

Unfortunately, due to the current limitations of TeamCity, this combined Publish build configuration can’t be both composite and deployment at the same time. We’ve already had this feature requested by some users.

In this case, the build won’t be shown as running if any of the Publish to env* dependencies are still running, which is a bit inconvenient.

Maintaining the configuration

If we’re using the Kotlin DSL, we can program such a build chain relatively easily, as we can just create a loop that will generate all these Publish to env* build configurations. You can see an example of similar Kotlin DSL code in one of our previous posts.

If we’re not using the Kotlin DSL, then we have to create the build configurations via the UI. Usually, in such cases, creating a template first would be quite helpful. Maintaining this type of deployment configuration is much simpler with templates. However, even with a template, it still takes a lot of clicks to change anything in the configuration.

Moreover, whenever we add a new environment, we still need to create a new build configuration and add a dependency to it from the Publish build configuration. 

All of this feels cumbersome, especially if the number of deployment targets is quite large. Is there really no better way?

Using Matrix builds

If we’ve already defined a template for the actual Publish to env* build configurations, then we’ve already generalized our publishing scripts such that they now accept a parameter denoting a particular environment and perform publishing based on that. In this case, a much better approach would be to use the Matrix Build feature.

To do this, we can add a Matrix Build feature to the Publish build configuration with a matrix parameter with the desired name and an array of values:

The Publish build configuration won’t need dependencies on Publish to env* build configurations anymore. Instead, it will have a dependency on Test. The deployment steps will be copied from a Publish to env* template. 

In regards to the configuration of the dependencies, the resulting build chain will look the same as in the beginning:

However, if we trigger a Publish build configuration, its build will be transformed into a composite build with automatically generated dependencies on a set of parallel [env*] tasks, one for each value of the matrix parameter:

If you look closer at this screenshot, you can see that the Publish build is shown as running even though its dependencies are not finished yet. So, apparently a build can be both composite and deployment at the same time. This looks like a nice solution, at least for some of the users who voted for the composite deployment build feature ticket.

With the Matrix Build feature, the settings become much simpler. Now, there’s no need to have a template or a dedicated build configuration for each environment. 

At the same time, adding a new environment is as simple as adding a new value to the matrix parameter (provided that our scripts understand this value). 

The presentation of the results looks nicer too:

Matrix Builds also allow us to retain flexibility. For instance, if an [env*] build fails, we can retry this individual build or better yet, we can rerun the Publish build itself with the Rebuild failed dependencies option:

If our publish build requires an artifact prepared by the Build package task, we can just add an artifact dependency to the Publish build itself. When it is executed, the same dependency will be copied to the auto-generated [env*] builds so they all use the same artifact.

Finally, these [env*] build configurations won’t clutter our view as they won’t be visible by default. If necessary, one can always navigate to them and analyze their history.

Once again, the Matrix Build feature proves to be quite useful. If you’re using TeamCity 2023.11+, please give it a try and tell us how it can be improved!

image description