gradle - kotlin, multiple exec tasks - kotlin

Given an Exec gradle task my-custom-cli-task in a gradle kotlin build script defined by
task<Exec>("my-custom-cli-task") {
workingdir(projectDir)
commandLine("my-cli-command","somefile.someextension")
}
I need to run this for a set of project files, defined by
val sourcefileSet = fileTree("$projectDir/") { include("**/*.someextension") }
All theses commands could be run in parallel.
How should this be done using gradle kotlin best practices ?
(Note : I'm aware of https://stackoverflow.com/a/43070603/5565079 but It applies to gradle groovy and I'm not sure how it would translate to kotlin.)

Related

How to use Gradle liquibaseRuntime configuration in a Kotlin/Multiplatform project

Currently, I'm porting my Spring Boot build.gradle.kts configuration to the Kotlin/MP stack. I don't know what to do with one part of the liquibaseRuntime configuration. The original config looks like:
// other dependencies omitted
liquibaseRuntime("org.liquibase:liquibase-core")
liquibaseRuntime("org.liquibase.ext:liquibase-hibernate5:3.8")
liquibaseRuntime(sourceSets.getByName("main").compileClasspath)
liquibaseRuntime(sourceSets.getByName("main").output)
liquibaseRuntime("org.postgresql:postgresql")
liquibaseRuntime("org.springframework.boot:spring-boot:$springBootVersion")
Some part of this config possibly can be replaced with:
sourceSets {
val jvmMain by getting {
dependencies {
configurations["liquibaseRuntime"].dependencies.addAll(listOf(
DefaultExternalModuleDependency("org.liquibase", "liquibase-core", null, "default"),
DefaultExternalModuleDependency("org.liquibase.ext", "liquibase-hibernate5", "3.8", "default"),
DefaultExternalModuleDependency("org.postgresql", "postgresql", null, "default"),
DefaultExternalModuleDependency("org.springframework.boot", "spring-boot", "2.2.4.RELEASE", "default")
// DefaultSelfResolvingDependency(configurations["compileClasspath"])
))
I've got stuck with these two and don't know what to do:
liquibaseRuntime(sourceSets.getByName("main").compileClasspath)
liquibaseRuntime(sourceSets.getByName("main").output)
They add instances of the dependency class DefaultSelfResolvingDependency (they also seem to be wrapped with some proxy). Looking through the liquibase-gradle plugin code didn't help.
So, how should I port these two dependencies?
Not familiar with with the Liquibase Gradle plugin. My assumption is you have applied the plugin in the following manner:
plugins {
id("org.liquibase.gradle") version "2.0.2"
}
Then you should be able to do what you have normally:
dependencies {
liquibaseRuntime("org.liquibase:liquibase-core")
liquibaseRuntime("org.liquibase.ext:liquibase-hibernate5:3.8")
liquibaseRuntime("org.postgresql:postgresql")
liquibaseRuntime("org.springframework.boot:spring-boot:$springBootVersion")
}
If for some reason that didn't work out-of-the-box, then you need to help Gradle's Kotlin DSL by explicitly retrieving a reference of the configuration:
val liquibaseRuntime by configurations
dependencies {
liquibaseRuntime("org.liquibase:liquibase-core")
liquibaseRuntime("org.liquibase.ext:liquibase-hibernate5:3.8")
liquibaseRuntime(sourceSets.getByName("main").compileClasspath)
liquibaseRuntime(sourceSets.getByName("main").output)
liquibaseRuntime("org.postgresql:postgresql")
liquibaseRuntime("org.springframework.boot:spring-boot:$springBootVersion")
}
You could also do the following as well:
dependencies {
"liquibaseRuntime"("org.liquibase:liquibase-core")
"liquibaseRuntime"("org.liquibase.ext:liquibase-hibernate5:3.8")
// ...
}
Reference: Understanding what to do when type-safe model accessors are not available
Now these two lines do not make sense to me.
liquibaseRuntime(sourceSets.getByName("main").compileClasspath)
liquibaseRuntime(sourceSets.getByName("main").output)
According to the API documentation for DependencyHandler, there are certain allowed notations. A sourceSet is not one of them. So not sure what to do there.

How to disable default gradle buildType suffix (-release, -debug)

I migrated a 3rd-party tool's gradle.build configs, so it uses android gradle plugin 3.5.3 and gradle 5.4.1.
The build goes all smoothly, but when I'm trying to make an .aab archive, things got broken because the toolchain expects the output .aab file to be named MyApplicationId.aab, but the new gradle defaults to output MyApplicationId-release.aab, with the buildType suffix which wasn't there.
I tried to search for a solution, but documentations about product flavors are mostly about adding suffix. How do I prevent the default "-release" suffix to be added? There wasn't any product flavor blocks in the toolchain's gradle config files.
I realzed that I have to create custom tasks after reading other questions and answers:
How to change the generated filename for App Bundles with Gradle?
Renaming applicationVariants.outputs' outputFileName does not work because those are for .apks.
I'm using Gradle 5.4.1 so my Copy task syntax reference is here.
I don't quite understand where the "app.aab" name string came from, so I defined my own aabFile name string to match my toolchain's output.
I don't care about the source file so it's not deleted by another delete task.
Also my toolchain seems to be removing unknown variables surrounded by "${}" so I had to work around ${buildDir} and ${flavor} by omitting the brackets and using concatenation for proper delimiting.
tasks.whenTaskAdded { task ->
if (task.name.startsWith("bundle")) { // e.g: buildRelease
def renameTaskName = "rename${task.name.capitalize()}Aab" // renameBundleReleaseAab
def flavorSuffix = task.name.substring("bundle".length()).uncapitalize() // "release"
tasks.create(renameTaskName, Copy) {
def path = "$buildDir/outputs/bundle/" + "$flavorSuffix/"
def aabFile = "${android.defaultConfig.applicationId}-" + "$flavorSuffix" + ".aab"
from(path) {
include aabFile
rename aabFile, "${android.defaultConfig.applicationId}.aab"
}
into path
}
task.finalizedBy(renameTaskName)
}
}
As the original answer said: This will add more tasks than necessary, but those tasks will be skipped since they don't match any folder.
e.g.
Task :app:renameBundleReleaseResourcesAab NO-SOURCE

Pass tagTemplate as command line argument - Gradle Release Plugin

I have to create builds depending on environment and tag/push to nexus. Currently, I have tagTemplate = 'release-${version}' in my release settings. The first part of the tagTemplate (release) is what we're using in our pipeline to fire off the builds, which require slightly different args for each.
I need to be able to pass in an argument that will replace 'release' with some other build type.
Ideally, I'd like to be able to just pass in what goes in the 'release' part of the template, so the setting would look like:
tagTemplate = '${tagPrefix}-${version}'
Then I'd be able to run the command:
gradle release -PtagPrefix='build1'
I've tried passing it in like this:
gradle release -PtagTemplate='build1-${version}'
gradle release -Prelease.tagTemplate='build1-${version}'
gradle release -Pproject.release.tagTemplate='build1-${version}'
None of these work.
gradle release -PtagPrefix would be available via roject.findProperty('tagPrefix').
I'm not sure if you can use template strings for the property args as it depends when they are evaluated.
I would recommend either
release {
def tagPrefix = project.findProperty('tagPrefix') != null ? project.findProperty('tagPrefix') : 'default'
tagTemplate = '${tagPrefix}-${version}'
}

Citrus-Cucumber: Can I run a single Cucumber Scenario?

I have a Cucumber feature file with a bunch of Scenarios to execute Citrus integration tests (Citrus-Cucumber integration). I can run all Scenarios at once in IntelliJ or through Maven.
Works perfect, but is it possible to run a single Scenario in IntelliJ or through Maven?
In IntelliJ I found a Cucumber plugin that gives me this option, but after fixing lots of NoClassDefFound errors with dependency tricks, it fails because it does not respect the Citrus project files such as citrus-application.properties.
Answer to myself: Yes, I can.
According to the Cucumber docs, it uses tags to (what a surprise) tag scenarios. Tags begin with the # symbol and one can set any number of tags on a Feature or Scenario.
Example:
#Test123
#Important
Scenario: My awesome Scenario
Given ...
When ...
Then ...
#Test456
Scenario: My other Scenario
Given ...
When ...
Then ...
With these tags in place, I can "select" scenarios to execute based on tags. For example to execute all important Scenarios I use the tag #Important.
cucumber --tags #Important
Since I execute my tests through a Citrus class, I have to pass the tags to execute in the CucumberOptions of my test class.
#RunWith(Cucumber.class)
#CucumberOptions(
strict = true,
glue = { "com.consol.citrus.cucumber.step.runner.core" },
plugin = { "com.consol.citrus.cucumber.CitrusReporter" },
tags = { "#Important" }
)
public class RunCucumberTests {
}
It is not super-convenient, to edit the test class metadata every time, but it works.

How can I create a shortcut task for a single test in Gradle?

In Gradle, I can run a single test from the command line as follows:
gradle -Dtest.single=VeryCriticalTestX test
VeryCriticalTestX is frequently executed alone, and I'd like to provide a more readable and flexible API to my fellow developers. Ideally, they would only need to run
gradle testCritical
without worrying about the test's name. This would also allow me to change the name over time without breaking Jenkins builds.
How do I do achieve this?
Gradle's Test-Tasks can be configured to only include tests matching a given name pattern. You can create a new task testCritical as follows:
task testCritical(type: Test) {
group = 'verification'
description = 'Runs a very critical test'
outputs.upToDateWhen { false }
include('**/VeryCriticalTestX.class')
}
With this, renaming VeryCriticalTestX to something else doesn't break other people's commands or Jenkins jobs. However, there is the risk that someone accidentally disables this task by renaming the VeryCriticalTestX without adapting the task configuration. This can be prevented with the following TaskExecutionListener:
// verify that testCritical is not skipped unexpectedly due to a renamed classfile
// we detect this using Gradle's NO-SOURCE TaskState
gradle.addListener(new TaskExecutionListener() {
void beforeExecute(Task task) {}
void afterExecute(Task task, TaskState state) {
if (checkJooqEnumBindings.equals(task) && state.getNoSource()) {
throw new GradleException("testCritical did not run because it couldn't find VeryCriticalTestX")
}
}
})