How to create custom build step based on existing one in TeamCity Kotlin DSL? - kotlin

I use TeamCity Kotlin DSL 2018.1 to set up build configuration. My settings.kts file looks like this:
version = "2018.1"
project {
buildType {
id("some-id")
name = "name"
steps {
ant {
name = "Step1"
targets = "target1"
mode = antFile { path = "/some/path" }
workingDir = "/some/dir"
jdkHome = "some_jdk"
}
ant {
name = "Step2"
targets = "target2"
mode = antFile { path = "/some/path" }
workingDir = "/some/dir"
jdkHome = "some_jdk"
}
...
}
}
}
It works as expected, but I want to avoid writing the same repeating parameters for every step over and over again.
I tried to write function, which would construct build step pre-filled with default values:
fun customAnt(init: AntBuildStep.() -> kotlin.Unit): AntBuildStep {
val ant_file = AntBuildStep.Mode.AntFile()
ant_file.path = "/some/path"
val ant = AntBuildStep()
ant.mode = ant_file
ant.workingDir = "/some/dir"
ant.jdkHome = "some_jdk"
return ant
}
project {
buildType {
id("some-id")
name = "name"
steps {
customAnt {
name = "Step1"
targets = "target1"
}
customAnt {
name = "Step2"
targets = "target2"
}
...
}
}
}
It compiles but doesn't work: TeamCity just ignores build steps, defined in this way.
Unfortunately, official documentation doesn't contain any information about customizing and extending DSL. Probably, I'm doing something wrong with Kotlin's () -> Unit construction, but can't find out what exactly is wrong.

I got it.
Actually, I was close. The following code works just as I wanted:
version = "2018.1"
fun BuildSteps.customAnt(init: AntBuildStep.() -> Unit): AntBuildStep {
val ant_file = AntBuildStep.Mode.AntFile()
ant_file.path = "/some/path"
val result = AntBuildStep(init)
result.mode = ant_file
result.workingDir = "/some/dir"
result.jdkHome = "some_jdk"
step(result)
return result
}
project {
buildType {
steps {
customAnt {
name = "step1"
targets = "target1"
}
customAnt {
name = "step2"
targets = "target2"
}
...
}
}
}

Related

Error publishing to Sonatype because "repositoryUrl" is null

I've been trying to publish a project to Sonatype's repository, but I got an error and I can't find the solution online.
I have the following publishing block on my build.gradle.kts file:
publishing {
publications {
create<MavenPublication>("mavenJava") {
pom {
name.set("Keen")
description.set("A genetic algorithm framework for Kotlin")
url.set("https://github.com/r8vnhill/keen")
licenses {
license {
name.set("Attribution 4.0 International (CC BY 4.0)")
url.set("https://creativecommons.org/licenses/by/4.0/legalcode")
}
}
}
groupId = "cl.ravenhill"
artifactId = "keen"
version = projectVersion
from(components["java"])
}
}
repositories {
maven {
name = "sonatype"
if (version.toString().endsWith("SNAPSHOT")) {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "ossrh"
credentials(PasswordCredentials::class)
}
} else {
maven("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") {
name = "ossrh"
credentials(PasswordCredentials::class)
}
}
}
}
}
And the error I've got:
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':publishMavenJavaPublicationToSonatypeRepository'.
> Failed to publish publication 'mavenJava' to repository 'sonatype'
> Cannot invoke "java.net.URI.getScheme()" because "repositoryUrl" is null
Any idea of what could be happening?
The repository called 'sonatype' doesn't have a URL.
Assuming the version is a snapshot version, e.g. 1.2.3-SNAPSHOT, what you've written is
repositories {
maven {
name = "sonatype"
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "ossrh"
credentials(PasswordCredentials::class)
}
}
}
This is also equivalent to
repositories {
maven {
name = "sonatype"
}
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "ossrh"
credentials(PasswordCredentials::class)
}
}
So repository 'sonatype' has a name, but doesn't have a URL.
Gradle will create a publication task for each defined repository, even if the definition isn't valid (which can't be known ahead of time). If you ran the Gradle task
./gradlew publishMavenJavaPublicationToOssrhRepository
it would succeed.
To correct the problem, remove the 'sonatype' repository. It would also be more clear if you defined the target URL in a separate variable.
val ossrhRepositoryUrl = if (version.toString().endsWith("SNAPSHOT")) {
"https://s01.oss.sonatype.org/content/repositories/snapshots/"
} else {
"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
}
publishing {
repositories {
maven(ossrhRepositoryUrl) {
name = "ossrh"
credentials(PasswordCredentials::class)
}
}
}

