Features of interaction between Bitbucket server and Teamcity
Today, almost every project has at least a basic infrastructure, which includes the project code hosting system (Github, Bitbucket, etc.) and its build system (Teamcity, Jenkins, etc.). We at CFT actively use Bitbucket server and Teamcity in our pipelines. Under the cut I will tell you how we set up the interaction of these services, what difficulties we encountered and how we dealt with them.
Typically, the interaction of the project’s code hosting systems and its build system is very simple: for each change in the project code that appears in the code hosting system, it must be assembled in the build system. That is, in our case, each push to Bitbucket should be built on Teamcity. To do this, you need to configure the VCS Roots configuration to read all changes in the repository:
And set up the Trigger of the configuration to start the build for any change in the repository:
Congratulations, your pipeline is ready! Now you and your team will always be aware that new changes in the project still allow it to be assembled. And everything is cool, as long as the number of developers in the project is small and changes are not submitted to Bitbucket every minute. But it happens that there are several dozen developers in a project, and work is going on simultaneously in the same number of branches. This load leads to a decrease in the performance of the build system.
There are two ways to solve this problem:
- buy a lot of build agents with impressive characteristics;
- optimize the pipeline.
It is clear that adding resources in order for changes to immediately receive their builds is a serious financial cost. This approach when expanding your team will look like this:
Therefore, it is worth considering the option of optimizing the pipeline. To do this, it is worth answering a couple of questions:
- can we write less code? Naturally not.
- can we collect the project not for every change? Maybe.
In a regular project pipeline, there is a main develop branch, into which developers periodically merge changes from working branches with features, bug fixes, technical debts, etc. Commits to working branches are often intermediate and do not need to be checked for a successful build status, so you can not run builds for such changes. Before the actual influence of the working branch into the main one, a pull request is usually created, which is checked by other developers. The very fact of creating a pull request means that the changes are ready to go to the main branch and you need to check their build status. Thus, you can save on resources and collect the project only for each pull request.
How to build a project only on pull requests?
In order to build the project only for changes in pull requests, we decided to use links in git to pull requests in Bitbucket. To do this, you need to change the VCS roots of the project build configuration to Teamcity:
Thus, we tell Teamcity to update information only from git links to the pool requests that Bitbucket creates.
The beauty! Now the build of the project starts only if changes have occurred in the branch for which the pull request was created. In Teamcity, the branch will have a name – the number of the pull request in Bitbucket.
And everything, it would seem, is good: the build agents are intact, the developers are fed up. But this did not last long …
From time to time, we began to notice that project builds for pull requests began to run with a delay, which could be up to several hours. Of course, this is an impermissible error that broke our entire pipeline. We noticed that Teamcity does not immediately see the pull requests created on Bitbucket. After checking the local git links, it turned out that the links to the pool requests appear with the same delay as on Teamcity. We decided to find out why Bitbucket is bad at updating git pull requests, and it turned out that we not the first faced such a problem.
In short, Bitbucket says that you shouldn’t use git links to pull requests, as this is their internal API.
We realized that the option with git links was no longer suitable for us, but we didn’t want to go back to the huge build queue, so we had to think again how to save the build system from heavy loading. We decided to dig towards the public Bitbucket server API…
In the public API, we found methods that can return a list of pool requests for different parameters. Including the commit id. And since Teamcity cannot find out from the git links about the presence of a pool request for the received commit, it can find out using the public API of the Bitbucket server.
That is, it is necessary to implement the following process in Teamcity:
Teamcity has a Dependencies mechanism that allows you to create chains of configurations and customize the strategy of behavior of configurations in these chains. This means that we can create a configuration that will receive absolutely all changes from Bitbucket and finish successfully if a pull request for such a commit exists, and fail if there is no pull request for such a commit.
We call this configuration CheckPR. Configured VCS roots to receive all changes from Bitbucket:
And we added one Build step of the Command Line type with a bash script with a request to the Bitbucket API, which receives all the pull requests for this commit and checks if there is an open one:
<![CDATA[#!/bin/bash
pr=$(curl -u "login:password" https://your.hostname.ru/rest/api/1.0/projects/PROJECT/repos/repository/commits/%build.vcs.number%/pull-requests)
if [[ $pr != *""state":"OPEN""* ]]; then
exit 1
fi]]>
It remains to configure the build configuration itself. We configure VCS roots and Trigger in the same way as at the beginning of the article, but add the setting of the Dependencies parameter:
We put a dependency on the CheckPR configuration and set the conditions for closing the configuration in case of an error in the CheckPR configuration. This will allow you to simply throw the configuration out of the queue and not start collecting it if there is no pull request for this commit. This is victory!
But you won’t believe …
Everything worked great, but not for all pool requests. Sometimes the Bitbucket API tricked us and said that there was no pull request for a commit, although there was one. Then we decided to change the approach using the same Bitbucket API.
Since the pool request really exists, we began to get a list of all pool requests and look for the commit in them, for which the launch of the Teamcity configuration worked. To do this, we simply changed the bash script in the Build Step of the CheckPR configuration:
#!/bin/bash
pr=$(curl -u "login:password" https://your.hostname.ru/rest/api/1.0/projects/PROJECT/repos/repository/pull-requests)
if [[ $pr != *""fromRef":{"id":"%teamcity.build.vcs.branch.TestVerification%","displayId":"%teamcity.build.branch%","latestCommit":"%build.vcs.number%""* ]]; then
exit 1
fi
This gave us a 100% result. Instead of building a project for each commit and spending 20-30 minutes of building an agent on building, we spend a maximum of 10 seconds to check if it is necessary to take these 20-30 minutes of building.