CLI command task discovery: configuration-cache compatibility and resolution scoping
- Dominant language
- Groovy
- Stars
- 2.9k
- Forks
- 975
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 92
Description
## Context
Follow-up from the CLI dependency split (#15948, review thread: https://github.com/apache/grails-core/pull/15948#discussion_r3605219281).
Per-command Gradle tasks (`dbmUpdate`, `generate-*`, ...) are registered from command names discovered in the `META-INF/grails-cli.factories` files of resolved `grailsCliClasspath` jars. Gradle requires task names to exist when the task graph is assembled, so this discovery currently resolves the CLI dependency graph at configuration time — every invocation pays it (including `./gradlew help`), and Grails does not yet support the configuration cache, so the cost is never amortized.
There is no Gradle API for lazily-materialized task *names*: dynamically named tasks that are visible in `gradle tasks`/IDE sync require configuration-time resolution. The work below reduces or amortizes the cost within that constraint.
## Tactical follow-ups
1. **Configuration-cache compatibility for the CLI plugins.** Configuration-time resolution is supported under CC (the resolved result is serialized into the cache entry), so CC effectively becomes the caching mechanism: discovery is paid once and replayed until inputs change. The blockers in the current code are incidental rather than structural — cross-project `findProject` access during companion discovery, `Project` captures in task closures. Clean these up and add a `--configuration-cache` functional test to lock it in.
2. **Scope the resolution cost.** Cache the manifest/factories scan per resolved artifact set so jars are not re-opened repeatedly within a single invocation.
3. **Optional task-rule mode.** A `tasks.addRule`-based mode would defer discovery until an unmatched task name is actually requested, making unrelated builds pay nothing — at the cost of per-command tasks no longer being listed individually in `gradle tasks`/IDE task views. Consider offering it behind a flag for build-speed-sensitive projects, keeping eager registration (discoverability) as the default.
## Strategic note
Much of this complexity exists because commands are surfaced as Gradle tasks. A fuller separation — tracking dependencies in `grailsCli` but resolving/using them in the Grails CLI itself — would decouple the build system from the CLI system entirely, but that is a redesign tied to the unresolved profile/forge restructuring discussion and is explicitly out of scope here. See the PR thread for details.
Contributor guide
Research direction
Start with the CLI plugin code that registers per-command Gradle tasks and scans META-INF/grails-cli.factories from resolved grailsCliClasspath jars. Trace companion discovery and task closures, then run the existing functional tests while adding a --configuration-cache case. Done means configuration-cache compatibility, one manifest scan per resolved artifact set, and a clearly decided approach for optional task-rule mode.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- groovy
- Domain
- build-system, cli
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100