Skip to content

Auto-exclude Spring Boot LiquibaseAutoConfiguration when Database Migration Plugin is present#15405

Open
jamesfredley wants to merge 2 commits intoapache:7.0.xfrom
jamesfredley:fix/liquibase-autoconfig-exclusion
Open

Auto-exclude Spring Boot LiquibaseAutoConfiguration when Database Migration Plugin is present#15405
jamesfredley wants to merge 2 commits intoapache:7.0.xfrom
jamesfredley:fix/liquibase-autoconfig-exclusion

Conversation

@jamesfredley
Copy link
Contributor

@jamesfredley jamesfredley commented Feb 17, 2026

Summary

Automatically excludes Spring Boot's LiquibaseAutoConfiguration when the Database Migration Plugin is on the classpath, using Grails' existing AST transformation in ApplicationClassInjector. This eliminates the need for users to manually add spring.liquibase.enabled: false to their configuration.

Bug Description

When a Grails application uses the Database Migration Plugin, Spring Boot's LiquibaseAutoConfiguration creates its own SpringLiquibase bean that conflicts with the plugin's own Liquibase lifecycle management in DatabaseMigrationGrailsPlugin.doWithApplicationContext(). This causes:

  • Duplicate changelog execution attempts
  • Lock contention on DATABASECHANGELOGLOCK
  • Startup failures or unexpected behavior

Users must currently add this workaround to application.yml:

spring:
  liquibase:
    enabled: false

Fix

Added a conditional exclusion to ApplicationClassInjector - the same AST transformation that already excludes DataSourceAutoConfiguration, ReactorAutoConfiguration, and HibernateJpaAutoConfiguration. When DatabaseMigrationGrailsPlugin is detected on the classpath via ClassUtils.isPresent(), LiquibaseAutoConfiguration is added to @EnableAutoConfiguration(excludeName=...) at compile time.

This is a single-mechanism approach that hooks into existing Grails infrastructure rather than adding new runtime components.

Opt-out

To disable the automatic exclusion, add the following to your project's gradle.properties:

systemProp.grails.dbmigration.excludeLiquibaseAutoConfiguration=false

This system property is read at compile time during the AST transformation. When set to false, the exclusion is not added and Spring Boot's Liquibase auto-configuration will run alongside the plugin.

Files Changed

  • grails-core/.../ApplicationClassInjector.groovy - Added CONDITIONAL_EXCLUSIONS list and conditional exclusion logic with system property opt-out
  • grails-core/.../ApplicationClassInjectorSpec.groovy - 6 new Spock tests
  • grails-doc/.../automaticDatabaseMigration.adoc - Documentation for the auto-exclusion and opt-out
  • grails-data-hibernate5/dbmigration/README.md - Documentation for the auto-exclusion and opt-out

Test Coverage

All 6 tests pass:

Test Verifies
EXCLUDED_AUTO_CONFIGURE_CLASSES contains expected entries Existing exclusions intact
CONDITIONAL_EXCLUSIONS contains LiquibaseAutoConfiguration entry Correct plugin class, exclude class, and system property
system property defaults to true (exclusion enabled) Default behavior is to exclude
system property set to false disables exclusion Opt-out mechanism works
shouldInject returns false for null URL Edge case
artefact types contains Application Injector configuration

Example Application

https://github.com/jamesfredley/grails-liquibase-autoconfig-conflict

Environment Information

  • Grails: 7.0.7
  • Spring Boot: 3.5.10
  • Groovy: 4.0.30
  • Liquibase: 4.27.0 (managed by BOM)
  • JDK: 17+

Copilot AI review requested due to automatic review settings February 17, 2026 16:26
@jdaugherty
Copy link
Contributor

There's already a built in way to exclude problematic autoconfiguration via the Application AST transform. Since there's a way to do this already, shoudln't we adopt the "grails way"?

@jamesfredley jamesfredley self-assigned this Feb 17, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jdaugherty
Copy link
Contributor

Wait, I just looked at the referenced spring security config - this PR makes it sound like we agreed on this approach and this PR is just following that approach. But that approach doesn't appear to be existing; it's new via https://github.com/apache/grails-spring-security/pull/1205/changes

As an optional, external plugin I understand that it needs to implement the exclusion logic, but we've approached the Grails autoconfiguration exclusion differently in core. We need to decide on one way and not adopt multiple ways.

ApplicationClassInjector is the class that has built in excludes. The database plugin should be added here if it's present on the classpath.

@matrei what are your thoughts?

@jamesfredley jamesfredley marked this pull request as draft February 17, 2026 19:59
@jamesfredley
Copy link
Contributor Author

jamesfredley commented Feb 19, 2026

The readme is where the spring security config suggestion currently lives: https://github.com/apache/grails-spring-security/blame/7.0.x/README.md#L52. https://github.com/apache/grails-spring-security/pull/1205/changes suggests making it automatic instead of manual.

@jamesfredley
Copy link
Contributor Author

@jdaugherty I agree this works, it just feels a bit odd doing it in grails-core for an optional (although often used) plugin

if (ClassUtils.isPresent('org.grails.plugins.databasemigration.DatabaseMigrationGrailsPlugin', classLoader)) {
    GrailsASTUtils.addExpressionToAnnotationMember(enableAutoConfigAnnotation, 'excludeName', 
        constX('org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration'))
}

@jdaugherty
Copy link
Contributor

I think ti's reasonable for anything in core to use the core accepted solution. For independent projects, it doesn't make sense to make the change in core. I just mostly want to have 1 way of doing this instead of multiple in grails-core.

@jamesfredley
Copy link
Contributor Author

Ok, I will switch it out.

Replace the runtime AutoConfigurationImportFilter approach with a
compile-time AST transformation in ApplicationClassInjector. When
DatabaseMigrationGrailsPlugin is on the classpath, the exclusion is
added to @EnableAutoConfiguration(excludeName=...) during compilation -
the same mechanism already used for DataSourceAutoConfiguration,
ReactorAutoConfiguration, and HibernateJpaAutoConfiguration.

Opt-out via system property in gradle.properties:
  systemProp.grails.dbmigration.excludeLiquibaseAutoConfiguration=false

Assisted-by: Claude Code <Claude@Claude.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants

Comments