CI/CD processes update. Step 1: Planning and Preparations

CI/CD processes update. Step 1: Planning and Preparations

Content of the article

In 2020, it was almost impossible to find a project without IaC, microservices, blockchain, VR, and other stuff  from the description, as well as without AWS, Azure, or GCloud mentioned in the tech stack. And it's really cool! Progress does not standstill. We grow, so our projects do, here come more comfortable and useful tools for solving modern problems.

It could be an introduction to this article, but I've got several talks with colleagues, took a look at my projects, and understood my misconception. There are lots of projects with a relic tech stack that also require maintenance.

For some reason, tech stack can’t be updated, so we have to support an outdated project. It's even harder when such a project is under development. Thus, we have a snowball effect here: new features are required, the code has to be delivered, servers are screaming for some care, and then Bitbucket doesn't support Mercurial anymore. The next case is exactly as described.

This case study includes a load of solutions and decisions like: Mercurial-Git conversion, migration of CI from CruiseControl.NET to TeamCity, and from git-deployment to Octopus. Of course, with an excessive description of the whole process, so there should be more than one article.


So, for this project, we got a Mercurial repository, over 300 opened branches, CCNet, one more CCNet with Git for deploying needs, and a load of modules with their own configurations and separate clients. Here also were four environments, over five hundred domains, other stuff, and active development, as a cherry on top of the cake. Of course, it wasn't the only project I'm working on, thus it was important not to spread yourself too thin.

Understanding that I have to pay attention to other tasks and projects, I've spent lots of time discovering an existing infrastructure to avoid unresolvable issues.

One of the most interesting features of this project is some number of modules with common core but different configurations. Also, there are some unique modules for resellers and enterprise customers. One module for one client,where clients are organizations or persons accessing the exact module identified by the domain name. Every client has access to a personal domain with a unique design and some individual settings.

The overall project’s scheme looks like

Ft1pjp 5Mnyfk2fu07salwryrew[1]

It can be seen, the core remains the same, which could be useful.

There are reasons to review and update CI/CD processes:

  1. CruiseControl.NET as a build system, which is not the best tool at all due to many reasons.
  2. There is a necessity to limit the possibility of editing configs without blocking access to the server for some developers, mostly in leading roles. The best way to do it is to establish RBAC (role-based access control) for the CI system.
  3. CCNet was also used as a deployment system but hosted on the client-side and bound with local git. With such conditions, we got build-and-delivery process like on the picture:Hcykkvp6zijbax7ykugimj3qwkq[1]
  4. I've got empirical evidence that the maintenance of this system requires too much time and resources
  5. Build configs were stored on the shared server with some other projects, so as the project grew, we decided to place it on the customer's facilities and support it with dedicated services.
  6. We were lacking a sort of dashboard with an easy, centralized and comprehensive display of the current state.
  7. The process of code assembly and delivery was outdated with non-optimal resource utilization.

At the planning stage, we decided to use Teamcity as a build system and Octopus for deployment. Customer's hardware infrastructure remains the same: dedicated servers for dev, test, staging and production environments, as well as reseller's servers, mainly production.

As a Proof of Concept, we presented to the customer one of the modules with a detailed roadmap and performed preparations, like installation and setup. It's not a big deal to describe in detail, you can always google how to install Teamcity, so I'll focus at the new system's requirements:

  1. Easy support with all the stuff: backups, updates, troubleshooting.
  2. Versatility, in the best case, must be realized as a template for modules assembling and delivery.
  3. Decreased time spent on adding new build configs and maintenance because clients are added and removed, sometimes new setups are required, and we can't afford a delay in delivery.

Around this moment, we've recalled that BitBucket doesn't support Mercurial repositories anymore, and added a requirement to migrate on git with remaining branches and commits history.

Preparation: Repository Conversion

It would seem that somebody already solved this problem before, so we have just found the most suitable solution. But, it wasn't so easy! Fast export was not so fast, in fact, and didn't do its job. Bitbucket could provide its own converter, but it doesn't. Several more solutions, found in Google guides, also failed. So, I decided to create my own toolset, which should be even more useful because we have other Mercurial repositories. Here I'll describe how it works.

  1. As the basis, we take Mercurial HgGit plugin
  2. Receive all the branches of the Mercurial repository 
  3. Transforming branches names - many thanks to Mercurial and developers for spaces in branches names! And one more for umlauts and other special symbols, which made my job even better!
  4. Creating HG bookmarks and pushing them to the intermediate repository, because mercurial does not allow creating bookmarks with titles similar to the branches' names (eg. staging), and Bitbucket can’t optimize repositories on-the-fly.
  5. Removing postfix from the branch's name in the new Git repository and migrating hgignore to gitignore
  6. Pushing to the general repository

Here are the commands step by step


$ cd /path/to/hg/repo
$ cat << EOF >> ./.hg/hgrc


$ hg branches > ../branches


#!/usr/bin/env bash
sed -i 's/:.*//g' ${hgBranchList}
symbolsToDelete=$(awk '{print $NF}' FS=" " ${hgBranchList} > sym_to_del.tmp)
while read hgBranch; do
  i=$((i 1))
done <${hgBranchList}
while read str; do
  i=$((i 1))
done < ./sym_to_del.tmp
for i in ${!strToDel[@]}
  echo ${hgBranches[$i]} | sed "s/${strToDel[$i]}//" >> result.tmp
sed -i 's/[ \t]*$//' result.tmp
sed 's/^/"/g' result.tmp > branches_hg
sed -i 's/$/"/g' branches_hg
sed 's/ /-/g' result.tmp > branches_git
sed -i 's/-\/-/\//g' branches_git
sed -i 's/-\//\//g' branches_git
sed -i 's/\/-/\//g' branches_git
sed -i 's/---/-/g' branches_git
sed -i 's/--/-/g' branches_git
rm sym_to_del.tmp
rm result.tmp


  • DevOps
Back to Articles


Popular articles

Contact Us