gradle publishing not working - No such Algorithm

I have a nexus repository that I want to publish my gradles versions catalog to. Here's the gradle build script:
buildscript {
dependencies {
classpath("com.android.tools.build:gradle:7.2.0")
}
}
plugins {
id ("version-catalog")
id ("maven-publish")
}
catalog {
versionCatalog {
from(files("gradle/libs.versions.toml"))
}
}
publishing {
publications {
create<MavenPublication>("VersionCatalog") {
groupId = "com.phinneyridge"
artifactId = "com.phinneyridge.version.catalog"
version = "1.0.0"
from(components["versionCatalog"])
}
repositories {
maven {
name = "PhinneyRidgeRepository"
url = uri(System.getenv()["PhinneyRidgeReleaseRepoUrl"].toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
println(url)
println(credentials.username + ":" + credentials.password)
}
}
}
}
Here's the output I get when I try to publish:
gradlew publish
Type-safe dependency accessors is an incubating feature.
Configure project :
https://ridgetop/nexus/repository/maven-releases/
publisher:mavenpublisher
Task :publishVersionCatalogPublicationToPhinneyRidgeRepositoryRepository FAILED
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':publishVersionCatalogPublicationToPhinneyRidgeRepositoryRepository'.
Failed to publish publication 'VersionCatalog' to repository 'PhinneyRidgeRepository'
java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.security.ssl.
SSLContextImpl$DefaultSSLContext)
If I changed the maven repository to mavenLocal(), the script actually publishes the version catalog to the local maven repository just fine.
What's also interesting, is that I have a custom gradle plugin in a different build script and it fairly well the same looking code pattern, and it successfully publishes the custom gradle plugin to same repository. Here's the code for that working script:
plugins {
id ("java-gradle-plugin")
"java-library"
id ("maven-publish")
id("com.gradle.plugin-publish") version "0.20.0"
id("org.jetbrains.kotlin.jvm") version "1.6.20"
id("org.gradle.kotlin.kotlin-dsl") version "2.1.7"
}
gradlePlugin {
group = "com.phinneyridge"
plugins {
create("PhinneyRidgePlugin") {
id = "com.phinneyridge.project"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeProjectPlugin"
}
create("PhinneyRidgeAndroidAppPlugin") {
id = "com.phinneyridge.android.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidAppPlugin"
}
create("PhinneyRidgeAndroidLibPlugin") {
id = "com.phinneyridge.android.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidLibPlugin"
}
create("PhinneyRidgeAndroidKotlinAppPlugin") {
id = "com.phinneyridge.android.kotlin.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidKotlinAppPlugin"
}
create("PhinneyRidgeAndroidKotlinLibPlugin") {
id = "com.phinneyridge.android.kotlin.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeAndroidKotlinLibPlugin"
}
create("PhinneyRidgeJavaAppPlugin") {
id = "com.phinneyridge.java.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaAppPlugin"
}
create("PhinneyRidgeJavaLibPlugin") {
id = "com.phinneyridge.java.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaLibPlugin"
}
create("PhinneyRidgeJavaKotlinAppPlugin") {
id = "com.phinneyridge.java.kotlin.application"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaKotlinAppPlugin"
}
create("PhinneyRidgeJavaKotlinLibPlugin") {
id = "com.phinneyridge.java.kotlin.library"
implementationClass = "com.phinneyridge.android.gradle.PhinneyRidgeJavaKotlinLibPlugin"
}
create("PhinneyRidgeDSAnnotations") {
id = "com.phinneyridge.dsannotations"
implementationClass = "com.phinneyridge.osgi.DSAnnotations"
}
create("Aar2Jar") {
id = "com.phinneyridge.aar2jar"
implementationClass = "com.phinneyridge.android.Aar2Jar"
}
}
}
group = "com.phinneyridge"
version = "1.0.0"
publishing {
repositories {
maven {
url = uri(System.getenv().get("PhinneyRidgeReleaseRepoUrl").toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
}
}
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
gradleApi();
implementation ("com.android.tools.build:gradle:7.1.3")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20")
}
There's some differences, but the maven repository is constructed exactly the same. So the question is why am I getting the NoSuchAlgorithm error when I try to publish the version catalog?
If I try publishing with stacktrace I see the following:
Caused by: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: sun.securit
y.ssl.SSLContextImpl$DefaultSSLContext)
at org.gradle.internal.resource.transport.http.HttpClientConfigurer.jdkSupportsTLSProtocol(HttpClientConfigurer.java:115)
And here's what jdkSupportTLSProtocol looks like:
private static boolean jdkSupportsTLSProtocol(#SuppressWarnings("SameParameterValue") final String protocol) {
try {
for (String supportedProtocol : SSLContext.getDefault().getSupportedSSLParameters().getProtocols()) {
if (protocol.equals(supportedProtocol)) {
return true;
}
}
return false;
} catch (NoSuchAlgorithmException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
}
It's the SSLContext.getDefault() that throws the NoSuchAlgorithException. I'm just currently at a lost understand what I need to change to get around this problem.
I figured out a solution to the problem.
I added a line of code to the maven repository declaration:
maven {
javax.net.ssl.SSLContext.setDefault(javax.net.ssl.SSLContext.getInstance("default"))
name = "PhinneyRidgeRepository"
url = uri(System.getenv()["PhinneyRidgeReleaseRepoUrl"].toString())
credentials {
username = System.getenv().get("PhinneyRidgeRepoUser").toString()
password = System.getenv().get("PhinneyRidgeRepoUserPW").toString()
}
println(url)
println(credentials.username + ":" + credentials.password)
}
}
That solved the problem, but I don't have an explanation why I had to set the SSLContext in one situation and not the other. According to the gradle documentation SSLContext.getDefault() is supposeto automagically call SSLContext.getInstance("Default") when the SSLContext is not initialized. (By the way the instance name seems to be case insensitive, so "default" or "Default" both worked.) My speculation is that this is a gradle bug, probably because the publication in one case is from component. That's the only obvious difference that I see that sets the two contrasting situations apart. By the way SSLContext.getInstance("TLSv1.2") would not work; I had to use "default"

