Improve unresolved plugin dependency diagnostics#15995
Conversation
Assisted-by: opencode:gpt-5.6-sol
There was a problem hiding this comment.
Pull request overview
This PR improves Grails plugin discovery diagnostics by replacing a malformed unresolved-dependency ERROR with a single WARN that enumerates which plugin dependencies are missing or version-incompatible, without changing the underlying plugin discovery/registration rules.
Changes:
- Replace the previous unresolved-plugin ERROR log with a WARN emitted when delayed plugins ultimately fail to resolve.
- Add detailed per-dependency messages indicating “missing” vs “version mismatch” (required vs actual).
- Add a Spock spec asserting the new WARN content and that no stack trace is printed at normal verbosity.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| grails-core/src/main/groovy/org/apache/grails/core/plugins/DefaultPluginDiscovery.java | Introduces logUnresolvedDependencies() to emit a single detailed WARN for plugins that fail dependency resolution. |
| grails-core/src/test/groovy/org/apache/grails/core/plugins/PluginDiscoverySpec.groovy | Adds a test to validate the new unresolved dependency WARN format and content. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private void logUnresolvedDependencies(PluginInfo plugin) { | ||
| var unresolvedDependencies = new ArrayList<String>(); | ||
| for (var name : plugin.getDependsOnNames()) { | ||
| var requiredVersion = plugin.getMetadata().getDependentVersion(name); | ||
| var dependency = findPlugin(name); | ||
| if (dependency == null) { | ||
| unresolvedDependencies.add( | ||
| "dependency [" + name + "] with required version [" + requiredVersion + "] is missing" | ||
| ); | ||
| } else if (!GrailsVersionUtils.isValidVersion(dependency.getPluginVersion(), requiredVersion)) { | ||
| unresolvedDependencies.add( | ||
| "dependency [" + name + "] has version [" + dependency.getPluginVersion() + | ||
| "] but requires [" + requiredVersion + "]" | ||
| ); | ||
| } | ||
| } | ||
| LOG.warn( | ||
| "Grails plug-in [{}] with version [{}] cannot be loaded: {}", | ||
| plugin.getName(), | ||
| plugin.getPluginVersion(), | ||
| String.join("; ", unresolvedDependencies) | ||
| ); | ||
| } |
|
The suggestion to improve To implement this, you can update the logic to check these collections before defaulting to the "missing" classification: private void logUnresolvedDependencies(PluginInfo plugin) {
var unresolvedDependencies = new ArrayList<String>();
for (var name : plugin.getDependsOnNames()) {
var requiredVersion = plugin.getMetadata().getDependentVersion(name);
var dependency = findPlugin(name);
if (dependency == null) {
// Check if it exists in failed or delayed collections
if (failedPlugins.containsKey(name)) {
unresolvedDependencies.add("dependency [" + name + "] failed to load");
} else if (delayedLoadPlugins.stream().anyMatch(p -> p.getName().equals(name))) {
unresolvedDependencies.add("dependency [" + name + "] is delayed");
} else {
unresolvedDependencies.add("dependency [" + name + "] with required version [" + requiredVersion + "] is missing");
}
} else if (!GrailsVersionUtils.isValidVersion(dependency.getPluginVersion(), requiredVersion)) {
unresolvedDependencies.add("dependency [" + name + "] has version [" + dependency.getPluginVersion() + "] but requires [" + requiredVersion + "]");
}
}
// ... log warning
}grails-core/src/main/groovy/org/apache/grails/core/plugins/DefaultPluginDiscovery.java |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15995 +/- ##
================================================
+ Coverage 0 51.1209% +51.1209%
- Complexity 0 17604 +17604
================================================
Files 0 2041 +2041
Lines 0 95505 +95505
Branches 0 16590 +16590
================================================
+ Hits 0 48823 +48823
- Misses 0 39397 +39397
- Partials 0 7285 +7285
🚀 New features to boost your workflow:
|
✅ All tests passed ✅🏷️ Commit: 9e30fb0 Learn more about TestLens at testlens.app. |
davydotcom
left a comment
There was a problem hiding this comment.
Looks good — approving.
Failure determination is unchanged: plugins still fail the same way via the existing dependency resolution / delayed-load path into failedPlugins. Discovery, registration, failed-plugin tracking, and load order are untouched.
What’s new here is the notification: replacing the malformed ERROR with a single concise WARN that enumerates missing vs version-incompatible dependencies. That’s a clearer alert for users without changing how unresolved plugins are decided.
| ); | ||
| } | ||
| } | ||
| LOG.warn( |
There was a problem hiding this comment.
I disagree on this being a warning, there is a legitimate configuration issue. This should be an error message
davydotcom
left a comment
There was a problem hiding this comment.
Revising my earlier approval with one requested change.
The richer diagnostics look good, and failure determination is still unchanged — this is still just a clearer alert for an already-failed plugin. Please keep the log level at ERROR, not WARN.
The previous message was already LOG.error. Downgrading to WARN isn’t justified in the PR beyond “one concise WARN,” and unresolved/failed plugin dependencies are a real load failure. ERROR keeps the severity aligned with that outcome and with prior behavior; WARN can be filtered out of environments that only surface errors.
Requested change:
- use
LOG.error(...)inlogUnresolvedDependencies - update the new
PluginDiscoverySpecassertion accordingly (ERRORinstead ofWARN)
| ); | ||
| } | ||
| } | ||
| LOG.warn( |
There was a problem hiding this comment.
Please keep this at ERROR rather than WARN.
Unresolved dependencies already put the plugin in failedPlugins; that is a load failure, and the previous code logged it with LOG.error. A clearer message is welcome, but the severity should stay ERROR so it isn’t lost when WARN is filtered.
Summary
Verification
This is an AI-generated starting point for richer plugin dependency diagnostics.