Is there a way to have Gradle call flywayMigrate on build? - gradle

I'd find it really useful to invoke Flyway's migrate command automatically each time I run gradle build.
Spring Boot does this under the hood, but can Gradle do this itself? I have a non-Boot app that I'd like to be able to manage the same way.
I'm hoping it is some lifecycle hook. This question is helpful, but how do I execute flyway pre-build?

Yes you can. You have several options. You can hook into the lifecycle at any point. By default the java gradle plugin has several places you could hook into.
$ ./gradlew clean build
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build
You can attach to any of these points
Or you if you need to be applied no matter what before anything else then you might want to consider a simple plugin.
Here is an example of both:
build.gradle:
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
testCompile 'junit:junit:4.12'
}
task runFlyAwayCommand << {
// process is type java.lang.Process
def process = "printf lifecycle hooked task".execute()
def processExitValue = process.waitFor()
def processOutput = process.text
project.logger.lifecycle("Flyaway{ exitValue: $processExitValue output: $processOutput }")
}
// compileJava could be any lifecycle task
tasks.findByName('compileJava').dependsOn tasks.findByName('runFlyAwayCommand')
// if you need to execute earlier you might want to create a plugin
apply plugin: SamplePlugin
class SamplePlugin implements Plugin<Project> {
#Override
void apply(Project project) {
def process = "printf plugin apply".execute()
def processExitValue = process.waitFor()
def processOutput = process.text
project.logger.lifecycle("Flyaway{ exitValue: $processExitValue output: $processOutput }")
}
}
Output:
$ ./gradlew clean build
Configuration on demand is an incubating feature.
Flyaway{ exitValue: 1 output: plugin }
:clean
:runFlyAwayCommand
Flyaway{ exitValue: 1 output: lifecycle }
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
Total time: 1.294 secs

Related

Ignore file when calculating gradle cache fingerprint

In my project I access the build-info.properties generated by the gradle springboot plugin buildInfo() task during runtime to include my project version in log metadata.
My problem is that this file is included in the fingerprint calculation for gradle tasks such as tests via the classpath fingerprint, but the version in that file changes with every build in my pipelines. Therefore I can never reuse that cache.
I saw this question on how to exclude that file from runtime, but if I follow that advise,
the file's not available during runtime anymore, naturally.
the file /BOOT-INF/classes/META-INF/build-info.properties is empty. Therefore my application fails on startup.
Can I somehow exclude it from the cache fingerprint calculation only?
Edits:
I'm currently on Gradle 6.8.1 and Spring Bot 2.2.2.
This is how I generate the file:
springBoot {
buildInfo()
}
And this is how I add the normalization:
normalization {
runtimeClasspath {
ignore "**/build-info.properties"
}
}
Update: As stated in the comment, this problem appeared due to a missconfiguration of my Gradle build scripts in another location. The normalization approach linked in the question and explained in the accepted answer is the solution to the initial question.
Gradle input normalization should be a solution for it.
normalization {
runtimeClasspath {
ignore '**/build-info.properties'
}
}
Not sure why you are saying " if I follow that advise, the file's not available during runtime anymore". According to documentation
The effect of this configuration would be that changes to build-info.properties would be ignored for up-to-date checks and build cache key calculations. Note that this will not change the runtime behavior of the test task — i.e. any test is still able to load build-info.properties and the runtime classpath is still the same as before.
Here are some tests that proves the above
Running build first time
./gradlew build -Pversion=0.0.1 --console=plain
> Task :bootBuildInfo
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes
> Task :resolveMainClassName
> Task :bootJar
> Task :jar
> Task :assemble
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test
> Task :check
> Task :build
test task was executed because there is no build cache.
Running build second time with different version
./gradlew build -Pversion=0.0.2 --console=plain
> Task :bootBuildInfo
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes
> Task :resolveMainClassName UP-TO-DATE
> Task :bootJar
> Task :jar
> Task :assemble
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test UP-TO-DATE
> Task :check UP-TO-DATE
> Task :build
As you may see only build tasks were executed but test task is still UP-TO-DATE.
build-info.properties is still available under build/resources/main/META-INF/
build.artifact=demo
build.group=com.example
build.name=demo
build.time=2023-02-01T18\:32\:03.871040Z
build.version=0.0.2
and could be accessed using Spring Boot actuator endpoint /actuator/info in case it's enabled
{
"build": {
"artifact": "demo",
"name": "demo",
"version": "0.0.2",
"group": "com.example"
}
}
Consider excluding build time
You could optimize even more by excluding time from the build info.
springBoot {
buildInfo {
excludes = ['time']
}
}
Usually it's a good idea for optimizing local builds. Otherwise build tasks will be always executed. By excluding time all tasks will be cached
./gradlew build --console=plain
> Task :bootBuildInfo UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :resolveMainClassName UP-TO-DATE
> Task :bootJar UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test UP-TO-DATE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE
but build.time will not be part of the build-info.properties
build.artifact=demo
build.group=com.example
build.name=demo
build.version=0.0.1-SNAPSHOT

