Gradle Liquibase plugin not using specified contexts - gradle

We have a Gradle project which uses Liquibase, and our build file has:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.liquibase:liquibase-gradle-plugin:1.1.1'
classpath 'org.liquibase:liquibase-core'
}
}
apply plugin: 'liquibase'
Yet when we try to update our project via:
gradle update -Dliquibase.contexts=foobar
(tried with --contexts too)
it seems to ignore the specified contexts and runs all the changesets instead.
Example:
changeSet(author: 'me', id: 'someId1', context: 'somethingElse') { // This runs, but shouldn't
// ...
changeSet(author: 'me', id: 'someId2', context: 'foobar') { // Should only run this
The project was forked at one point, so maybe we're misinterpreting the documentation (original, new) but it seems like this should work.
Do we need a different plugin/version? Are we calling it wrong?

Derp, looks like you need to grab the contexts manually in your build file:
main {
if (project.hasProperty('contexts')) {
contexts contexts
}
url 'someurl'
username 'username'
password 'pass'
}
Then pass it in via:
gradle update -Pcontexts=schema

You should define your contexts inside task
task('liquibase_dev') << {
liquibase {
activities {
main {
changeLogFile changeLog
url 'jdbc:postgresql://localhost:5432/test'
username 'postgres'
password 'admin'
contexts 'DEV'
}
}
}
}
Liquibase changelog:
<changeSet author="xxx" context="DEV" id="1591257804177-1" ...>
<changeSet author="xxx" context="PROD" id="1591257804177-2" ...>
Then you can run like:
./gradlew :liquibase_dev update
Only the first changeset will be executed

Newer plugin (2.0.3), same problem and slightly different solution, 'cause I'm keeping a default context.
liquibase.gradle (snippet):
if (!project.hasProperty("contexts")) {
project.ext.contexts = "dev"
}
liquibase {
activities {
main {
driver "${datasource_driver}"
url "${datasource_url}"
username "${datasource_username}"
password "${datasource_password}"
changeLogFile "src/main/resources/db/changelog/master.xml"
defaultSchemaName ""
logLevel "info"
contexts contexts
}
}
}
tasks.liquibaseUpdate.dependsOn(tasks.classes)
gradle.properties (snippet):
liquibaseTaskPrefix=liquibase
So, now it's safe to call:
./gradlew liquibaseUpdate # to apply contexts=dev
And:
./gradlew liquibaseUpdate -Pcontexts=test # to apply contexts=test

Related

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

Gradle Liquibase Integration

I am trying to integrate Liquibase in my project using Gradle.
For that I made below changes to build.gradle but it gives me error as follows:
Below is my build.gradle file:
buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.liquibase:liquibase-gradle-plugin:2.0.1"
}
}
allprojects {'
apply plugin: 'liquibase'
}
dependencies {
liquibaseRuntime 'org.liquibase:liquibase-core:3.6.1'
liquibaseRuntime 'org.liquibase:liquibase-groovy-dsl:2.0.1'
liquibaseRuntime 'mysql:mysql-connector-java:5.1.34'
}
liquibase {
activities {
doFirst {
if (!project.hasProperty('runList')) {
project.ext.runList = "main,test"
}
}
main {
defaultsFile "$projectDir/sql-migration/mysql/app_mysql.properties"
logFile "$projectDir/sql-migration/mysql/logs/liquibase-" + new Date().format("yyyy-MM-dd_HH-mm-ss")+".log"
}
test {
defaultsFile "$projectDir/sql-migration/mysql/app_test_mysql.properties"
logFile "$projectDir/sql-migration/logs/liquibase-test-" + new Date().format("yyyy-MM-dd_HH-mm-ss")+".log"
}
}
// To execute liquibase on single environment, gradlew app:update -PrunList=test
runList = project.ext.runList
}
I am not able to get why it is not able to find out 'liquibaseRuntime' repositories.
Any help in this would be really appreciated.
Assuming that you have a changelog file, make sure that the changelog file is properly referenced from build.gradle.
Also I would suggest defining the liquibase.url, username and password.
Refer to this post for detailed implementation:
https://dzone.com/articles/managing-your-database-with-liquibase-and-gradle
The error message
Cannot resolve external dependency [...] because no repositories are defined
indicates that you're missing a repositories block. You have one in your buildscript, but you probably need to move it to the top-level, as shown here.

classes not visible to gradle task swagger when using kongchen swagger-maven-plugin

