We are developing a large modular UI application consisting of a large number of plug-ins with different release cycles. All code resides in one repository, so a QA-specialist constantly comes to the developers and asks: “And what component has changed? What version to lay out to check the task? ". The question turned out to be relevant not only on the UI (C #), but also on the backend (Java). After our rash promises to write everything with pens, I suggested automatically generating the necessary list based on the changed files at the time of the merge pull-request. In this article we will describe how we organized this by extending the functionality of assemblies on TeamCity (TC) without administrator rights on the server and installing external plug-ins.
Conditions of the problem
First, let's define what our tool should be:
- If the build is successful, add the build number to JIRA and specify additional information in the comments (in which branch they repaired, which components to deploy, etc.)
- In case of unsuccessful assembly, send a letter to specific people with a specific template and description of the problem.
Here is what it might look like:
')

Now we list the implementation requirements:
- Some nontrivial code (logic) after assembly.
- Using Windows and Linux agents.
- Nothing non-standard on agents.
- Easy replicability on build configurations.
Choosing a solution
Because of the first item in the requirements above, there is no option using Command Line Build Step s in TC, because:
- Difficult logic to write on this is difficult (although possible);
- Specialists who know bash / cmd / powershell are few;
- It is not versioned;
- The same code on Windows and Linux agents cannot be run.
Therefore, we decided to write a maven-plugin that would be launched by the maven-runner. The collected version was saved in a binary storage, from where it was downloaded right during the build process. It was possible each time to assemble it directly during the build, but this would significantly increase the build time, and in addition, create additional dependency on a separate VCS-root, which would lead to unnecessary builds.
Alternatively, you can make a NuGet-package with the desired exe-file, download it from a private NuGet-server and run it. Although in our case, due to Linux servers, the option with .NET programs was not available, but in another project, this approach showed itself well.
Now about replication on a large number of build-configurations. There are two ways:
- Build template. From it you can inherit our assembly, and thus change in one place the various parameters / steps / properties.
- Meta Runner. Mimics under runner, does not appear in the assembly.
Build template in our case is not very suitable, since the assemblies in which we want to use it are inherited from their build template. Also in this case, many irrelevant details appear in the steps / properties.
Thus, the possible technical implementation looks like this:
- A separate repository is created, where the code of our plugin is stored.
- The plugin is assembled in a separate build and laid out in the storage of binary artifacts (Nexus or Artifactory).
- For target assemblies in some creative way, run this plugin.
- All this we make out as a meta-runner.
The simplest maven-plugin
Let's start by creating a simple maven-plugin called 'com.db.meteor: jira-commenter'. To do this, inherit AbstractMojo, like this:
@Mojo( name = "stampJira", requiresProject = false) public class MainMojo extends AbstractMojo { @Parameter( property = "jiraCommenter.branch") public String branchName; public void execute() throws MojoExecutionException{ getLog().info(branchName); } }
Here is an example of passing a parameter from mvn. Also in our pom.xml we specify how to compile it using maven-plugin-plugin:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <version>3.2</version> <configuration> <skipErrorNoDescriptorsFound</b>>true</<b>skipErrorNoDescriptorsFound> </configuration> <executions> <execution> <id>mojo-descriptor</id> <goals> <goal>descriptor</goal> </goals> </execution> </executions> </plugin>
Plugin ready. Now we will save the changes to our VCS and form a separate assembly that will make mvn: deploy. It's easy: you need a single build step.

Now we have a separate build that will build and upload our maven-plugin to the local nexus (or artifactory, depending on what you use for binary artifacts).
Running on TeamCity
Now run this plugin on TeamCity to make sure that we are going in the right direction. To do this, we will make a separate assembly, in which we will not connect any VCS-root and add a single step - launch maven, in which we specify our plugin in the goals and pass our parameter:

So, when maven starts, it will download our plugin from the right place, put it into the local cache and launch the specified mojo (in our case, stampJira).
In Maven 2, it was still possible to specify LATEST as a version, but from the third version you have to specify the exact version. By running the build, make sure that our plugin is downloaded and executed:

Passing parameters via maven is inconvenient, but, fortunately, there is an alternative. TeamCity has a REST API. The REST API itself is quite powerful and allows you to learn almost everything about assemblies, agents, etc. I will not dwell on it in detail, there is
excellent detailed documentation for this.
I used to get information about the current status of the build and about problems that have arisen (tests / something else / build output). During assembly work, its status will be RUNNING or FAILING, based on this we will determine whether the build is successful or not.
To access the API, you need a login and password. Do not use your own, because when starting the assembly, a one-time login and password are generated, which are available through the teamcity parameter:

You will also need the build id, which will be passed in the same way.
Now it's worth integrating our plugin with Jira to go there and update the necessary tasks. We will not elaborate here, since this is somewhat beyond the scope of the article. We used the com.atlassian.jira library for this: jira-rest-java-client, on stackoverflow many examples of its use.
Extract meta-runner
I will show with an example how meta-runner is created. First you need to find the rarely used menu item and come up with a name for the new meta-runner.

After extraction, we will be on the tab with the project meta-runners. There you can edit and update it, if necessary:

Technically, the meta-runner is an xml description of the step (s) to be executed, and it is available just like any other build step. I, for example, turned out this:
<?xml version="1.0" encoding="UTF-8"?> <meta-runner name="Temporary for article"> <description>Temporary for article</description> <settings> <parameters> <param name="maven.security" value="%teamcity.agent.home.dir%/.m2/settings-security.xml" spec="text validationMode='any' display='hidden'" /> <param name="teamcity.build.branch" value="master" /> </parameters> <build-runners> <runner name="Launch Jira commenter." type="Maven2"> <parameters> <param name="goals" value="com.db.meteor.tools:jira-commenter:master-1.0.0.33:stampJira" /> <param name="maven.home" value="" /> <param name="mavenSelection" value="mavenSelection:default" /> <param name="runnerArgs" value="-DjiraCommenter.branch=%teamcity.build.branch%" /> <param name="teamcity.coverage.emma.include.source" value="true" /> <param name="teamcity.coverage.emma.instr.parameters" value="-ix -*Test*" /> <param name="teamcity.coverage.idea.includePatterns" value="*" /> <param name="teamcity.coverage.jacoco.patterns" value="+:*" /> <param name="teamcity.step.mode" value="default" /> <param name="userSettingsPath" value="%teamcity.agent.home.dir%/.m2/settings.xml" /> <param name="userSettingsSelection" value="userSettingsSelection:byPath" /> </parameters> </runner> </build-runners> <requirements /> </settings> </meta-runner>
If you wish, you can write this XML yourself, but it is easier to get the initial one from the assembly. You can edit it right here by changing the XML. We, for example, change the build version this way.
Now it is possible to add a new buildstep in any build in the current project:

Moreover, the parameters that were used in the test build are now available for filling in our build step, for example, teamcity.build.branch:

Great, now we can ask our colleagues and all those interested to add this step to our assembly and enjoy life.
Note: after extracting meta-runner-a, it does not synchronize with the project from which it was extracted. So if you want to change something, change it in it.Run always?
So now with a successful build our code will be executed. And if the assembly failed? In advanced options, there is a useful option: "when to perform this step":

Here a suitable execution condition is selected. For the current task, we set “Even if some of the previous steps failed”.
This is how we managed to make our QA-engineers happier - now after each merge request in the master or release branches, a detailed comment is written to the corresponding task in JIRA about what has changed and what to deploy for testing.