Publishing artifact only after tests were successful

I've been fighting this problem for a long time and genuinely out of ideas. In my project I use the maven-publish plugin for publishing artifact to my repository:
plugins {
kotlin("jvm") version "1.6.20"
id("maven-publish")
}
And also using Kotest to run tests:
tasks.withType<Test> {
useJUnitPlatform()
}
Obviously, I want to publish artifacts only if all tests passed, so I add the following:
tasks.publish {
dependsOn(tasks.test)
mustRunAfter(tasks.test)
}
However, when I run ./gradlew clean build test publish on the command line, the artifacts are always published before the tests run:
> Task :clean
> Task :processResources NO-SOURCE
> Task :processTestResources
> Task :generatePomFileFor«project-name»Publication
> Task :compileKotlin
> Task :compileJava NO-SOURCE
> Task :classes UP-TO-DATE
> Task :inspectClassesForKotlinIC
> Task :jar
> Task :assemble
> Task :generateMetadataFileFor«project-name»Publication
> Task :publish«project-name»PublicationTo«repository-name»Repository
> Task :compileTestKotlin
> Task :compileTestJava NO-SOURCE
> Task :testClasses
> Task :test
Is there something I'm missing?
Took me long enough. Apparently, the publish task is not the one that actually publishes the jar, but the publish«project-name»PublicationTo«repository-name»Repository. However, this task is not available until after configuration, so the best I could do is:
afterEvaluate {
tasks.getByName("publish«project-name»PublicationTo«repository-name»Repository").dependsOn(tasks.test)
}

Trying to run Spock Specification using Gradle... "Test Events were not received" (In IntelliJ Idea)

I am trying to run a simple Spock specification with Groovy, in a Gradle project. But I am getting this message Test events were not received.
Here is my build.gradle:
plugins {
id 'groovy'
}
group 'com.Sample'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
testCompile "org.spockframework:spock-core:1.1-groovy-2.4"
}
Here is my spock specification :
import spock.lang.Specification
class SampleSpec extends Specification {
def "validate string concat"(){
given:
def string1 = "hello"
def string2 = "world"
when:
def stringConcat = string1.concat(string2)
then:
stringConcat == "helloworld"
}
}
Here is the message I get when I run the Spock Specification:
> Task :cleanTest UP-TO-DATE
> Task :compileJava UP-TO-DATE
> Task :compileGroovy NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :compileTestGroovy NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 up-to-date
9:20:33 AM: Tasks execution finished ':cleanTest :test --tests "SampleSpec.validate string concat"'.
Any help would be greatly appreciated!
EDIT:
Now my Spock tests are running successfully but I am getting this message:
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/C:/Users/dtara/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy-all/2.4.9/3334e99a8baae12d6e014d444149e337ceb99a00/groovy-all-2.4.9.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
**Anyone knows that this means? Should I worry about it? **
Ok, first question:
The tests should live in src/test/groovy not src/test/java (and packages would be nice) ;-)
Second question:
You're using quite an old version of Groovy, but until Groovy 3.0 comes out, you'll see that warning when using Java 9+. It's trying to use things in Java that Java is going to remove in a future release...
It's fine to run with what you have at the moment (apart from maybe pull in the latest groovy -- 2.5.8 at the time I'm writing this)

