How do I load a .yml property in a Gradle build script? - spring

I have .yml file with the following properties:
spring:
application:
name: auth module
profiles:
active: prod
My gradle.build script has these settings for the jar task:
jar {
baseName = 'auth-module-dev' // `dev` should come from `spring.profiles.active`
version = '0.1.2'
}
I want to build jar files with the naming convention auth-module-%profile_name%.jar where %profile_name% is the value of spring.profiles.active. How can I do this?

assume your yaml file has name cfg.yaml
don't forget to add --- at the beginning of yaml
---
spring:
application:
name: auth module
profiles:
active: prod
build.gradle:
defaultTasks "testMe"
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.yaml', name: 'snakeyaml', version: '1.19'
}
}
def cfg = new org.yaml.snakeyaml.Yaml().load( new File("cfg.yaml").newInputStream() )
task testMe( ){
doLast {
println "make "
println "profile = ${cfg.spring.profiles.active}"
assert cfg.spring.profiles.active == "prod"
}
}

Related

Pipeline Task Report Generator fails

I'm trying to create a test coverage report in Azure DevOps pipelines but I get The report file "path" is invalid. File does not exist.
Before I run this failing task, I run a task to execute the tests, and I see that task runTestsWithJacoco, defined in the build.gradle to create a Jacoco report is SKIPPED.
I'm using gradle 7.0.1 and jacoco version 0.8.7.
The gradle buil for project:
buildscript {
ext.jacoco_version = "0.8.7"
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.1"
classpath "org.jacoco:org.jacoco.core:$jacoco_version"
}
}
apply plugin: PropertiesPlugin
allprojects {
repositories {
google()
mavenCentral()
}
}
The gradle.build for app
plugins {
id 'jacoco'
}
jacoco {
toolVersion = jacoco_version
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
android {
testOptions {
unitTests.all {
useJUnitPlatform()
}
unitTests {
includeAndroidResources = true
}
}
}
task runTestsWithJacoco(
type: JacocoReport,
dependsOn: ['testDebugUnitTest'],
group: 'Reporting',
description: 'Running my tests with jacoco'
) {
def coverageSourceDirs = [
"src/main/java"
]
def fileFilter = [
'**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*'
]
def javaClasses = fileTree(
dir: buildDir,
includes: ['intermediates/javac/debug/classes/**'],
excludes: fileFilter
)
classDirectories.from(files([ javaClasses ]))
additionalSourceDirs.from(files(coverageSourceDirs))
sourceDirectories.from(files(coverageSourceDirs))
executionData.from(fileTree(dir: buildDir, includes: [
"jacoco/testDebugUnitTest.exec"
]))
reports {
xml.enabled = true
html.enabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
The pipeline:
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Gradle#2
name: run_unit_tests
inputs:
workingDirectory: ''
gradleWrapperFile: 'gradlew'
gradleOptions: '-Xmx3072m'
publishJUnitResults: true
testResultsFiles: 'app/build/test-results/testDebugUnitTest/TEST-*.xml'
tasks: ':app:runTestsWithJacoco'
- task: reportgenerator#4
name: generate_report
inputs:
reports: 'app/build/reports/jacoco/runTestsWithJacoco/runUnitTestsWithCoverage.xml'
targetdir: 'coverage'
sourcedirs: 'service/src/main/java'
reporttypes: 'Badges;HtmlInline_AzurePipelines;Cobertura'
Why does reportgenerator#4 fail?
I appreciate any help.
I can reproduce the same issue in reportgenerator#4 task.
The root cause of this issue can be that the publishing path you set is invalid.
You can check the build log in Gradle task and check if you can see the following log record:
To solve this issue, you can try the following sample:
- task: Gradle#2
name: run_unit_tests
inputs:
workingDirectory: ''
gradleWrapperFile: 'gradlew'
gradleOptions: '-Xmx3072m'
publishJUnitResults: true
testResultsFiles: '**/TEST-*.xml'
tasks: ':app:runTestsWithJacoco'
- task: reportgenerator#4
name: generate_report
inputs:
reports: '$(build.sourcesdirectory)/build/test-results/TEST-*.xml'
targetdir: 'coverage'
sourcedirs: 'service/src/main/java'
reporttypes: 'Badges;HtmlInline_AzurePipelines;Cobertura'
Result:
I ended up changing the build agent from Ubuntu to MacOS and that stopped the build from falling. I don't know the reason why it was failing with Ubuntu.

Liquibase module and a variable in Spring Boot Project

I use liquibase in my project, and here is one of my config files:
databaseChangeLog:
- changeSet:
id: 123
author: m.rybalkin
changes:
- update:
columns:
- column:
name: code
value: '123'
schemaName: prod
tableName: config_bundle
where: code='321'
Here is my build.gradle of the "liquibase" module:
group 'com.project.test'
version '0.1.0'
buildscript {
dependencies {
classpath "org.liquibase:liquibase-gradle-plugin:${liqubasePluginVersion}"
classpath "gradle.plugin.com.avast.gradle:gradle-docker-compose-plugin:${gradleDockerComposePluginVersion}"
}
}
apply plugin: 'java'
apply plugin: 'org.liquibase.gradle'
apply plugin: 'com.avast.gradle.docker-compose'
dependencies {
liquibaseRuntime "org.liquibase:liquibase-core:${liquibaseCoreVersion}"
liquibaseRuntime "org.postgresql:postgresql:${postgresqlVersion}"
}
liquibase {
activities {
main {
def file = new File("${projectDir}/liquibase.properties")
if (file.exists()) {
def props = new Properties()
InputStream is = new FileInputStream(file)
props.load(is)
is.close()
changeLogFile props['changeLogFile']
outputFile 'liquibase/sql-migration-bundle.sql'
url props['url']
username props['username']
password props['password']
} else {
println "Add ${projectDir}/liquibase.properties if you want use liquibase plugin"
}
}
dockerPostgres {
changeLogFile "${projectDir}/changelog/changelog-master.yml"
url 'jdbc:postgresql://localhost:5555/test'
username 'test'
password 'test'
}
runList = 'main'
}
}
task localUpdate(dependsOn: "composeUp") {
doFirst {
liquibase.setProperty("runList", "dockerPostgres")
}
finalizedBy update
}
task localDropAll(dependsOn: "composeUp") {
doFirst {
liquibase.setProperty("runList", "dockerPostgres")
}
finalizedBy dropAll
}
I have two different names of my schema, a "prod" for production and a "test" for tests.
Is it possible to set a variable in my application.yml or build.gradle for changing the name when I'm testing my app and when I'm deploying it?
P.S. I also have two different profiles of my Spring app - "prod" and "test"
You certainly can add properties at runtime of liquibase (which can be passed in from gradle, directly from commandline, etc).
So you can for example call liquibase on the CLI:
liquibase -Durl= update

Connect war task to cargo deploy task

I've create a gradle script in order to deploy a deployable on a container using cargo plugin:
class RemoteContainer {
String name
String container
String hostname
Integer port
String username
String password
String purpose
}
def remoteContainers = [new RemoteContainer(
name: 'wildfly10',
container: 'wildfly10x',
hostname: 'localhost',
port: 9990,
username: 'user',
password: 'passwd',
purpose: 'development'
)
]
remoteContainers.each { config ->
task "deployDev${config.name.capitalize()}"(type: com.bmuschko.gradle.cargo.tasks.remote.CargoDeployRemote) {
description = "Deploys WAR to remote Web Application Server: '${config.name}'."
containerId = config.container
hostname = config.hostname
port = config.port
username = config.username
password = config.password
dependsOn war
}
task "undeployDev${config.name.capitalize()}"(type: com.bmuschko.gradle.cargo.tasks.remote.CargoUndeployRemote) {
description = "Deploys WAR to remote Web Application Server: '${config.name}'."
containerId = config.container
hostname = config.hostname
port = config.port
username = config.username
password = config.password
dependsOn = war
}
}
Nevertheless, I've created several tasks in order to create custom war files according to its scope:
task createQAWar(type: War, dependsOn: classes) {
archiveName "webapi-demo-${versioning.info.display}.war"
destinationDir = file("$buildDir/dist")
webInf {
...
}
}
task createDevelopmentWar(type: War, dependsOn: classes) {
archiveName "webapi-dev-${versioning.info.display}.war"
destinationDir = file("$buildDir/dist")
webInf {
...
}
}
task createTestingWar(type: War, dependsOn: classes) {
archiveName "webapi-test-${versioning.info.display}.war"
destinationDir = file("$buildDir/dist")
webInf {
...
}
}
task createProductionWar(type: War, dependsOn: classes) {
archiveName "webapi-prod-${versioning.info.display}.war"
destinationDir = file("$buildDir/dist")
webInf {
...
}
}
I'd like to link that deployDev tasks pick the war artifact generated on createDevelopmentWar.
I've tried to set dependsOn property to createDevelopmentWar:
remoteContainers.each { config ->
task "deployDev${config.name.capitalize()}"(type: com.bmuschko.gradle.cargo.tasks.remote.CargoDeployRemote) {
description = "Deploys WAR to remote Web Application Server: '${config.name}'."
containerId = config.container
hostname = config.hostname
port = config.port
username = config.username
password = config.password
dependsOn = createDevelopmentWar <<<<<<<<<<<<<<<<<
}
}
Nevertheless, gradle it's getting me this message:
What went wrong:
A problem occurred evaluating root project 'webapi'.
Cannot cast object 'task ':createDevelopmentWar'' with class 'org.gradle.api.tasks.bundling.War_Decorated' to class 'java.lang.Iterable
'
I've also tried setting dependsOn to war task, nevertheless the message is the same.
EDIT
Once, I've changed the syntax for dependsOn = [createDevelopmentWar], I'm facing up with another trouble:
It's getting me this message:
Execution failed for task ':deployDevWildfly10'.
Deployable D:\projects\living\platform\webapi\build\libs\webapi-dev-89c157a-dirty.war does not exist
It's trying to get the artifact from build\libs\. Nevertheless, the war artifact has been created in destinationDir = file("$buildDir/dist"):
task createDevelopmentWar(type: War, dependsOn: classes) {
archiveName "webapi-dev-${versioning.info.display}.war"
destinationDir = file("$buildDir/dist")
webInf {
...
}
}
How could I set cargo picks the artifact up from previously war type task(createDevelopmentWar) execution information?
dependsOn = createDevelopmentWar results in a call to setDependsOn(createDevelopmentWar) which expects an Iterable that a task is not.
dependsOn createDevelopmentWar would result in a call to dependsOn(createDevelopmentWar) which expects a varargs parameter and thus sould add the task to the dependencies.
If you really want to replace all dependencies with this one dependency, you have to do it like dependsOn = [createDevelopmentWar].

How to use jOOQ code generation with Gradle?

I am reading this tutorial of jOOQ
It shows me how I can use jOOQ code-generation from inside Gradle.
Based on this tutorial I modified my build.gradle file and it looks like this:
group 'com.abhi'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'org.flywaydb.flyway'
sourceCompatibility = 1.8
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.flywaydb:flyway-gradle-plugin:3.2.1'
classpath 'org.jooq:jooq-codegen:3.7.1'
classpath 'com.h2database:h2:1.4.177'
}
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.jooq', name: 'jooq', version: '3.7.1'
compile group: 'org.jooq', name: 'jooq-meta', version: '3.7.1'
compile group: 'org.jooq', name: 'jooq-codegen', version: '3.7.1'
runtime group: 'com.h2database', name: 'h2', version: '1.4.177'
}
flyway {
url = 'jdbc:h2:file:target/foobar'
user = 'sa'
}
But the part I am not able to understand is that the tutorial also provides some code:
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.7.0.xsd') {
jdbc() {
driver('org.h2.Driver')
url('jdbc:h2:~/test-gradle')
user('sa')
password('')
}
generator() {
database() {
}
generate() {
}
target() {
packageName('org.jooq.example.gradle.db')
directory('src/main/java')
}
}
}
// Run the code generator
// ----------------------
org.jooq.util.GenerationTool.generate(
javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class)
)
Where should I put this code and how shall I execute it?
This code looks like Groovy code but I am writing a Java project. So how and where does this fit into my project?
My objective is that every time I build the project, all the code generation is done by Gradle itself so that I don't have to run any tools manually.
Does it mean that I copy and paste this code inside of my build.gradle file?
You can e.g. add generate task that will be defined as follows:
task generate << {
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.7.0.xsd') {
jdbc() {
driver('org.h2.Driver')
url('jdbc:h2:~/test-gradle')
user('sa')
password('')
}
generator() {
database() {
}
generate() {
}
target() {
packageName('org.jooq.example.gradle.db')
directory('src/main/java')
}
}
}
org.jooq.util.GenerationTool.generate(
javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class)
)
}
You can create a task and use the org.jooq.meta.jaxb.Configuration class to configure code generation:
In your build.gradle file, you will want to place the following on the very top of the file (this will allow you to use the jOOQ classes (such as the org.jooq.meta.jaxb.Configuration class inside your Gradle file).
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.jooq', name: 'jooq', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-meta', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-codegen', version: '3.13.4'
classpath group: 'org.postgresql', name: 'postgresql', version: '42.2.16'
}
}
Next, create a new file (you can also do it inside build.gradle, but I like to separate it for better organization) called jooq.gradle, or whatever you would like to call it.
The following file is adapted from here with some minor modifications.
At the top of your program, you will want to add the following imports that will let us work with jOOQ (they may show up as red or invalid, but we'll fix that in just a bit):
import org.jooq.codegen.GenerationTool
import org.jooq.meta.jaxb.Database
import org.jooq.meta.jaxb.Generator
import org.jooq.meta.jaxb.Jdbc
import org.jooq.meta.jaxb.Target
Great!, now we can define some variables after this for better organization (not required):
ext.db = [
url: 'jdbc:postgresql://localhost:5432/postgres',
user: 'postgres',
password: 'postgres',
schema: 'public',
driver: 'org.postgresql.Driver',
jooqDbImpl: 'org.jooq.meta.postgres.PostgresDatabase',
packageName: 'samplepackage'
]
ext.genpath = new File("${projectDir}/build/generated-src/jooq/main")
For the above snippet, you will want to change the variables according to your configuration.
Next, we can add our buildscript again for this file:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.jooq', name: 'jooq', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-meta', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-codegen', version: '3.13.4'
classpath group: 'org.postgresql', name: 'postgresql', version: '42.2.16'
}
}
After this, we can add genPath as a Java source file:
sourceSets.main.java.srcDirs += genpath.toString()
And we can finally start our generateCode() task!
Create the function definition as such:
task generateCode() {
}
Inside you will want to put this snippet of code, which will configure the generator. Feel free to modify this according to your needs, but I will be putting a snippet that works for me:
org.jooq.meta.jaxb.Configuration configuration = new
org.jooq.meta.jaxb.Configuration()
.withJdbc(new Jdbc()
.withDriver(db.driver)
.withUrl(db.url)
.withUser(db.user)
.withPassword(db.password)
)
.withGenerator(new Generator()
.withDatabase(new Database()
.withName(db.jooqDbImpl)
.withIncludes(".*")
.withExcludes("")
.withInputSchema(db.schema)
)
.withTarget(new Target()
.withPackageName(db.packageName)
.withDirectory(genpath.toString())
)
);
Finally, after all the hard work, you can execute this function, which will create your code using the configuration above:
GenerationTool.generate(configuration);
If you would like to have a function to delete the generated code, you can use the following snippet:
task deleteGeneratedCode(type: Delete) {
delete genpath
}
And here is the following, completed jooq.gradle file:
import org.jooq.codegen.GenerationTool
import org.jooq.meta.jaxb.Database
import org.jooq.meta.jaxb.Generator
import org.jooq.meta.jaxb.Jdbc
import org.jooq.meta.jaxb.Target
ext.db = [
url: 'jdbc:postgresql://localhost:5432/postgres',
user: 'postgres',
password: 'postgres',
schema: 'limehrm',
driver: 'org.postgresql.Driver',
jooqDbImpl: 'org.jooq.meta.postgres.PostgresDatabase'
packageName: 'limehrm'
]
ext.genpath = new File("${projectDir}/build/generated/source/jooq/main")
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath group: 'org.jooq', name: 'jooq', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-meta', version: '3.13.4'
classpath group: 'org.jooq', name: 'jooq-codegen', version: '3.13.4'
classpath group: 'org.postgresql', name: 'postgresql', version: '42.2.16'
}
}
sourceSets.main.java.srcDirs += genpath.toString()
task generateCode() {
if (!genpath.exists()) {
genpath.mkdirs()
}
org.jooq.meta.jaxb.Configuration configuration = new
org.jooq.meta.jaxb.Configuration()
.withJdbc(new Jdbc()
.withDriver(db.driver)
.withUrl(db.url)
.withUser(db.user)
.withPassword(db.password)
)
.withGenerator(new Generator()
.withDatabase(new Database()
.withName(db.jooqDbImpl)
.withIncludes(".*")
.withExcludes("")
.withInputSchema(db.schema)
)
.withTarget(new Target()
.withPackageName(db.packageName)
.withDirectory(genpath.toString())
)
);
GenerationTool.generate(configuration);
}
task deleteGeneratedCode(type: Delete) {
delete genpath
}
#Opal's answer works great (thanks!) with a couple of tweaks. Thought I'd share to spare anyone the hassle with newer versions of jOOQ & Gradle.
The changes:
<< removed from the first line and doLast added.
jOOQ .xsd updated to 3.9.2
org.jooq.util.GenerationTool.generate(writer.toString()) to generate the code
task generate {
doLast {
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.9.2.xsd') {
jdbc() {
driver('org.h2.Driver')
url('jdbc:h2:~/test-gradle')
user('sa')
password('')
}
generator() {
database() {
}
generate() {
}
target() {
packageName('org.jooq.example.gradle.db')
directory('src/main/java')
}
}
}
org.jooq.util.GenerationTool.generate(writer.toString())
}
}

