ant.propertyFile updates comment everytime - spring

In my build.gradle file I have the following variable to indicate which environment I'm building to:
buildscript {
ext {
springBootVersion = '1.5.15.RELEASE'
kotlinVersion = '1.2.70'
queryDslVersion = '4.1.4'
env = 'local'
}
// ...
}
And on my application.properties I have the following line for spring to use the correct application-{env}.properties:
spring.profiles.active=local
I have created a gradle task to update this value on my application.properties as follow:
task setEnv {
doFirst {
ant.propertyfile(file: "$projectDir/src/main/resources/application.properties") {
entry( key: "spring.profiles.active", value: "$env")
}
println "Building with env = $env"
}
}
The task works just fine except it adds one comment line with the timestamp of the update. So my application.properties now looks like:
#Fri, 14 Dec 2018 11:22:04 -0200
spring.profiles.active=local
This is a problem because everytime someone builds the project it changes this file, causing conflicts when commiting and pushing to git.
Any ideas on how I could get rid of that comment? Or even a better approach to set spring profile?
Thanks!

I don't think it's possible with ant, because it's implemented with the java.util.Properties class and it's said in the javadoc that:
...a comment line is always written, consisting of an ASCII #
character, the current date and time (as if produced by the toString
method of Date for the current time), and a line separator as
generated by the Writer.
So, to avoid it, you have to change a value in your properties file in some other way. For example, by configurion the ProcessResources Gradle task as follows:
processResources {
filesMatching('**/*.properties') {
filter {
it.replace('#ENV#', "$env")
}
}
}
And the value should be set to #ENV# inside the application.properties

Related

How to set gradle subproject artifact as task input?

My gradle build has a subproject with a task that produces a file
$ ./gradlew :strings:tokenizeStrings # creates strings/string_tokens.csv
then in my root project I have a task which consumes that file
tasks.generateLocalizationFiles {
inputTokensCsvFile.set(layout.projectDirectory.file("strings/string_tokens.csv"))
}
this works, but since gradle doesn't know about the dependency, it only works if I run the two tasks manually in the right order
$ ./gradlew :strings:tokenizeStrings
$ ./gradlew :generateLocalizationFiles
I want to add the proper dependency to gradle so that I can run just :generateLocalizationFiles and it will go into the subproject and do whatever it needs to. But I can't figure out the right way to do it.
What I've tried:
Following Simple sharing of artifacts between projects, I tried adding a consumable configuration to the suproject build script
val localizationData by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
}
tasks.tokenizeStrings {
artifacts {
add("localizationData", outputTokensCsvFile) {
builtBy(this)
}
}
}
and then a resolvable configuration plus the dependency to the root project build script
val localizedStringData by configurations.creating {
isCanBeConsumed = false
isCanBeResolved = true
}
// hook up our resolvable configuration to the strings' consumable configuration
dependencies {
localizedStringData(project(mapOf(
"path" to ":strings",
"configuration" to "localizationData")
))
}
tasks.generateLocalizationFiles {
dependsOn(localizedStringData)
inputTokensCsvFile.set(localizedStringData.singleFile)
}
but that fails, seemingly because the consumable configuration is not populated?
Caused by: java.lang.IllegalStateException: Expected configuration ':localizedStringData' to contain exactly one file, however, it contains no files.
You need to add the outgoing artifact directly in the subproject build script, not inside the task configuration (which is only run lazily). You also don't need builtBy if you're using a RegularFileProperty for the artifact.
val localizationData by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
}
artifacts {
add("localizationData", tasks.tokenizeStrings.flatMap { it.outputTokensCsvFile })
}
The trick is to use flatMap to lazily access the task. You should similarly use map when passing it to the task resolving the data. That allows for lazy task creation and implicitly tells gradle about the dependency between the two:
tasks.generateLocalizationFiles {
inputTokensCsvFile.set(localizedStringData.elements.map { it.first().asFile })
}
This still feels somewhat hacky, since it would be very clumsy if you wanted to repeat this for many artifacts, but it does seem to be the idiomatic way of doing it in gradle since it doesn't require any explicit dependency creation via builtBy/dependsOn.

How to add parameters to flyway build.gradle file?

