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❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15995 +/- ##
================================================
+ Coverage 0 51.4999% +51.4999%
- Complexity 0 17791 +17791
================================================
Files 0 2039 +2039
Lines 0 95608 +95608
Branches 0 16598 +16598
================================================
+ Hits 0 49238 +49238
- Misses 0 39059 +39059
- Partials 0 7311 +7311
🚀 New features to boost your workflow:
|
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.
- Revert the unresolved-dependency log from WARN back to ERROR, per
jdaugherty and davydotcom (davydotcom revised an initial approval to
CHANGES_REQUESTED specifically for this): unresolved dependencies are
a real load failure - the plugin already lands in failedPlugins - and
the prior code logged it at ERROR. WARN risked being filtered out of
environments that only surface errors.
- Fix logUnresolvedDependencies() misclassifying a dependency as
"missing" whenever it isn't yet registered, even when the dependency
plugin actually exists but itself failed to load or is still waiting
in the delayed-load queue (flagged independently by Copilot's inline
review and bito-code-review's bot comment; verified by tracing
loadDelayedPlugins()'s processing order). Now distinguishes three
cases: registered with an incompatible version (unchanged), found in
failedPlugins ("failed to load"), found in delayedLoadPlugins ("is
still pending load"), and only "is missing" when none of the above.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
✅ All tests passed ✅🏷️ Commit: 9e73262 Learn more about TestLens at testlens.app. |
Summary
Verification
This is an AI-generated starting point for richer plugin dependency diagnostics.