17

I have a webservice coded in kotlin and managed by gradle for the build/dependencies etc...

I want to do a simple thing: When someone call the version REST endpoint of my webservice, it returns the version.

Since gradle already handle the version with the version properties in build.gradle, I thought it would be easy to get this value but it seems it is not... I've been searching google for hours and everything seems so complicated.

Any idea how to handle the version the good way?

Here is what I have right now:

build.gradle

version = "1.0.0"
...

StatusService.kt

class StatusService : IStatusService {

    //TODO: Here I would like to get the version from the gradle 
    //file instead of maintaining 2 places for the version
    //Something like Gradle.getProperties("version") for example
    override fun getVersion() : String = "1.0.0"

}

I may be completely wrong about this so if you have another equivalent solution, I will accept it too! Thanks

3
  • Do you use gradle run or execute the resulting jar? Commented May 1, 2018 at 14:13
  • Why don't you simply write the version straight to the source code via gradle using filter(ReplaceTokens...)? Commented May 2, 2018 at 12:54
  • @xuesheng Never heard of this method. Feel free to add a possible answer below. Commented May 3, 2018 at 13:39

5 Answers 5

11

One approach to this is to output a version file as part of your Gradle build to a location in your classpath that is accessible as a resource within your application.

For example:

You can add this to your build.gradle file which will generate a properties file containing the project version number and add it to your applications source set so that it is available in the classpath.

def generatedVersionDir = "${buildDir}/generated-version"

sourceSets {
    main {
        output.dir(generatedVersionDir, builtBy: 'generateVersionProperties')
    }
}

task generateVersionProperties {
    doLast {
        def propertiesFile = file "$generatedVersionDir/version.properties"
        propertiesFile.parentFile.mkdirs()
        def properties = new Properties()
        properties.setProperty("version", rootProject.version.toString())
        propertiesFile.withWriter { properties.store(it, null) }
    }
}
processResources.dependsOn generateVersionProperties

Then in your code you will be able to access it by reading in the properties file:

class StatusService : IStatusService {
    private val versionProperties = Properties()

    init {
        versionProperties.load(this.javaClass.getResourceAsStream("/version.properties"))
    }

    override fun getVersion() : String = versionProperties.getProperty("version") ?: "no version"
}

Of course you might find it simpler and more appropriate to just output a String to the version file, and then read in the entire file as a String in your code instead of using a properties file.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the reply. I will try this!
The first section works perfectly, the file is generated in the build folder. Unfortunately, I'm not able to load it using this.javaClass.getResourceAsStream. Should it be generated in the build/resources folder instead? It always return null and the load crashes with a NullPointerException
Oh! Got it working! Just need to add / before version.properties in the getResourceAsStream function! Thanks!!!
@JKLy, Unfortunately, I'm not able to load too it using this.javaClass.getResourceAsStream. My load crashes with a NullPointerException. This is still works? Maybe I need to start this task "generateVersionProperties" somehow?
@FranMarzoa you should check that the version.properties is in the jar artifact. If it's not, then something in your build process isn't copying it in properly.
|
6

It took some time to figure out how translate the accepted answer to build.gradle.kts, but this works:

import java.io.FileOutputStream
import java.util.Properties

version = "your-version"

val generatedVersionDir = "$buildDir/generated-version"

sourceSets {
  main {
    kotlin {
      output.dir(generatedVersionDir)
    }
  }
}

tasks.register("generateVersionProperties") {
  doLast {
    val propertiesFile = file("$generatedVersionDir/version.properties")
    propertiesFile.parentFile.mkdirs()
    val properties = Properties()
    properties.setProperty("version", "$version")
    val out = FileOutputStream(propertiesFile)
    properties.store(out, null)
  }
}

tasks.named("processResources") {
  dependsOn("generateVersionProperties")
}

Comments

4

In modern build.gradle.kts it is rather simpler. In the build file all you need is to add a task:

tasks.withType<ProcessResources>() {
    doLast {
        val propertiesFile = file("$buildDir/resources/main/version.properties")
        propertiesFile.parentFile.mkdirs()
        val properties = Properties()
        properties.setProperty("version", rootProject.version.toString())
        propertiesFile.writer().use { properties.store(it, null) }
    }
}

Now you have it packed and can use it the way you want, I prefer a helper like:

object Config : LogTag("CONF") {

    private val versionProps by lazy {
        Properties().also {
            it.load(this.javaClass.getResourceAsStream("/version.properties"))
        }
    }

    val version by lazy {
        versionProps.getProperty("version") ?: "no version"
    }
}

1 Comment

rootProject.version.toString() resulted in unspecified, but using $version (from superdave's answer) worked for me.
1

Consider simple Kotlin Spring Boot example:

@SpringBootApplication
@RestController
class DemoApplication {
    @Value("\${version}")
    private lateinit var version: String

   @GetMapping("/version")
   fun version() = version
}

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

Since the property could be taken from application.properties we need to generate that file with required version. To do so we could create a template properties file with following content:

version=###appVersion

Now lets tell Gradle to copy that template to the resource folder and put the version instead of placeholder

import org.apache.tools.ant.filters.ReplaceTokens

task preprocessProperties(type: Copy) {
    from 'src/main/resources/templates'
    into 'src/main/resources'
    include 'template.properties'
    rename 'template.properties', 'application.properties'
    filter(ReplaceTokens, beginToken: '###', endToken: '', tokens: [appVersion: '1.0.0'])
}

compileKotlin.dependsOn preprocessProperties

And, voilà, every time you compile Kotlin sources you will get a property file with the version you need.

1 Comment

I used the essence of this approach for my Java based Spring Boot application and it worked great. In build.gradle I just defined a function to update the application's properties directly, then called it where it made sense for my workflow, which was building a docker image in my case. Here's the function I used for that: def setVersionProperty() { exec { commandLine 'sh', '-c', "sed -i.bak \"s/^version:.*\$/version: ${project.version}/\" src/main/resources/application.yml && rm src/main/resources/application.yml.bak" } }
0

Gradle is present only at the compilation, not at Runtime.

For doing this, you have to generate a class at the compilation time.
With Android project, the file BuildConfig.java is generated but I don't know if it's Gradle or Android.

For generating Kotlin code, you can check KotlinPoet.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.