Flyway in Kotlin Gradle - cannot import FlywayMigrateTask

I need to run two different flyway migrations on two different shcemas, each with their own user account.
According to the Flyway documentation I only need to set up one custom task for each connection with FlywayMigrateTask. From official documentation using default (Groovy) gradle:
task migrateDatabase1(type: org.flywaydb.gradle.task.FlywayMigrateTask) {
url = 'jdbc:h2:mem:mydb1'
user = 'myUsr1'
password = 'mySecretPwd1'
}
task migrateDatabase2(type: org.flywaydb.gradle.task.FlywayMigrateTask) {
url = 'jdbc:h2:mem:mydb2'
user = 'myUsr2'
password = 'mySecretPwd2'
}
I try to do this in Kotlin gradle (kts) but my project can't resolve the refrence to FlywayMigrateTask, even though I have it in my External Libraties:
How I register task:
tasks.register(
"flywayTesting",
type = org.flywaydb.gradle.task.FlywayMigrateTask::class.java) {
// Details hidden
}
I'm able to resolve org.flywaydb.gradle.task package but not the FlywayMigrateTask class in the task folder.
What am I doing wrong?
Highligts from my gradle setup:
plugins {
kotlin("jvm") version "1.6.0"
id("org.flywaydb.flyway") version "8.5.1"
}
repositories {
mavenCentral()
}
tasks.withType<Wrapper> {
gradleVersion = "6.8.2"
distributionType = Wrapper.DistributionType.BIN
}
dependencies {
implementation("org.flywaydb:flyway-core:8.5.1")
implementation("org.flywaydb:flyway-gradle-plugin:8.5.0")
}
kotlin {
sourceSets["main"].apply {
kotlin.srcDir("src/main/kotlin")
}
}
task<org.flywaydb.gradle.task.FlywayMigrateTask>("migrateDatabase1") {
url = 'jdbc:h2:mem:mydb1'
user = 'myUsr1'
password = 'mySecretPwd1'
}
task<org.flywaydb.gradle.task.FlywayMigrateTask>("migrateDatabase2") {
url = 'jdbc:h2:mem:mydb2'
user = 'myUsr2'
password = 'mySecretPwd2'
}

How to get publish to wait on AAR file to exist before trying to publish?

