4

I wish to access the build information from inside my java project which uses gradle to build the project. The information I need to access is the build number generated by teamcity, build vcs number etc. These are easily accessed by maven and using the maven-replacer-plugin these can be replaced in a properties file. Firstly, is there an alternative in gradle to achieve the same? And then, how to do it? :)

I tried the approach shared here, but I always get null as the build number.

In maven I placed a properties file (project.properties) in the project to hold key value pairs with keys being those project information like

project.groupId, project.artifactId, project.version, maven.build.timestamp

I wish to access the same build information using gradle. . The pom would look like below:

                    <!-- Write project version number to file -->
                <plugin>
                    <groupId>com.google.code.maven-replacer-plugin</groupId>
                    <artifactId>maven-replacer-plugin</artifactId>
                    <version>1.3.8</version>
                    <executions>
                        <execution>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>replace</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <includes>
                            <!-- replace the token in this file -->
                            <include>target/classes/project.properties</include>
                        </includes>
                        <replacements>
                            <replacement>
                                <token>PROJECT_GROUP</token>
                                <value>${project.groupId}</value>
                            </replacement>
                            <replacement>
                                <token>PROJECT_ARTIFACT</token>
                                <value>${project.artifactId}</value>
                            </replacement>
                            <replacement>
                                <token>PROJECT_VERSION</token>
                                <value>${project.version}</value>
                            </replacement>
                            <replacement>
                                <token>PROJECT_BUILD_DATE</token>
                                <value>${maven.build.timestamp}</value>
                            </replacement>
                            <replacement>
                                <token>BUILD_NUMBER</token>
                                <value>${build.number}</value>
                            </replacement>
                        </replacements>
                    </configuration>
                </plugin>

What the maven replacer plugin does is replace the values in the properties object, that can be used in the java application.

I need to access the same properties using gradle.

Team City configuration in the pom is below:

<ciManagement>
    <system>Team City</system>
    <url>http://teamcity.mycompany.com/teamcity/project.html?projectId=bt007
    </url>
</ciManagement>

In the build.gradle file, I have a task defined as below:

task getProjectGroup << {
    ext.projectGroup = System.getProperty("project.group")
    println "Project Group: $projectGroup"
  }

I get the following output when executing this task:

:service:getProjectGroup
Project Group: null

BUILD SUCCESSFUL
Community
  • 1
  • 1