set variable from task only when task is called

I've defined a task called debug that should set the applicationDefaultJvmArgs of the application plugin so that spring-boot will halt and wait for the debugger.
I would like to use this task in combination with another task I have defined called local which sets a spring.profiles.active variable.
apply plugin: 'spring-boot'
apply plugin: 'application'
apply plugin: 'org.flywaydb.flyway'
dependencies {
compile group: "org.apache.camel", name: "camel-netty4-http", version: camelVersion
compile group: "org.apache.camel", name: "camel-spring-boot", version: camelVersion
compile group: "org.springframework.boot", name: "spring-boot-starter", version: "1.2.1.RELEASE"
}
flyway {
url = 'jdbc:mysql://localhost:3306/foobar'
user = 'foo'
password = 'bar'
}
task local {
tasks.withType(org.springframework.boot.gradle.run.BootRunTask) {
systemProperty('spring.profiles.active', 'local')
}
}
task debug << {
project.ext {
applicationDefaultJvmArgs = [
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
]
}
}
When I call gradle using gradle local debug the debug the applicationDefaultJvmArgs are set but when I call gralde local they are also set ?
I've tried experimenting with
task debug << { ...
but that wouldn't make a difference.
Where is applicationDefaultJvmArgs defined? Can you provide the full code for your build script?
I believe that the next piece of code should do the job:
task debug << {
project.ext {
applicationDefaultJvmArgs = [
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005"
]
}
}
The changes:
Assigning applicationDefaultJvmArgs as part of the debug task action (rather than a configuration)
Defining applicationDefaultJvmArgs as a project property

Resources