Problem Statment
I am trying to create a build and publish a library based on properties I pass in through command line. I would like to use :
./gradlew clean build publish -PnexusUsername=* -PnexusPassword=* -PmajorVersion=x -PminorVersion=x -PbuildNumber=x
Work Around
My current work around for this is I use
./gradlew clean; ./gradlew build; ./gradlew publish -PnexusUsername=* -PnexusPassword=* -PmajorVersion=x -PminorVersion=x -PbuildNumber=x
Below you will find my main build gradle. I have tried many ideas of waiting for assembleRelease to be added and publishing being finalized by it, but alas I am not a gradle expert, so I am asking for help. I have two POJO/POKO libraries and one Android Library that I am publishing out as an AAR. The error I receive is that the POM file cannot be made because the AAR does not exist.
import CiCdDependencies.BOSS_SDK_SNAPSHOT
import CiCdDependencies.buildNumber
import CiCdDependencies.majorVersion
import CiCdDependencies.minorVersion
import CiCdDependencies.nexusPassword
import CiCdDependencies.NEXUS_URL
import CiCdDependencies.nexusUserName
import CiCdDependencies.productVersion
import CiCdDependencies.VERSION_NAME
import Coverage.androidCoverage
import Coverage.testCoverage
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.0.4")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0")
classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3")
}
}
plugins {
id("java")
id("maven-publish")
id("org.sonarqube").version("3.3")
}
ext {
// Major / Minor / Release and Build numbering
productVersion = (project.findProperty("productVersion") ?: (System.getenv("PRODUCT_VERSION")
?: "0")).toString()
majorVersion =
(project.findProperty("majorVersion") ?: (System.getenv("MAJOR_VERSION") ?: "0")).toString()
minorVersion =
(project.findProperty("minorVersion") ?: (System.getenv("MINOR_VERSION") ?: "0")).toString()
buildNumber =
(project.findProperty("buildNumber") ?: (System.getenv("BUILD_NUMBER") ?: "0")).toString()
//for publishing
nexusUserName =
(project.findProperty("nexusUsername") ?: (System.getenv("NEXUS_USERNAME"))).toString()
nexusPassword =
(project.findProperty("nexusPassword") ?: (System.getenv("NEXUS_PASSWORD"))).toString()
testCoverage = project.hasProperty("TEST_COVERAGE")
androidCoverage = project.hasProperty("ANDROID_COVERAGE")
}
description = "MAFIA BOSS SDK (Kotlin)"
allprojects {
apply {
plugin("jacoco")
}
// Read only repositories for dependencies; this should never be used to publish
repositories {
google()
mavenCentral()
maven {
name = "DI2E-BossSdkReleases"
setUrl("$NEXUS_URL$BOSS_SDK_RELEASES")
credentials {
username = nexusUserName
password = nexusPassword
}
}
maven {
name = "DI2E-BossSdkSnapshots"
setUrl("$NEXUS_URL$BOSS_SDK_SNAPSHOT")
credentials {
username = nexusUserName
password = nexusPassword
}
}
}
}
sonarqube {
properties {
val gitBranch = "git rev-parse --abbrev-ref HEAD".runCommand()
property("sonar.sourceEncoding", "UTF-8")
property("sonar.host.url", "https://sonarqube.di2e.net")
property("sonar.projectVersion", gitBranch)
property("sonar.projectKey", "MAFIA-KotlinSDK")
property("sonar.login", "c58feafd2a81797dec0361dd2d8758885e4145d7")
property("sonar.junit.reportPaths", "**/test-results/**/*.xml")
property("sonar.coverage.jacoco.xmlReportPaths", "**/reports/jacoco/jacocoTestReport/jacocoTestReport.xml")
}
}
tasks.getByName("publish").dependsOn("build")
/**
* Publishing configuration
*/
afterEvaluate {
publishing {
logger.lifecycle("Publishing ${VERSION_NAME()}")
publications {
val isSnapshot = !project.hasProperty("RELEASE")
create<MavenPublication>("BOSSSDKJAVA") {
groupId = "BOSS"
artifactId = "SDK-KOTLIN"
version = if (isSnapshot) {
"${VERSION_NAME()}-SNAPSHOT"
} else {
VERSION_NAME()
}
// The interfaces JAR
artifact("${rootProject.projectDir}/InfrastructureBase/build/libs/InfrastructureBase.jar") {
classifier = "InfrastructureBase"
}
// The implementations JAR
artifact("${rootProject.projectDir}/InfrastructureImpl/build/libs/InfrastructureImpl.jar") {
classifier = "InfrastructureImpl"
}
}
create<MavenPublication>("BOSSSDKANDROID") {
groupId = "BOSS"
artifactId = "SDK-ANDROID"
version = if (isSnapshot) {
"${VERSION_NAME()}-SNAPSHOT"
} else {
VERSION_NAME()
}
// The implementations AAR
artifact("${rootProject.projectDir}/InfrastructureAndroidImpl/build/outputs/aar/InfrastructureAndroidImpl-release.aar") {
classifier = "InfrastructureAndroidImpl"
}
}
repositories {
maven {
url = if (isSnapshot) {
logger.lifecycle("Performing snapshot build.")
uri("$NEXUS_URL$BOSS_SDK_SNAPSHOT")
} else {
logger.lifecycle("Performing release build.")
uri("$NEXUS_URL$BOSS_SDK_RELEASES")
}
credentials {
username = nexusUserName
password = nexusPassword
}
}
}
}
}
}
afterEvaluate {
tasks.getByName("jacocoTestReport", type = JacocoReport::class) {
reports {
print("I am reporting" )
xml.required.set(testCoverage)
html.required.set(testCoverage)
}
val fileFilter = mutableSetOf(
"**/R.class",
"**/R\$*.class",
"**/BuildConfig.*",
"**/Manifest*.*",
"**/*Test*.*",
"android/**/*.*",
"**/*\$Lambda$*.*", // Jacoco can not handle several "$" in class name.
"**/*\$inlined$*.*" // Kotlin specific, Jacoco can not handle several "$" in class name.
)
val debugTree = fileTree("${buildDir}/tmp/kotlin-classes/debug") {
exclude(fileFilter)
}
val mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories.from(files(mainSrc))
classDirectories.setFrom(debugTree)
if (androidCoverage) {
executionData.setFrom(fileTree("$buildDir") {
include(
setOf(
"jacoco/*.exec",
"outputs/code-coverage/debugAndroidTest/connected/*coverage.ec",
"outputs/unit_test_code_coverage/*.exec"
)
)
})
} else if (testCoverage) {
executionData.setFrom(fileTree("$buildDir") {
include(
setOf(
"jacoco/*.exec",
"outputs/unit_test_code_coverage/*.exec"
)
)
})
}
configurations.all {
resolutionStrategy {
eachDependency {
if ("org.jacoco" == requested.group) {
useVersion("0.8.7")
}
}
}
}
}
}
tasks.getByName("sonarqube"){
dependsOn(tasks.getByName("jacocoTestReport"))
}
fun String.runCommand(currentWorkingDir: File = file("./")): String {
val byteOut = getByteOutput()
project.exec {
workingDir = currentWorkingDir
commandLine = this#runCommand.split("\\s".toRegex())
standardOutput = byteOut
}
return String(byteOut.toByteArray()).trim()
}```
I believe AGP has done something to make it easier, check it here: https://developer.android.com/studio/build/maven-publish-plugin
release(MavenPublication) {
// Applies the component for the release build variant.
from components.release
// You can then customize attributes of the publication as shown below.
groupId = 'com.example.MyLibrary'
artifactId = 'final'
version = '1.0'
}
The Android Gradle plugin creates a component for each build variant artifact in your app or library module that you can use to customize a publication to a Maven repository.
And you may need to move the android part to that android library's build.gradle(.kts).

Is it possible to name play evolution sql scripts?

evolution sql scripts should be named
1.sql,
2.sql,
XXX.sql
according to documentation.
https://www.playframework.com/documentation/2.5.x/Evolutions#Running-evolutions-using-compile-time-DI
Is there any possibility to change naming convention to
1_create__customers.sql
2_create__orders.sql
3_alter__customers__add__home_address.sql
like it RoR:
http://edgeguides.rubyonrails.org/active_record_migrations.html#creating-a-standalone-migration
Yes, I evaluated https://github.com/flyway/flyway-play
and we can switch to it.
Right now I'm looking for default play functionality.
Yes, it's possible.
Create a new file at this path: your/modules/folder/EvolutionsModule.scala
Then, add the following:
package your.modules.folder
import javax.inject._
import play.api.db.evolutions._
import play.api.inject.{ Module }
import play.api.libs.Collections
import play.api.{ Configuration, Environment }
import org.apache.commons.io.FileUtils
class EvolutionsModule extends Module {
def bindings(environment: Environment, configuration: Configuration) = {
Seq(
bind[EvolutionsConfig].toProvider[DefaultEvolutionsConfigParser],
bind[EvolutionsReader].to[YourEvolutionsReader],
bind[EvolutionsApi].to[DefaultEvolutionsApi],
bind[ApplicationEvolutions].toProvider[ApplicationEvolutionsProvider].eagerly
)
}
}
#Singleton
class YourEvolutionsReader #Inject()(environment: Environment) extends EvolutionsReader {
def evolutions(db: String): Seq[Evolution] = {
val upsMarker = """^#.*!Ups.*$""".r
val downsMarker = """^#.*!Downs.*$""".r
val UPS = "UPS"
val DOWNS = "DOWNS"
val UNKNOWN = "UNKNOWN"
val mapUpsAndDowns: PartialFunction[String, String] = {
case upsMarker() => UPS
case downsMarker() => DOWNS
case _ => UNKNOWN
}
val isMarker: PartialFunction[String, Boolean] = {
case upsMarker() => true
case downsMarker() => true
case _ => false
}
val folder = environment.getFile(Evolutions.directoryName(db))
val sqlFiles = folder.listFiles()
.filter(file => file.getName.indexOf(".sql") > -1)
.sortBy(file => {
val fileName = file.getName
val nameAfterSqlNumber = fileName.split("\\.")(0).split("_").drop(1).mkString("") + ".sql"
val sqlNumber = fileName.split("\\.")(0).split("_")(0).toInt
val newPrefix = "%07d".format(sqlNumber)
newPrefix + nameAfterSqlNumber
})
.toSeq
sqlFiles.zip(1 to sqlFiles.size)
.map {
case (file, revision) => {
val script = FileUtils.readFileToString(file)
val parsed = Collections.unfoldLeft(("", script.split('\n').toList.map(_.trim))) {
case (_, Nil) => None
case (context, lines) => {
val (some, next) = lines.span(l => !isMarker(l))
Some((next.headOption.map(c => (mapUpsAndDowns(c), next.tail)).getOrElse("" -> Nil),
context -> some.mkString("\n")))
}
}.reverse.drop(1).groupBy(i => i._1).mapValues { _.map(_._2).mkString("\n").trim }
Evolution(
revision,
parsed.getOrElse(UPS, ""),
parsed.getOrElse(DOWNS, "")
)
}
}
}
}
Go to your applications.conf, then add these lines. This is for replacing the default EvolutionsModule with our very own.
play.modules {
enabled += "your.modules.folder.EvolutionsModule"
disabled += "play.api.db.evolutions.EvolutionsModule"
}
Explanation for the first block of text:
According to the source code, evolutions are added solely based on a single class and method - the EvolutionsReader and the method evolutions.
After running the evolutions method, the evolutions module will rely on the database entries generated. So, we only need to override this method with our own.
Despite the huge block of code, it's actually a minor edit of the source code. I replaced the functionality of the loadResource function with the list of files inside the evolutions sql files folder. The rest of it is a straight-up copy of the evolutions method in EvolutionsApi.scala
The source codes quoted by this answer are most likely utility functions - the Evolutions module doesn't actually rely on it. I couldn't find any significant usage of fileName and resourceName for running the evolutions. Instead, they're all referencing the play_evolutions table.
Reference: https://github.com/playframework/playframework/issues/6919
No, it's not possible, the source code:
def fileName(db: String, revision: Int): String = s"${directoryName(db)}/${revision}.sql"
https://github.com/playframework/playframework/blob/2.5.x/framework/src/play-jdbc-evolutions/src/main/scala/play/api/db/evolutions/Evolutions.scala#L103
def resourceName(db: String, revision: Int): String = s"evolutions/${db}/${revision}.sql"
https://github.com/playframework/playframework/blob/2.5.x/framework/src/play-jdbc-evolutions/src/main/scala/play/api/db/evolutions/Evolutions.scala#L108
#Singleton
class EnvironmentEvolutionsReader #Inject() (environment: Environment) extends ResourceEvolutionsReader {
def loadResource(db: String, revision: Int) = {
environment.getExistingFile(Evolutions.fileName(db, revision)).map(new FileInputStream(_)).orElse {
environment.resourceAsStream(Evolutions.resourceName(db, revision))
}
}
}
https://github.com/playframework/playframework/blob/2.5.x/framework/src/play-jdbc-evolutions/src/main/scala/play/api/db/evolutions/EvolutionsApi.scala#L471-L479
So, as it describet in the documentation, you can only use the number {revision}.sql