Force Gradle to run task even if it is UP-TO-DATE

I came across a situation, when everything is UP-TO-DATE for Gradle, although I'd still like it to run the task for me. Obviously it does not:
gradle test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.833 secs
I can force it to run test by running clean test, although this may be considered an overhead in some cases. Is there a way to force task execution no matter if Gradle believes it's UP-TO-DATE or not?
If you want to rerun all tasks, you can use command line parameter --rerun-tasks. However this is basically the same as doing a clean as it reruns all the tasks.
If you want to run a single task every time, then you can specify that it is never up-to-date:
mytask {
outputs.upToDateWhen { false }
}
If you want to rerun a single task once and leave all the other tasks, you have to implement a bit of logic:
mytask {
outputs.upToDateWhen {
if (project.hasProperty('rerun')) {
println "rerun!"
return false
} else {
return true
}
}
}
And then you can force the task to be re-run by using:
gradle mytask -Prerun
Note that this will also re-run all the tasks that depend on mytask.
In Gradle v7.6, the new --rerun flag can tell an individual task to ignore up-to-date checks:
./gradlew theTask --rerun

Gradle does not import groovy class when running task

I have a gradle (1.8) project with 2 classes A and B where B import A. Both files are under /foo/bar/ package (myProjectFolder/test/foo/bar folder).
A.groovy class
package foo.bar
import org.junit.Test;
public class ATest{
#Test
public void run() {
System.out.println("CLASS A");
}
}
B.groovy class
package foo.bar
import org.junit.Test;
public class BTest{
#Test
public void run() {
ATest a = new ATest();
a.run();
System.out.println("CLASS B");
}
}
build.gradle
apply plugin: "groovy"
apply plugin: "java"
repositories {
mavenCentral()
}
dependencies {
groovy group: "org.codehaus.groovy", name:"groovy-all", version: "1.8.6"
testCompile "junit:junit:4.10"
}
test {
testLogging.showStandardStreams = true
}
sourceSets {
test { groovy {
srcDir 'test/foo/bar'
} }
}
buildscript {
repositories { mavenCentral() }
}
configurations{
addToClassLoader
}
dependencies {
addToClassLoader "junit:junit:4.10"
}
URLClassLoader loader = GroovyObject.class.classLoader
configurations.addToClassLoader.each {File file ->
loader.addURL(file.toURL())
}
task runA << {
new GroovyShell().run(file('test/foo/bar/ATest.groovy'));
}
task runB << {
new GroovyShell().run(file('test/foo/bar/BTest.groovy'));
}
Output console for gradle clean -Dtest.single=A test
[root#vm1]# gradle clean -Dtest.single=A test
The groovy configuration has been deprecated and is scheduled to be removed in Gradle 2.0. Typically, usages of 'groovy' can simply be replaced with 'compile'. In some cases, it may be necessary to additionally configure the 'groovyClasspath' property of GroovyCompile and Groovydoc tasks.
:clean
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources
:classes
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test
foo.bar.ATest > run STANDARD_OUT
CLASS A
BUILD SUCCESSFUL
Total time: 13.205 secs
Output console for gradle clean -Dtest.single=B test
[root#vm1]# gradle clean -Dtest.single=B test
The groovy configuration has been deprecated and is scheduled to be removed in Gradle 2.0. Typically, usages of 'groovy' can simply be replaced with 'compile'. In some cases, it may be necessary to additionally configure the 'groovyClasspath' property of GroovyCompile and Groovydoc tasks.
:clean
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources
:classes
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test
foo.bar.BTest > run STANDARD_OUT
CLASS A
CLASS B
BUILD SUCCESSFUL
Total time: 12.218 secs
Output console for gradle -q runA -i
[root#vm1]# gradle -q runA -i
Starting Build
Settings evaluated using empty settings script.
Projects loaded. Root project using build file '/opt/myProject/build.gradle'.
Included projects: [root project 'myProject']
Evaluating root project 'myProject' using build file '/opt/myProject/build.gradle'.
Starting file lock listener thread.
The groovy configuration has been deprecated and is scheduled to be removed in Gradle 2.0. Typically, usages of 'groovy' can simply be replaced with 'compile'. In some cases, it may be necessary to additionally configure the 'groovyClasspath' property of GroovyCompile and Groovydoc tasks.
All projects evaluated.
Selected primary task 'runA'
Tasks to be executed: [task ':runA']
:runA (Thread[main,5,main]) started.
:runA
Executing task ':runA' (up-to-date check took 0.001 secs) due to:
Task has not declared any outputs.
CLASS A
JUnit 4 Runner, Tests: 1, Failures: 0, Time: 63
:runA (Thread[main,5,main]) completed. Took 0.739 secs.
BUILD SUCCESSFUL
Total time: 7.826 secs
Output console for gradle -q runB -i <-- THIS EXECUTION FAILS and I don't know why.
[root#vm1]# gradle -q runB -i
Starting Build
Settings evaluated using empty settings script.
Projects loaded. Root project using build file '/opt/myProject/build.gradle'.
Included projects: [root project 'myProject']
Evaluating root project 'myProject' using build file '/opt/myProject/build.gradle'.
Starting file lock listener thread.
The groovy configuration has been deprecated and is scheduled to be removed in Gradle 2.0. Typically, usages of 'groovy' can simply be replaced with 'compile'. In some cases, it may be necessary to additionally configure the 'groovyClasspath' property of GroovyCompile and Groovydoc tasks.
All projects evaluated.
Selected primary task 'runB'
Tasks to be executed: [task ':runB']
:runB (Thread[main,5,main]) started.
:runB
Executing task ':runB' (up-to-date check took 0.001 secs) due to:
Task has not declared any outputs.
:runB FAILED
:runB (Thread[main,5,main]) completed. Took 0.237 secs.
FAILURE: Build failed with an exception.
* Where:
Build file '/opt/myProject/build.gradle' line: 46
* What went wrong:
Execution failed for task ':runB'.
> startup failed:
/opt/myProject/test/foo/bar/BTest.groovy: 8: unable to resolve class ATest
# line 8, column 9.
ATest a = new ATest();
^
/opt/myProject/test/foo/bar/BTest.groovy: 8: unable to resolve class ATest
# line 8, column 13.
ATest a = new ATest();
^
2 errors
* Try:
Run with --stacktrace option to get the stack trace. Run with --debug option to get more log output.
BUILD FAILED
Total time: 7.872 secs
So, what am I missing to run the task runB correctly?
Note: the premise of the question is based on a truly unorthodox use-case, which should be corrected. That said, I can explain the behaviour that is observed.
When the command-line specifies the test task, then ~/build/classes/test is added to the classpath. In the case of running the runB task, it is not. So BTest.groovy can't find ATest.
To correct it (only as an illustration, not as a suggestion for 'real' code), consider:
// don't do this in a real project!
task runB(dependsOn: 'compileTestGroovy') << {
def testDirURL = new File("${projectDir}/build/classes/test").toURL()
loader.addURL(testDirURL)
new GroovyShell(loader).run(file('test/foo/bar/BTest.groovy'))
}
Now, runB requires that the Groovy test code is compiled, and then manually (!?) adds it to the classloader used by GroovyShell.

Resources