Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"cSpell.words": [
"gradleup",
"Gratatouille",
"Liquibase",
"Multiplatform",
"nmcp",
"OSSRH",
Expand Down
4 changes: 4 additions & 0 deletions docs/plugin-development/configuration-cache/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ in the main documentation:
- [Auto-testing configuration Cache compatibility](https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:testing)


## Examples and Case Studies

- [Enabling Gradle Configuration Cache Support in the Liquibase Plugin](./example-config-cache-liquibase-plugin.md)

## FAQ

### How to fix "invocation of 'Task.project' at execution time is unsupported"?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Example - Enabling Gradle Configuration Cache Support in the Liquibase Plguin
---
title: Case Study - Enabling Gradle Configuration Cache Support in the Liquibase Plugin
description: >
This page outlines the changes made to the Liquibase Gradle plugin to add support for Gradle's Configuration Cache.
---

## 1. Summary

Expand All @@ -17,19 +21,24 @@ For the cache to work, a critical rule must be followed: **tasks must not access
The previous implementation of the plugin violated this rule in several ways:

* **Direct Project Access in `LiquibaseTask`:** The main task action (`@TaskAction`) directly accessed the project's `liquibase` extension to get its configuration at execution time.

```groovy
// Violation inside LiquibaseTask's execution logic:
def activities = project.liquibase.activities
def runList = project.liquibase.runList
jvmArgs(project.liquibase.jvmArgs)
```

* **Project Dependency in `ArgumentBuilder`:** The `ArgumentBuilder` class, used during task execution, held a reference to the `project` and used it to access project properties, the logger, and the build directory.

```groovy
// Violations inside ArgumentBuilder:
project.properties.findAll { ... }
commandArguments += "--output-directory=${project.buildDir}/database/docs"
```

* **Runtime Configuration Resolution:** The task resolved its `liquibaseRuntime` classpath inside its execution logic instead of during the configuration phase.

```groovy
// Violation inside LiquibaseTask's execution logic:
def classpath = project.configurations.getByName(LiquibasePlugin.LIQUIBASE_RUNTIME_CONFIGURATION)
Expand All @@ -49,6 +58,7 @@ Two new classes were created to act as simple, serializable data containers:

1. **`LiquibaseInfo`**: This class is responsible for carrying project-level information that the `ArgumentBuilder` needs.
* *See the new class file: [`LiquibaseInfo.groovy`]([lhttps://github.com/Nouran-11/liquibase-gradle-plugin/blob/fix-cc/src/main/groovy/org/liquibase/gradle/LiquibaseInfo.groovy$0])*

```groovy
// org/liquibase/gradle/LiquibaseInfo.groovy
class LiquibaseInfo {
Expand All @@ -62,7 +72,7 @@ Two new classes were created to act as simple, serializable data containers:
}
}
```
2. **`ProjectInfo`**: This class holds configuration from the `liquibase { ... }` extension block.
1. **`ProjectInfo`**: This class holds configuration from the `liquibase { ... }` extension block.
* *See the new class file: [`ProjectInfo.groovy`]([https://github.com/Nouran-11/liquibase-gradle-plugin/blob/fix-cc/src/main/groovy/org/liquibase/gradle/ProjectInfo.groovy$0])*
```groovy
// org/liquibase/gradle/ProjectInfo.groovy
Expand All @@ -77,26 +87,31 @@ Two new classes were created to act as simple, serializable data containers:
}
}
```

The key feature of these classes is the static `fromProject()` factory method. This method acts as the bridge, safely extracting data at configuration time.

### Step 2: Refactor `ArgumentBuilder` to be Stateless

The `ArgumentBuilder` was refactored to remove its dependency on the `Project` object. Instead of holding a reference to the project, its methods now require a `LiquibaseInfo` object to be passed in.
* *See the full class changes: **[`ArgumentBuilder.groovy`]([https://github.com/liquibase/liquibase-gradle-plugin/blob/master/src/main/groovy/org/liquibase/gradle/ArgumentBuilder.groovy$0])** -> **[`ArgumentBuilder.groovy`]([https://github.com/Nouran-11/liquibase-gradle-plugin/blob/fix-cc/src/main/groovy/org/liquibase/gradle/ArgumentBuilder.groovy$0])***
**Before:**

```groovy
// ArgumentBuilder.groovy
def buildLiquibaseArgs(Activity activity, commandName, supportedCommandArguments) {
// ... code that used internal `project` reference ...
}
```

**After:**

```groovy
// ArgumentBuilder.groovy
def buildLiquibaseArgs(Activity activity, commandName, supportedCommandArguments, LiquibaseInfo liquibaseInfo) {
// ... code now uses liquibaseInfo.logger, liquibaseInfo.buildDir, etc. ...
}
```

## Step 3: Update LiquibaseTask to Use Inputs

The `@TaskAction` method (`exec` and its helpers) was rewritten to read from configured input properties instead of the project. This table highlights the specific transformations that eliminate configuration cache violations at execution time:
Expand All @@ -109,6 +124,7 @@ The `@TaskAction` method (`exec` and its helpers) was rewritten to read from con
| Accessing Project Properties | `argumentBuilder` reads `project.properties` | `argumentBuilder` receives `liquibaseInfo` with properties |
| Accessing buildDir | `argumentBuilder` reads `project.buildDir` | `argumentBuilder` receives `liquibaseInfo` with build dir |
---

## 4. Verification: The Configuration Cache Test

To provide concrete proof of these improvements, a dedicated integration test was added. This test runs a build with the configuration cache enabled, verifying that the plugin's tasks execute successfully and are correctly stored and retrieved from the cache on subsequent runs. This confirms that the refactoring was successful and the plugin is now fully compatible.
Expand All @@ -119,5 +135,5 @@ See the test in action: [Configuration Cache Test]([https://github.com/Nouran-11

## 5. Conclusion

In conclusion, this refactoring effort was centered on achieving full compatibility with Gradle's Configuration Cache. By decoupling task execution from the project model, the plugin now allows Gradle to serialize its state and entirely skip the configuration phase on subsequent runs. This unlocks the core performance promise of the configuration cache, resulting in a dramatically faster and more efficient development workflow.
In conclusion, this refactoring effort was centered on achieving full compatibility with Gradle's Configuration Cache. By decoupling task execution from the project model, the plugin now allows Gradle to serialize its state and entirely skip the configuration phase on subsequent runs. This unlocks the core performance promise of the configuration cache, resulting in a dramatically faster and more efficient development workflow.

7 changes: 6 additions & 1 deletion docs/plugin-development/reporting-api/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Gradle Reporting API
---
title: Gradle Reporting API
description: >
Gradle has a reporting system that tasks creating reports can expose,
letting users configure formats and output locations.
---

Gradle has a reporting system that tasks creating reports can expose, letting users configure formats and output locations.
For plugin developers that would like closer integration with Gradle systems, to integrate the [Reporting API](https://docs.gradle.org/current/javadoc/org/gradle/api/reporting/Reporting.html)
Expand Down
6 changes: 4 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ nav:
- Plugin Development:
- Summary: plugin-development/README.md
- Kotlin plugin development: plugin-development/kotlin-plugins.md
- Ensuring Configuration Cache compatibility: plugin-development/configuration-cache/
- Gradle Reporting API: plugin-development/reporting-api/
- Ensuring Configuration Cache compatibility:
- Overview: plugin-development/configuration-cache/
- Case study - Liquibase Plugin: plugin-development/configuration-cache/example-config-cache-liquibase-plugin.md
- Gradle Reporting API: plugin-development/reporting-api/README.md

# Theme
theme:
Expand Down
Loading