I have this in my build.gradle file:
flyway {
url = 'jdbc:oracle:thin:#localhost:1521:DB'
user = 'user'
password = 'pass'
driver = 'oracle.jdbc.OracleDriver'
placeholderReplacement = true
schemas = ['OWNER']
println "PRINT ARG:"
println testArg
locations = ["filesystem:soem-path/common"]
placeholders = [
'some.store.owner': flyway.schemas[0],
'some.user' : 'USER',
]
}
The build always fails after I added that part with the testArg. I wanted to get it in like this:
./gradlew flywayMigrate -PtestArg=test -Dflyway.configFiles=../flyway/flyway-dev.properties -i
The error message is:
Caused by: groovy.lang.MissingPropertyException: Could not get unknown property 'testArg' for object of type org.flywaydb.gradle.FlywayExtension.
at org.gradle.internal.metaobject.AbstractDynamicObject.getMissingProperty(AbstractDynamicObject.java:87)
at org.gradle.internal.metaobject.ConfigureDelegate.getProperty(ConfigureDelegate.java:130)
How can I get a property in there?
I believe your problem is the that when you are within the flyway configuration closure (i.e. the flyway { ... } section), your delegate object (i.e. when a property is not found in your local code, what class gradle will look in next) is the flyway extension. In other words, while you are in flyway { ... }, gradle will look for any property not found immediately in your code in the flyway plugin code.
What you are looking for is properties on the gradle project object. You could for example do something like this:
ext {
projectProperty = { name ->
project.hasProperty(name) ? project.properties[name] : null
}
}
flyway {
def testArg = projectProperty('testArg')
println "project property testArg: ${testArg}"
}
(untested, but should work in principle at least).
where the hasProperty call makes sure that the code doesn't break when the property is not set for the project.
For an explanation of delegation for groovy closures, the groovy language docs have a section specifically explaining this.
This worked:
def devEnv = System.properties.containsKey("devEnv")
flyway {
.....
if (devEnv) {
locations += "filesystem:some/path"
}
....
}
and call flyway like this:
./gradlew flywayClean -DdevEnv -Dflyway.configFiles=...

Gradle 6 migration settings.gradle.kts