Aspirant
  • 1,934
  • 4
  • 25
  • 44
  • If you get `null`, your TeamCity build isn't setting this system property, and you wouldn't be able to access it from Maven either. Substituting placeholders in a properties file is a matter of configuring the `processResources` task (see `Copy` in the [Gradle Build Language Reference](http://gradle.org/docs/current/dsl/index.html)). – Peter Niederwieser May 15 '14 at 13:32
  • I can't spot any TeamCity property in the Maven POM above. – Peter Niederwieser May 15 '14 at 13:34
  • Updated above. Also added the team city configuration to make the question complete. – Aspirant May 15 '14 at 13:39
  • With the exception of `build.number`, couldn't you get all those values from Gradle directly? E.g. `project.group`, `project.archivesBaseName`, and `project.version` (and `new Date()` for build date) are all directly accessible in your Gradle script. – superEb May 15 '14 at 13:57
  • I get the `new Date()` object for the build date. All except that are null. I updated the gradle task in the original question. – Aspirant May 15 '14 at 14:31

1 Answers1

1

I do something very similar on my project - packaging up build info into a properties file that can be read from the classpath in your Java application.

Here's how I accomplish that in a Gradle script:

tasks.withType(Jar).all { Jar jar ->
    jar.doFirst {
        String jarType = jar.extension
        if(jar.classifier) jarType = jar.classifier
        String fileName = "project.properties"
        ant.propertyfile(file: "${jar.temporaryDir}/${fileName}") {
            entry(key: "PROJECT_GROUP", value: project.group)
            entry(key: "PROJECT_ARTIFACT", value: project.archivesBaseName)
            entry(key: "PROJECT_VERSION", value: project.version)
            entry(key: "PROJECT_BUILD_DATE", value: new Date())
            entry(key: "BUILD_NUMBER", value: hasProperty("teamcity") ? teamcity["build.number"] : "local")
        }
        String intoPath = "your/package/name/here/${project.name}/${jarType}"
        jar.from(jar.temporaryDir) {
            include fileName
            if(jar instanceof War) intoPath = "WEB-INF/classes/${intoPath}"
            into(intoPath)
        }
        println "\tCreated ${intoPath}/${fileName}"
    }
}

Here, I'm adding functionality to every Jar task (including War) that creates a properties file for that archive and includes it on the classpath under your/package/name/here/${project.name}/${jarType}/project.properties.

This is the beauty of Gradle. It makes customizations like this very simple - no plugin required.

Then, to read the properties in my app, I inject or hardcode the expected path to the file and load the properties like this:

public Properties lookupClassPathResource(String pathToResource) {
    Properties p = null;
    org.springframework.core.io.Resource r = new org.springframework.core.io.ClassPathResource(pathToResource);
    if(r.exists()) {
        try {
            p = org.springframework.core.io.support.PropertiesLoaderUtils.loadProperties(r);
        } catch (IOException e) {
            //log or wrap/rethrow exception
        }
    }
    return p;
}

And fun times are had by all!

EDIT: Apparently the question/answer linked in the OP (about accessing build.number) is a little out-of-date. Instead of doing System.getProperty("build.number"), you can use the teamcity properties that TeamCity implicitly adds to your project via a Gradle init script. The code above has been modified to reflect this. Also see this question for other examples.

Community
  • 1
  • 1
superEb
  • 5,613
  • 35
  • 38
  • Its a good approach. I will work on it and keep you posted. Thanks. – Aspirant May 15 '14 at 14:41
  • I still get the values as `null` in the properties file after that task is completed, i.e. after doing a `build`. – Aspirant May 16 '14 at 12:52
  • That doesn't make sense - even if you didn't explicitly set the built-in project properties, they would have non-null default values. Are you still trying to pull System properties instead of using `project`? Try adding this line to your build.gradle file and then run `gradlew x` from the command line and post your results: `task x << {println "$project.group $project.version"}`. At the very least it should print ` unspecified`. – superEb May 16 '14 at 14:59
  • Yes. I do see `unspecified` for some properties. `project.version` is `unspecified`. `build.vcs.number` & `build.number` are 'null'. – Aspirant May 16 '14 at 18:08
  • Ok, so we've established that you need to be setting `project.group` and `project.version` in your Gradle script, just as you would in your Maven POM. The `build.number` is a System property defined by TeamCity, so it's only going to be available when your Gradle script is run by a TeamCity agent. Does that not work? It seems you could pretty much just copy-paste my code from above and you'd have almost exactly what you want. – superEb May 16 '14 at 19:04
  • `build.number` and `build.vcs.number` still are null after the gradle script is run by team city. Not sure what I am missing here. `project.version` is also unspecified whereas it should be the value mentioned in the gradle script. – Aspirant May 19 '14 at 14:10
  • I use the exact code as above. The file is created properly once the gradle script is run by team city. `project.group` and `project.archivesBaseName` are populated fine. But the rest are either `unspecified` or `null`. – Aspirant May 19 '14 at 14:42
  • 1
    **1:** For the build.number, instead of doing `System.getProperty`, try `project.teamcity["build.number"]` instead. This works assuming you're using the "Gradle" runner for your build step (which applies a TeamCity-defined init script), but note that it won't be available in local builds - [see this answer for more on that](http://stackoverflow.com/a/23580666/1174467). **2:** The only reason `project.version` is `unspecified` is because *you haven't defined it in your Gradle script*. You should be setting this value, just as you would set the project `version` string in a POM. – superEb May 19 '14 at 15:41
  • Amazing. It works with `project.teamcity["build.number"]`. Any thoughts why it was not working with the `System.getProperty` approach? – Aspirant May 19 '14 at 16:07
  • Probably because the JetBrains team decided to expose it in a different, more-integrated way for Gradle builds. If this answer helped you, I would appreciate an up-vote and/or an "accepted" answer. – superEb May 19 '14 at 17:26
  • Appreciate all your help. – Aspirant May 19 '14 at 17:29