Wednesday, 11 November 2015

Setting up feature branch deploys using Github, Teamcity and Octopus Deploy

Sooner or later, when you have a website in production, the question arises: how to develop new features without deploying partly built features along with bug fixes? Feature toggles might be the way to go. But that can be tricky to implement, and almost always needs code cleanup after the feature is released.

We decided to try and find a way where we could quickly deploy any given feature branch if we needed to. Since we mob program we mainly work directly in the master branch, but sometimes we do bigger features that need to be looked at or tested outside the team.

Really short version - the quirks

  • Custom builds can not trigger another build using the Finish Build Trigger in Teamcity.
  • Variables can be passed from Teamcity to Octopus Deploy only if they are set up as prompted variables in Octopus.
  • The prompted variables in Octopus Deploy have to be set up directly on the project, not in an included variable set.

Deploy process

What we wanted to achieve was being able to trigger a build manually in Teamcity, by stating:
  • Which branch to deploy.
  • Which config transformation file to use (stage, pre-production, production).
  • Which subdomain to deploy to.
This would then create a release and deploy to one of five preconfigured Octopus Deploy projects, which did the rest of the job by setting up the IIS and deploying the Nuget package published by Teamcity. The reason we wanted to have these five preconfigured projects was to have a dashboard available where we could easily see what feature branch was deployed where.

There are of course many other ways to solve feature branch deploys, but we wanted to have the manual step to choose what branch to deploy where and with what config.

Teamcity setup

First step was to set up a new build template in Teamcity. We have multiple projects and use build templates extensively to help us set up new builds quickly. The normal builds are quite basic:

Step 1 - Build

  1. Triggered by Github change to a certain VCS root.
  2. Runs Nuget restore.
  3. Runs Grunt tasks.
  4. Builds .Net sln.
  5. Runs tests.
  6. Copies minified files from grunt step into package.

Step 2 - Deploy

  1. Triggered by finishing build in step 1.
  2. Creates a Nuget spec.
  3. Packages the artifact from step 1 using the Nuget spec.
  4. Publishes the Nuget package to Octopus Nuget feed.
  5. Creates a release in Octopus Deploy and deploys it to stage environment. The Octopus plugin for Teamcity, which is simply a wrapper for octo.exe, works like a charm.
This seemed easy to convert into a feature build. Small changes in how to set up the Github VCS, how to decide which Octopus project to use and how to tell Octopus which config to use.

Set up custom build

To get the parameters in Teamcity that you require for each build, just set them up as required prompts. These can be configured as text fields, select lists etc. For our BranchToBuild we used text field, but for the Subdomain to deploy to and the ConfigTransformFile to use we added select lists.

Now, when we click the Run button for the build, we have to fill in those fields. Sweet!

One thing though! Custom builds in Teamcity can not trigger another build using the Finish Build Trigger. Our solution was to just merge the build and deploy steps into a single build. It took a few hours to discover why the trigger didn't work, and it hurt a bit to get a build consisting of 14 steps, but the show must go on, right?

Get the correct code from Github

In our normal builds we've configured the VCS root to look for changes in the master branch of a certain repo, or pull requests. In the feature branch we of course want the code to be fetched from the branch we input when we trigger the build. Easy thing, just open the VCS root and use the BranchToBuild parameter in the Default branch-field.

Naming of deploy Nuget package

This one we thought a bit about. We decided to go with a "Feature"-package id, instead of naming the packages according to their different application names like we normally do. The reason for this is that Octopus Deploy needs to be set up to look for a certain Nuget package id. By naming all the packages "Feature" we could set up five identical and application agnostic Octopus Deploy projects.

Creating an Octopus Deploy release

We want to deploy our feature to the Octopus Project that corresponds to the subdomain we chose in the first step. The Octopus projects are named Feature F1 to Feature F5 so we just use the parameter Subdomain to set the correct project. As release number we use the parameter DeployBuildNumber which is simply the concatenated build counter, product and branch name, e g '1.0.9-Product-Testbranch'.

When creating a release, Octopus Deploy automatically looks for the Nuget package with the highest release number. This is not what we want here though. Many builds use the same "Feature"-package id and the release number created might not be the highest one, since the build counter will be different for each build. Luckily, the Octopus plugin gives us a possibility to use the command line options of octo.exe. We have two options we need:
  • --packageversion %DeployBuildNumber% tells Octopus to use the specified release of the package instead of the default one.
  • --variable=ConfigTransformationFile:%ConfigTransformationFile% sets the variable ConfigTransformationFile in Octopus to the specified value, which is the config file stated in our input parameters.
When calling octo.exe to create a release, it's possible to send along variables like above. Important to know is that this can only be done if the variable is set up as a prompted variable in Octopus Deploy.

Octopus Deploy

In Octopus, we normally use a rolling deploy, taking the machines out of the load one by one, configuring IIS, setting up firewall rules, deploying, smoke testing and putting them back in the load if everything succeeds. Now, all we want to do is deploy the given "Feature"-package to a certain IIS website using the given transformation file.

We make extensive use of Step templates to create the building bricks for our deploy processes, it's a brilliant feature. Especially in this case where we want to be able to easily add new Feature deploys.

Deploy process setup

Our set up in Octopus Deploy is one project for each available feature deploy subdomain, F1 to F5. This could be changed to just using one and dynamically setting the hostname and IIS to deploy to, but we wanted to have a dashboard in Octopus where we could see what feature is currently deployed where.

Doing it like this is a fairly simple process. The release gets created in the project belonging to the subdomain chosen in Teamcity. The only thing that actually has to be handled dynamically is receiving the ConfigTransformationFile-variable to decide which config should be deployed.

To be able to receive a variable through octo.exe, two things are necessary:
  • The variable has to be set up as a prompted variable.
  • The variable has to be set up directly in the project, it will not work if it's in an included variable set.
Once the variable has been set up, it's ready to use in the Deploy Nuget Package-step of your process.

1 comment: