Run Kotlin Code
As the Automation DSL is based on Kotlin, you can run any Kotlin code inside job steps. In case of a container, you should put your code inside the kotlinScript
block. For example:
job("Build and publish") {
container(displayName = "Run publish script", image = "gradle:6.1.1-jre11") {
kotlinScript { api ->
api.gradle("build")
try {
api.gradle("publish")
} catch (ex: Exception) {
println("Publishing failed")
}
}
}
}
How it works under the hood (with some simplification): Space compiles the script into a .jar file and runs it with a command like java -jar script.jar
. This means that the image you use to run the script must include JRE/JDK (version 11 or later).
The kotlinScript
block provides various helper APIs:
Working with build tools (currently, Gradle only).
Accessing build-related information and features: getting a Git branch, a script execution number, accessing the file share, and so on.
Accessing various Space modules (Chats, Documents, Issues, and so on) right from the Kotlin code. Learn more
Find the full list of APIs here.
In the example below we use api.gradlew()
to run commands using the Gradle wrapper and api.gitBranch()
to get the name of the current branch:
job("Example") {
container(image = "gradle:7.1-jre11", displayName = "Use helper APIs") {
kotlinScript { api ->
api.gradlew("build")
// run publish task for release branches
if (api.gitBranch().contains("release")) {
api.gradlew("publish")
}
}
}
}
Use Maven libraries
kotlinScript
lets you use functions from external packages. To do this, you should reference the required package with the @file:DependsOn("$package_name")
annotation. The $package_name
is the name of the package available on Maven Central.
For example, the following script uses an OkHttp client to get the content of example.com
and prints it to the job's log.
@file:DependsOn("com.squareup.okhttp:okhttp:2.7.4")
import com.squareup.okhttp.*
job("Get example.com") {
container(image = "amazoncorretto:17-alpine") {
kotlinScript {
val client = OkHttpClient()
val request = Request.Builder().url("http://example.com").build()
val response = client.newCall(request).execute()
println(response)
}
}
}
Use Kotlin to its full potential
One of the greatest things about Automation scripts is that they let you use full-featured Kotlin: in .space.kts
you can create classes, extension methods, use coroutines, and much more.
For example, the following job uploads multiple files (say, build artifacts) to an external server over HTTP. As this might take a lot of time, the job uses Kotlin coroutines to upload the files asynchronously.
The example uses a Ktor HTTP client. Learn more about sending HTTP requests.
@file:DependsOn("io.ktor:ktor-client-core:1.6.0",
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0",
"org.jetbrains.kotlinx:kotlinx-coroutines:0.19.2")
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*
import java.io.File
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
job("Use Kotlin coroutines") {
container(image = "amazoncorretto:17-alpine", displayName = "Upload files to external server") {
// get auth token from a project secret
env["TOKEN"] = "{{ project:test-secret }}"
kotlinScript {
val url = "https://external-service.url/upload"
val token = System.getenv("TOKEN")
// generate random text files
// in real life, this could be build artifacts
println("Generating files...")
val files = FileGenerator.getTxtFiles(10, 100000, "log")
// run a coroutine
val client = HttpClient()
coroutineScope {
println("Starting upload...")
files.map { file ->
launch {
val response = client.uploadFile(url, token, file)
println("Response from server: ${response.body<String>()}")
}
}.joinAll()
}
}
}
}
// upload a file to a remote server
suspend fun HttpClient.uploadFile(url: String, authToken: String, file: File): HttpResponse {
return this.post("$url/$authToken") {
headers {
append("Authorization", "Bearer $authToken")
}
setBody(MultiPartFormDataContent(formData {
this.appendInput(
key = file.name, headers = Headers.build {
append(HttpHeaders.ContentDisposition, "filename=${file.name}")
}, size = file.length()
) {
buildPacket {
writeFully(file.readBytes())
}
}
}))
}
}
// put all file generation activity in one place
object FileGenerator {
// create text files with random content
// these files emulate output of a build script
fun getTxtFiles(numberOfFiles: Int, length: Int, name: String): List<File> {
return (0..numberOfFiles)
.map {
val file = File("$name$it.txt")
file.writeText(getRandomString(length))
file
}
}
private fun getRandomString(length: Int) : String {
val chars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..length)
.map { chars.random() }
.joinToString("")
}
}
Last modified: 15 December 2023