Continuous Integration with the TeamCity Kotlin REST Client

Whichever CI server you use for development, being able to access it programmatically via REST API is an essential capability for implementing all kinds of automation. In this article, you’ll learn how to utilize the TeamCity REST API using the TeamCity Kotlin REST Client.

Let’s start by setting up a primary goal.

To help us achieve this goal, JetBrains provides a REST client (mentioned above). So all I have to do is write a simple Kotlin script that uses the REST client and implement whichever automation I want, right?

Well…in my case, I’m not a Java or Android developer and building Kotlin source code is totally new to me, so this article will cover some basics.

⚙️ Setup

I’m working on a Mac OS X machine, so I’ll start by installing the Gradle Build Tool.

brew install gradle

Next, I’ll create a new directory for my project as well as the main Kotlin file and a Gradle wrapper.

mkdir -p ~/projects/teamcity-rest-client
cd ~/projects/teamcity-rest-client

# Package structure and Kotlin file.
mkdir -p src/main/kotlin/client
touch src/main/kotlin/client/Client.kt

# Create Gradle wrapper.
gradle wrapper

From now on, I should be running all Gradle commands using the wrapper, e.g. ./gradlew build. The wrapper will be saved in the gradle/wrapper folder. This folder can be committed to source control, which removes the dependency on global Gradle installation and makes the project self-contained.

I want to use Kotlin to write build scripts, so I create a build.gradle.kts file in the project root with the following contents:

tasks.withType<Wrapper> {
    gradleVersion = "5.4.1"
}

This is to make sure I’m using an exact version of Gradle and it doesn’t auto-update.

🔨 Implement

This may sound unusual, but in this case I choose to write the Kotlin code first and then make it compile and run. Based on the example on the REST client project page, I came up with the following:

// Client.kt

// Default JVM name for this file will be "ClientKt".
// Tell JVM that I want it to be called just "Client".
@file:JvmName("Client")

// Name of the application package.
package client

import kotlin.system.exitProcess
// Import TeamCity REST client package.
import org.jetbrains.teamcity.rest.*

// Main entry point to my application.
fun main(args: Array<String>) {
    // Expect TeamCity URL, username, password and bbuild configuration identifier
    // provided as input arguments.
    if (args.count() < 4) {
        println("Usage: client TEAMCITY_URL USERNAME PASSWORD BUILD_ID")
        exitProcess(1)
    }

    // Authenticate and connect to TeamCity instance.
    val tc = TeamCityInstanceFactory.httpAuth(args[0], args[1], args[2])

    // Create build configuration identifier instance.
    val id = BuildConfigurationId(args[3])

    val build = tc.builds() // Get builds visible to the user.
        .fromConfiguration(id) // Get builds from build configuration with the specified identifier.
        .latest() // Get latest build information.

    println(build) // Print build information to stdout.
}

In a nutshell, this code connects to TeamCity using a username and password provided as input arguments, then fetches the latest build information for a specified build configuration and prints it out to the console. Check the inline comments for more details.

👷‍♂️ Build

After a lot of trial an error, I came up with the the following build file:

// build.gradle.kts

// Configure Gradle wrapper.
tasks.withType<Wrapper> {
    // Specify Gradle version for the wrapper.
    gradleVersion = "5.4.1"
}

// Use Kotlin DSL feature available in latest Gradle versions.
// https://docs.gradle.org/current/userguide/kotlin_dsl.html
plugins {
    // Building and application.
    application
    // Building a Kotlin application to run on JVM.
    kotlin("jvm") version "1.3.40"
}

// Configure application plugin.
application {
    // See @file:JvmName("Client") in src/main/kotlin/Client.kt
    mainClassName = "client.Client"
}

// Repositories to pull dependencies from.
repositories {
    mavenCentral() // To get Kotlin dependencies.
    jcenter() // To pull TeamCity REST client dependency.
}

// Configure dependencies.
dependencies {
    // Add TeamCity REST client dependency, version 1.7.26.
    compile("org.jetbrains.teamcity:teamcity-rest-client:1.7.26")
}

Check the inline comments for detailed information. This build script builds a Kotlin application targeted for running on top of JVM. The application depends on the TeamCity REST client.

It’s time to build it:

./gradlew build

🏃‍♀️ Run

I can now run the Kotlin application:

./gradlew run --args='https://myteamcity.com MyUsername MyPassword MyBuildConfigurationId'

After customizing input arguments, I’m able to see something like this in the logs:

Build{id=12345, buildConfigurationId=MyBuildConfigurationId, buildNumber=1234, status=SUCCESS, branch=BranchImpl(name=master, isDefault=true)}

That’s just the beginning! I can now use Kotlin, a nice modern programming language, to automate various CI/CD tasks on TeamCity.

For a full example project see this repository.

Fritz

Our team has been at the forefront of Artificial Intelligence and Machine Learning research for more than 15 years and we're using our collective intelligence to help others learn, understand and grow using these new technologies in ethical and sustainable ways.

Comments 0 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

wix banner square