Skip to content

#2420: Improve BuildTool support further - #2447

Open
laert-ll wants to merge 2 commits into
devonfw:mainfrom
laert-ll:task/2420-improve-buildtool-support-further
Open

laert-ll wants to merge 2 commits into
devonfw:mainfrom
laert-ll:task/2420-improve-buildtool-support-further

Conversation

@laert-ll

@laert-ll laert-ll commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #2420

Implemented changes:

  • CommandletManager.findBuildTool now returns BuildTool instead of LocalToolCommandlet.
  • Replaced the hard-coded BUILD_TOOLS list in CommandletManagerImpl with dynamic iteration over all commandlets using instanceof BuildTool pattern matching.
  • Changed commandletTypeMap from HashMap to LinkedHashMap and moved Npm registration after Yarn so registration order decides detection priority (Yarn before Npm).
  • Removed findBuildDescriptor and findWrapper from LocalToolCommandlet (moved to BuildTool)
  • Made Gradle, Npm, and Yarn implement BuildTool; Yarn.findBuildDescriptor only matches when yarn.lock is present and falls back to Npm otherwise.
  • Updated BuildCommandlet to use BuildTool directly instead of casting through LocalToolCommandlet.
  • Updated ReleaseCommandlet to catch UnsupportedOperationException from getProjectVersion/setProjectVersion and rethrow as a CliException with a clear "does not support releasing" message.

Testing instructions

Test projects for points 1 - 5 can be found under cli/src/test/resources/ide-projects/build/project/workspaces/main/ and for points 6 - 8 under cli/src/test/resources/ide-projects/release/project/workspaces/main/.

  1. In a Maven project (has pom.xml): run ide build and verify it invokes mvn.
  2. In a Gradle project (has build.gradle): run ide build and verify it invokes gradle.
  3. In a Node project with only package.json: run ide build and verify it invokes npm.
  4. In a Node project with both package.json and yarn.lock: run ide build and verify it invokes yarn, not npm.
  5. In a Maven project that also has package.json (polyglot): run ide build and verify it invokes mvn, not npm.
  6. In a Gradle project: run ide release and verify it fails with a clear message containing "does not support releasing" rather than a stack trace or cryptic error.
  7. In an npm/yarn project: same, it should fail with the same readable message.
  8. In a Maven project: run ide release and verify it proceeds normally (no regression).

Checklist for this PR

Make sure everything is checked before merging this PR. For further info please also see
our DoD.

  • When running mvn clean test locally all tests pass and build is successful
  • PR title is of the form #«issue-id»: «brief summary» (e.g. #921: fixed setup.bat and not feature/921 fixed setup.bat). If no issue ID exists, title only.
  • PR top-level comment summaries what has been done and contains link to addressed issue(s)
  • PR and issue(s) have suitable labels
  • Issue is set to In Progress and assigned to you or there is no issue (might happen for very small PRs)
  • You followed all coding conventions
  • You have added the issue implemented by your PR in CHANGELOG.adoc unless issue is labelled
    with internal
  • You have not changed any dependency in pom.xml files or otherwise if runtime dependencies changed, you have updated our LICENSE.asciidoc
  • You have formulated clear instructions on how to test your contribution under "Testing instructions"

@coveralls

coveralls commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 34821751469

Coverage decreased (-0.02%) to 73.886%