When performing gradle clean and then gradle swagger a ClassNotFoundException is thrown. If gradle swagger is then run again (basically after the api build is done in previous run), it works fine.
build.gradle looks as below:
buildscript {
repositories {
maven { url hydraMavenRepo }
maven { url hydraPluginsRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
apply plugin: 'java'
configurations {
addclasspath
}
dependencies {
addclasspath files(project(':api:desktop-api').configurations['runtime'].files)
addclasspath files(project(':api:desktop-api').sourceSets['main'].output)
addclasspath files(project(':api:desktop-api').sourceSets.main.output.classesDir)
runtime project(':api:desktop-api')
}
sourceSets {
main {
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output)
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output.classesDir)
runtimeClasspath += files(project(':api:desktop-api').configurations['runtime'].files)
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: [':api:desktop-api:build']) {
doLast {
logger.info 'Swagger GenDoc...'
project.file(reportsDir).mkdirs()
// a trick to have all needed classes in the classpath
def customClassLoader = new GroovyClassLoader()
buildscript.configurations.classpath.each {
//println it.toURI().toURL()
customClassLoader.addURL(it.toURI().toURL())
}
configurations.addclasspath.each {
customClassLoader.addURL(it.toURI().toURL())
}
// the same settings as in the swagger-maven-example/pom.xml
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, customClassLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['com/vmware/vdi/hydra'],
schemes: ['http', 'https'],
host: 'vmware.com',
basePath: '/api',
info: new Info(
title: "Hydra DS-REST API's",
version: 'v100',
description: "Hydra DS-REST API's",
),
swaggerDirectory: reportsDir
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}
You have several flaws in your build script.
You should not depend on built things in build script dependencies. This is a hen and egg problem. You need to execute the build to get the classes that are necessary to execute the build. This cannot work reliably, if at all.
Instead you should declare them as dependencies outside the buildscript block. The buildscript block is only for dependencies that are needed by your build script to run itself, like Gradle Tasks and Gradle Plugins or classes you use during the build, like the swagger-maven-plugin stuff which is correct in the buildscript block.
Besides that, you execute part of your swagger stuff (the instanciation, execution and printing) during the configuration phase instead of during the execution phase. Everything you do in a task closure, but outside any doFirst or doLast blocks is run during the configuration phase, when the build is configured and thus always, no matter what tasks you actually want to execute and no matter whether the task may already be up-to-date or not. For the up-to-date check to work and save your time you need to declare all inputs like files and properties that might change between executions and all outputs that you generate, then Gradle can do its magic to only execute the task when actually necessary.
Also you should not use println in build scripts. That is like using System.out.println in Java programs. Instead you should use the provided logging facility directly, e. g. doing logger.info 'Swagger GenDoc task is completed'.
buildscript.classloader is what I was looking for.
Below is the code that works:
buildscript {
repositories {
maven { url mavenRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: ':api:build') {
doLast {
logger.info 'Swagger GenDoc...'
project.file(<dir>).mkdirs()
FileCollection apiRuntimeFiles = files(project(':api').configurations['runtime'].files)
apiRuntimeFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
FileCollection apiClassFiles =files(project(':api').sourceSets['main'].output)
apiClassFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, buildscript.classLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['<loc>'],
schemes: ['http', 'https'],
host: '<host>',
basePath: '/api',
info: new Info(
title: "REST API's",
version: 'v1',
description: "REST API's",
),
swaggerDirectory: <dir>
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}

Passing parameter and value to Gradle liquibase plugin

I'm using gradle liquibase plugin (https://github.com/liquibase/liquibase-gradle-plugin) but I don't understand how to pass parameters and values.
I want build a rollback based on tag and date but I'm only able to rollback on a tag with this command:
gradle rollback -DliquibaseTag=value
I don't understand how I should pass data, tag or count.
My grade.build is very simple:
apply plugin: 'liquibase'
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath 'org.liquibase:liquibase-gradle-plugin:1.1.0'
classpath 'com.h2database:h2:1.3.160'
}
}
liquibase {
activities {
main {
changeLogFile 'changelog.xml'
url 'jdbc:h2:db/liquibase_workshop'
username 'sa'
password ''
}
}
runList = 'main'
}
It looks like the syntax may be a little different than documented. Instead try something like:
gradle rollbackCount -PliquibaseCommandValue=1

Publishing custom artifact built from task in gradle

I am having some issues trying to create a task that build a special file which is then uploaded to artifactory.
Heres a simplified version:
apply plugin: 'maven-publish'
task myTask {
ext.destination = file('myfile')
doLast {
// Here it should build the file
}
}
publishing {
repositories {
maven {
name 'ArtifactoryDevDirectory'
url 'http://artifactory/artifactory/repo-dev'
credentials {
username 'username'
password 'password'
}
}
}
publications {
MyJar(MavenPublication) {
artifactId "test"
version "1.0"
groupId "org.example"
artifact myTask.destination
}
}
}
This works, except that gradle publish does not run myTask. I tried adding
publishMyJarPublicationToArtifactoryDevDirectoryRepository.dependsOn myTask
but i just get:
Could not find property 'publishMyJarPublicationToArtifactoryDevDirectoryRepository' on root project 'test'.
I tried messing about with the artifact, adding a custom artifact and configuration and publishing that instead but that did not work either.
Any help would be greatly appreciated.
afterEvaluate {
publishMyJarPublicationToArtifactoryDevDirectoryRepository.dependsOn myTask
}
Accomplishes what I want.

Resources