I've got the following code in the settings.gradle.kts, which is works fine in gradle 5
rootProject.name = "server"
val pluginsRepoUrl: String by settings
val repoUsername: String by settings
val repoPassword: String by settings
pluginManagement {
repositories {
maven {
url = uri(pluginsRepoUrl)
credentials {
username = repoUsername
password = repoPassword
}
}
}
}
I want to upgrade to gradle 6 but this code is not works and gives me a following error:
e: .../settings.gradle.kts:10:23: Unresolved reference: pluginsRepoUrl
The values comes from the gradle.properties file.
In Gradle 6, the behavior of the pluginManagement {} block was changed:
Previously, any pluginManagement {} blocks inside a settings script were executed during the normal execution of the script.
Now, they are executed earlier in a similar manner to buildscript {} or plugins {}. This means that code inside such a block cannot reference anything declared elsewhere in the script.
(emphasis mine)
This means that you cannot reference the variables declared outside of that block. To fix this, move those declarations inside the pluginManagement {} block:
pluginManagement {
val pluginsRepoUrl: String by settings
val repoUsername: String by settings
val repoPassword: String by settings
repositories {
...

Android gradle build: how to set global variables

How can I set a global variable that can be accessed from build.gradle and tasks?
To set a global variable
project.ext.set("variableName", value)
To access it from anywhere in the project:
project.variableName
For instance:
project.ext.set("newVersionName", versionString)
and then...
println project.newVersionName
For more information see: http://www.gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
EDIT:
As commented by Dmitry, In new versions you can use the following shorthand:
project.ext.variableName = value
The answer from Guy is excellent. I just want to add the practical code.
Example:
Put something like this in the Project build.gradle:
project.ext {
minSdkVersion = 21
targetSdkVersion = 23
}
And put something like this in the Module build.gradle to access it:
defaultConfig {
minSdkVersion.apiLevel project.minSdkVersion
targetSdkVersion.apiLevel project.targetSdkVersion
}
You can also do this. Let's say you want to add appcompat with the version 25.3.1, you can add a variable version_name in your project level build.gradle.
buildscript{
ext.version_name = '25.3.1'
}
Now you can add this to your application level build gradle and avoid any conflicts.
compile "com.android.support:appcompat-v7:$version_name"
compile "com.android.support:recyclerview-v7:$version_name"
compile "com.android.support:design:$version_name"
You cant create a Gradle file in the project's root directory and put all variables there, like this:
lib-versions.gradle
ext {
kt_core = '1.6.0'
app_compat = '1.3.1'
material = '1.4.0'
constraintlayout = '2.1.1'
nav_version = '2.3.5'
junit = '4.13.2'
junit_ext = '1.1.3'
escpresso = '3.4.0'
}
Then in the project build.gradle file on the bottom you should apply the Gradle file like this:
buildscript {
...
}
task clean(type: Delete) {
delete rootProject.buildDir
}
apply from: 'lib-versions.gradle' //apply it like this
Then you are able to use the variables in any module-level build Gradle file (app/build.gradle) like this:
implementation "androidx.core:core-ktx:$rootProject.ext.kt_core"
implementation "androidx.appcompat:appcompat:$rootProject.ext.app_compat"
implementation "com.google.android.material:material:$rootProject.ext.material"
implementation "androidx.constraintlayout:constraintlayout:$rootProject.ext.constraintlayout"
...
Additional, for dynamic global variables you can define global functions in the master build.gradle file:
First, define your function, for example for git branch:
def getGitBranch = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
In allProjects section set the variable:
allprojects {
repositories {
google()
jcenter()
}
project.ext {
gitBranch="\"${getGitBranch()}\""
}
}
In your build.gradle files of your sub projects or android modules, get this variable like this:
android {
compileSdkVersion project.mCompileSdkVersion.toInteger()
defaultConfig {
minSdkVersion project.mMinSdkVersion.toInteger()
...
buildConfigField "String", "GitBranch", project.gitBranch
}
...
}
Finally, you can use it in your code like this:
public static String getGitBranch() {
return BuildConfig.GitBranch;
}
This is for Kotlin DSL (build.gradle.kts).
Root (top-level) build file:
val myVariable by extra("watermelon")
// Alternative notations:
// extra.set("myVariable", "watermelon")
// extra["myVariable"] = "watermelon"
Extra properties on a project object are visible from its subprojects. Note that extra. is equivalent to project.extra.; in other words the project object is implicit.
A sub-project build file:
val myVariable: String by rootProject.extra
// Alternative notations:
// val myVariable: String by rootProject
// val myVariable: String = rootProject.extra["myVariable"] as String
// val myVariable: String = rootProject.extra.get("myVariable") as String
Note that when using Kotlin delegation (by keyword) the name of the variables should be the same in both build files.
See Gradle Docs: Extra properties for more information.

Is resource filtering in Gradle possible without using tokens?

The recommended way to do resource filtering in Gradle is by having tokens in the properties file and then replacing them when processing.
Example
# config.properties
hostname = #myhost#
and in build.gradle do something like below
processResources {
filter ReplaceTokens, tokens: [
"myhost": project.property('myhost')
]
}
The problem with this approach is that it won't work when running from IDEs like eclipse. I would like the property files to be free of Gradle specific tokens i.e just have
hostname = localhost
but have option to replace it when building from Gradle.
You could use the following (not tested):
processResources {
filesMatching('**/config.properties') {
filter {
it.replace('localhost', project.property('myhost'))
}
}
}
Or you could have a default file, used during development in your IDE, and have another file containing tokens and replacing the development one when building using gradle. Something like this (not tested)
processResources {
exclude '**/config.properties'
filesMatching('**/config-prod.properties') {
setName 'config.properties'
filter ReplaceTokens, tokens: [
"myhost": project.property('myhost')
]
}
}
Can use thing like placeholder if you want.
In config.properties file
var1=${var1}
var2=${var2}
In gradle.properties file
processResources {
filesMatching('**config.properties') {
expand(
'var1': project.property('var1'),
'var2': project.property('var2'),
)
}
}
The spring-boot approach
project.version=X.X.X.X
info.build.version=#project.version#
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-application-info-automatic-expansion
# File: application.yml
# forward gradle properties to spring boot properties
version: #version#
Setup gradle task (tested with Gradle 7.4):
import org.apache.tools.ant.filters.ReplaceTokens
processResources {
with copySpec {
from 'src/main/resources'
include 'application*.yml'
duplicatesStrategy 'include'
project.properties.findAll {it.value != null}.each {
filter(ReplaceTokens, tokens: [(it.key): it.value.toString()])
}
}
}
Resulting file:
# File: application.yml
# forward gradle properties to spring boot properties
version: 0.0.1-SNAPSHOT

Resources