Details

  • Coverage decreased (-0.02%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 29 coverage regressions across 9 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

29 previously-covered lines in 9 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java 8 91.74%
com/devonfw/tools/ide/commandlet/ReleaseCommandlet.java 7 66.28%
com/devonfw/tools/ide/tool/intellij/Intellij.java 4 87.65%
com/devonfw/tools/ide/commandlet/CommandletManager.java 3 75.0%
com/devonfw/tools/ide/tool/BuildTool.java 2 84.21%
com/devonfw/tools/ide/tool/gradle/Gradle.java 2 88.0%
com/devonfw/tools/ide/tool/LocalToolCommandlet.java 1 79.24%
com/devonfw/tools/ide/tool/npm/Npm.java 1 95.24%
com/devonfw/tools/ide/version/VersionSegment.java 1 90.03%

Coverage Stats

Coverage Status
Relevant Lines: 18801
Covered Lines: 14520
Line Coverage: 77.23%
Relevant Branches: 8403
Covered Branches: 5580
Branch Coverage: 66.4%
Branches in Coverage %: Yes
Coverage Strength: 3.29 hits per line

💛 - Coveralls

@laert-ll laert-ll moved this from 🆕 New to 🏗 In progress in IDEasy board Sep 11, 2026
@laert-ll
laert-ll marked this pull request as ready for review September 14, 2026 08:13
@laert-ll
laert-ll force-pushed the task/2420-improve-buildtool-support-further branch from 2bfbd26 to 6f7eddb Compare September 14, 2026 08:14
@laert-ll laert-ll moved this from 🏗 In progress to Team Review in IDEasy board Sep 14, 2026
@majesteSil majesteSil self-assigned this Sep 14, 2026
@majesteSil

Copy link
Copy Markdown
Contributor

thank you @laert-ll for your work on this there a some point where i see some improvments so let me know if you have any further Question on this

@majesteSil
majesteSil self-requested a review September 22, 2026 08:48
try {
currentVersion = buildTool.getProjectVersion(projectPath);
} catch (UnsupportedOperationException e) {
throw new CliException("The build tool " + buildTool.getName() + " detected in " + projectPath + " does not support releasing.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The caught exception is lost here. Per documentation/contributing/coding-conventions.adoc ("Catching and handling Exceptions"), when wrapping and re-throwing the caught exception must be passed as the cause:

throw new CliException("...", e);

*/
public interface BuildTool {

Logger LOG = LoggerFactory.getLogger(BuildTool.class);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Having a Logger field in this interface is a bit unusual. the coding conventions ("Logging") recommend using this.context instead of a LOG field for commandlets. If LOG is only used by the findWrapper default method, could we log via the passed-in IdeContext instead?

Comment on lines +60 to +85
default VersionIdentifier getProjectVersion(Path projectPath) {

throw new UnsupportedOperationException();
}

/**
* Sets the {@link #getProjectVersion(Path) version} of the project.
*
* @param projectPath the {@link Path} to the top-level directory of the project.
* @param version the new {@link VersionIdentifier version} to set.
*/
void setProjectVersion(Path projectPath, VersionIdentifier version);
default void setProjectVersion(Path projectPath, VersionIdentifier version) {

throw new UnsupportedOperationException();
}

/**
* Performs a single build-and-deploy (release) build of the project in the current working directory.
*
* @param additionalArgs the additional arguments to append to the build command (may be {@link List#isEmpty() empty}).
* @return the {@link ProcessResult} of the build (allowing the caller to react on {@link ProcessResult#isSuccessful() failures}, e.g. by retrying).
*/
ProcessResult buildAndDeploy(List<String> additionalArgs);
default ProcessResult buildAndDeploy(List<String> additionalArgs) {

throw new UnsupportedOperationException();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is allowed by the issue, but using UnsupportedOperationException as a capability flag is exception-as-control-flow (callers must catch it). For a follow-up, a nullable query (e.g. tryGetProjectVersion) or an explicit capability check would read more naturally.

super();
this.context = context;
this.commandletTypeMap = new HashMap<>();
this.commandletTypeMap = new LinkedHashMap<>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With HashMap → LinkedHashMap, the iteration order of getCommandlets() / commandletIterator now changes globally. this also affects command completion and findCommandlet. I assume the insertion order is required for the Yarn-before-Npm priority, but it is a side effect beyond the build tools. Also, the "Yarn must be registered before Npm" constraint now only lives in the CommandletManager javadoc. a short comment at the registration site (add(new Yarn(...)); add(new Npm(...));) would keep it visible where it is enforced.

protected void configureToolBinary(ProcessContext pc, ProcessMode processMode) {
Path gradle = Path.of(getBinaryName());
Path wrapper = findWrapper(GRADLE_WRAPPER_FILENAME);
Path wrapper = findWrapper(this.context.getCwd(), GRADLE_WRAPPER_FILENAME);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With the new interface default, doesn't this override become a no-op (return null;)? If it adds nothing, it could be removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Team Review

Development

Successfully merging this pull request may close these issues.

Improve BuildTool support further

3 participants