Skip to content

#1953: Complete cleanup commandlet implementation and review handoverShow more lines - #2230

Open
Caylipp wants to merge 34 commits into
devonfw:mainfrom
Caylipp:feature/1953-cleanup-commandlet-handover
Open

#1953: Complete cleanup commandlet implementation and review handoverShow more lines#2230
Caylipp wants to merge 34 commits into
devonfw:mainfrom
Caylipp:feature/1953-cleanup-commandlet-handover

Conversation

@Caylipp

@Caylipp Caylipp commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

This PR fixes #1953

This PR completes and supersedes #1957 after the original author left the team.

Implemented changes:

  • Reused the existing global -f / --force mode instead of introducing a cleanup-specific force option.
  • Removed the obsolete --force-delete help entries.
  • Kept cleanup discovery stateless by creating the discovered-tool list locally for each execution.
  • Moved the cleanup model into reusable top-level classes in the lowercase commandlet.cleanup package.
  • Scanned all repositories below _ide/software, including default, maven, and custom repository IDs.
  • Matched project software references against resolved installation paths instead of relying on fixed path positions.
  • Supported project links to subdirectories of an installation, such as Contents/MacOS.
  • Supported the following software link structures:
    • software/<tool>
    • software/extra/<tool>
    • software/extra/<tool>/<name>
  • Stored project usage only on the concrete tool edition version.
  • Derived the unused state of editions and tools from their child versions.
  • Improved JavaDoc for the cleanup model and its properties.

Testing instructions

Please add conscise, understandable instructions on how a reviewer can test/verify the functionality of your contribution here:

  1. mvn -pl cli -Dtest=CleanupCommandletTest test
  2. mvn clean test

Related

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). If no issue ID exists, title only.
  • PR top-level comment summarizes 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 labeled
    with internal
  • You have formulated clear instructions on how to test your contribution under "Testing instructions"

Checklist for tool commandlets

Have you added a new «tool» as commandlet? There are the following additional checks:

  • The tool can be installed automatically (during setup via settings) or via the commandlet call
  • The tool is isolated in its IDEasy project, see Sandbox Principle
  • The new tool is added to the table of tools in LICENSE.asciidoc
  • The new commandlet is a command-wrapper for «tool»
  • Proper help texts for all supported languages are added here
  • The new commandlet installs potential dependencies automatically
  • The variables «TOOL»_VERSION and «TOOL»_EDITION are honored by your commandlet
  • The new commandlet is tested on all platforms it is available for or tested on all platforms that are in scope of the linked issue

areinicke and others added 29 commits May 15, 2026 16:05
- scan all global software repositories
- detect used versions by resolved installation paths
- support links to installation subfolders and nested extra tools
- make cleanup discovery stateless
- track project usage only on version level
- reuse the global force mode for confirmation
- add regression tests for used and unused installations
@github-project-automation github-project-automation Bot moved this to 🆕 New in IDEasy board Jul 29, 2026
@Caylipp Caylipp self-assigned this Jul 29, 2026
@Caylipp Caylipp moved this from 🆕 New to Team Review in IDEasy board Jul 29, 2026
@Caylipp Caylipp moved this from Team Review to 👀 In review in IDEasy board Jul 29, 2026
@Caylipp Caylipp moved this from 👀 In review to Team Review in IDEasy board Jul 29, 2026
@Caylipp Caylipp added commandlet ide sub-command enhancement New feature or request labels Jul 29, 2026
@coveralls

coveralls commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 30540908755

Coverage increased (+0.2%) to 72.793%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 8 coverage regressions across 2 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

8 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java 7 91.9%
com/devonfw/tools/ide/tool/ide/IdeToolCommandlet.java 1 78.33%

Coverage Stats

Coverage Status
Relevant Lines: 17190
Covered Lines: 13042
Line Coverage: 75.87%
Relevant Branches: 7679
Covered Branches: 5061
Branch Coverage: 65.91%
Branches in Coverage %: Yes
Coverage Strength: 3.22 hits per line

💛 - Coveralls

@quando632 quando632 self-assigned this Jul 29, 2026

@hohwille hohwille left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We are closing the finishing line 👍
Still I found some points...

private final Path path;

/** A flag indicating whether the tool is marked for deletion. */
private boolean delete;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems some of my review feedback got lost (was maybe set to resolved by original author):
Please use OOP and avoid pointless redundancies.
We have these 3 classes that all should have a common abstract super-class.
Each of them has

  • name (simply generalize to just name instead of toolName, editionName, versionName)
  • path
  • delete

Also include methods like getters with JavaDoc, setter, etc.
BTW: I also suggested a simplification to have delete flag only in CleanupIdeToolEditionVersion so the consistency of the state gets trivial while with the current design it is complex. E.g. assume we later reuse this in the GUI and the user may see this as a tree with checkboxes and wants to unselect something manually to prevent deletion and then the parent deletion flag would need to be updated.
Simply when you iterate over the structure to perform the actual deletion check if the parent directory (edition or tool) now is empty after deletion of children and then also delete it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I implemented with a common superclass, version level deletion state and cleanup of empty parent folders.

List<CleanupIdeTool> installedCleanupIdeTools = discoverInstalledSoftware();

// Scan for IDEasy projects
List<Path> ideasyProjects = this.context.getFileAccess().listChildren(this.context.getIdeRoot(), Files::isDirectory);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We have the same logic also in our GUI. It would make sense to add a method IdeContext.findProjects() for this.
And as a motivation why I always insist on creating reusable structures:
This implementation is incorrect. It will also include _ide as well as folders somehow created by the end-user that are not really IDEasy projects. When we create a method and once get it right, then we can reuse it everywhere. Even if there still is a bug, we can fix it in once place instead of multiple places because of copy&paste.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, implemented and reused in both CleanupCommandlet and the GUI.

Comment on lines +60 to +62
if (ideasyProject.getFileName().toString().equals("_ide")) {
continue;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This would then be part of the central method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, this is now handled centrally by findProjects().

Path softwareRepositoryPath = this.context.getSoftwareRepositoryPath();
List<Path> repositoryFolders = this.context.getFileAccess().listChildren(softwareRepositoryPath, Files::isDirectory);
for (Path repositoryFolder : repositoryFolders) {
discoverInstalledSoftwareRepository(installedCleanupIdeTools, repositoryFolder);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is correct for default and maven but not for custom-tools:
https://github.com/devonfw/IDEasy/blob/main/documentation/software.adoc#custom-tools
For the given example URL
https://some-file-server.company.com/projects/my-project
We would have some-file-server.company.com as repositoryFolder here, but the actual repo with tool/edition/version structure is in the sub-path projects/my-project.
Sorry for this extra complication. I also missed this point in the review of the old PR.
So IMHO we should instead explicitly go to subfolders ToolRepository.ID_DEFAULT and MvnRepository.ID as well as this.context.getCustomToolRepository().getId(). The latter ID may be custom and that folder will typically not exist. Anyhow code should always be robust and handle non existing directories even when expected (I could delete entire _ide/software folder and that should not break ide cleanup - actually ide update should automatically repair my project if I did such evil deletion manually).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I implemented for default, Maven, and the full custom repository path. Missing folders are skipped.

Comment on lines +99 to +100
installedCleanupIdeTools.add(tool);
discoverInstalledEditions(toolFolder, tool);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just as a thought, the methods could return a boolean, if some tool/edition/version was found that looks sane. Then you could skip adding if nothing sane was found like this:

boolean success = discoverInstalledEditions(toolFolder, tool);
if (success) {
  installedCleanupIdeTools.add(tool);
}

I am just still thinking if we have a reliable way of detecting "sanity".
We could obviously check for existence of .ide.software.version what would be my preferred way, but assuming this file was deleted by accident (see also #2197) it would IMHO still make sense to delete the tool installation if unused.
Then I was thinking that version folder should have a name that is a valid version.
However, we have seen some tools violating our own rules for what we think is a valid version.
Also checking that there is at least one digit in the version is wrong since we have version latest.
Maybe checking for latest or "at least one digit" might make sense.
So far just my thoughts - we can still ignore this for now and consider as improvement after the merge.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I left this for a potential followup as suggested.

@quando632 quando632 left a comment

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.

Thanks for picking this up the substance of the #1957 review is addressed and the new tests cover exactly the cases that were requested there. I went through the branch and found a few points before this can go in, three of them blocking from my side:

  • the new tests error out on Windows without symlink privileges (verified locally, 3 of 4 tests)
  • the CHANGELOG entry landed in an already released section
  • the confirmation cannot actually be skipped with -f alone

The remaining ones are smaller correctness and consistency points. I left out anything already covered by the review above (common super class for the model classes, delete flag only on version level, central findProjects()).

void testCleanupDeletesUnusedAndKeepsUsedGlobalSoftware() throws IOException {

// arrange
IdeTestContext context = newContext(PROJECT_BASIC);

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.

These tests fail on Windows without symlink privileges (admin or Developer Mode). Verified locally with mvn -Dtest=CleanupCommandletTest test:

java.nio.file.FileSystemException: ...\software\cleanup-test-java: A required privilege is not held by the client
	at CleanupCommandletTest.createSoftwareLink(CleanupCommandletTest.java:187)
[ERROR] Tests run: 4, Failures: 0, Errors: 3, Skipped: 0

According to AGENTS.md, tests that create symbolic links must call WindowsSymlinkTestHelper.assumeSymlinksSupported() at the beginning of the test method, so they are skipped gracefully instead of erroring (see FileAccessImplTest for the pattern).

Suggested change
IdeTestContext context = newContext(PROJECT_BASIC);
WindowsSymlinkTestHelper.assumeSymlinksSupported();
IdeTestContext context = newContext(PROJECT_BASIC);

The same is needed in testCleanupKeepsVersionReferencedBySubdirectory and testCleanupKeepsVersionReferencedByNestedExtraTool, plus the import of com.devonfw.tools.ide.io.WindowsSymlinkTestHelper.

Comment thread CHANGELOG.adoc
* https://github.com/devonfw/IDEasy/issues/1884[#1884]: Fix java unzipping losing symlink information
* https://github.com/devonfw/IDEasy/issues/1716[#1716]: Add commandlet for Claude Code CLI
* https://github.com/devonfw/IDEasy/issues/1844[#1844]: Fix vscode installation hanging indefinitely in WSL Linux environments
* https://github.com/devonfw/IDEasy/issues/1953[#1953]: Implement base functionality of cleanup commandlet

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 entry was added to the == 2026.05.001 section (lines 83-129), which is already released. The currently open section is == 2026.08.001 at the top of the file.

Suggested change
* https://github.com/devonfw/IDEasy/issues/1953[#1953]: Implement base functionality of cleanup commandlet

Please add it under == 2026.08.001 instead:

* https://github.com/devonfw/IDEasy/issues/1953[#1953]: Implement base functionality of cleanup commandlet

cmd.claude=Tool commandlet for Claude Code CLI.
cmd.claude.detail=Claude Code CLI is a command-line interface for interacting with the Claude AI assistant. Detailed documentation can be found at https://code.claude.com/docs/en/overview
cmd.cleanup=Commandlet to clean up the IDEasy installation by uninstalling all unused tools.
cmd.cleanup.detail=This will remove any installed tools that are currently not in use by an IDEasy project.

@quando632 quando632 Jul 30, 2026

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 issue asks for a flag that skips the confirmation prompt, and dropping --force-delete in favour of the global force mode is the right call. However -f alone does not skip it: AbstractIdeContext.displayOptionsAndGetAnswer only auto-confirms when isBatchMode() && isForceMode(), and ContextCommandlet sets both flags independently (setBatchMode(...) / setForceMode(...)). So users actually need ide -b -f cleanup.

Since this is the documented way to fulfil the requirement from #1953, it should be mentioned in the help text:

Suggested change
cmd.cleanup.detail=This will remove any installed tools that are currently not in use by an IDEasy project.
cmd.cleanup.detail=This will remove any installed tools that are currently not in use by an IDEasy project. Before anything is deleted you are asked for confirmation. Run "ide -b -f cleanup" to skip the confirmation.

Help_de.properties:15 needs the same addition. Also, all four tests currently confirm via context.setAnswers("yes"), so the force path is untested, a test for it would be good.

Comment on lines +30 to +34
@Override
public String getName() {

return "cleanup";
}

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.

cleanup operates on IDE_ROOT and not on a single project, but isIdeHomeRequired() defaults to true, so ide cleanup outside of a project aborts with NO_IDE_HOME (see AbstractIdeContext:1391). CreateCommandlet has the same characteristic and overrides it:

Suggested change
@Override
public String getName() {
return "cleanup";
}
@Override
public String getName() {
return "cleanup";
}
@Override
public boolean isIdeHomeRequired() {
return false;
}

Comment on lines +104 to +118
/**
* This method discovers all installed editions of a tool at $IDE_ROOT/_ide/software/<repository>/<tool> and saves them to the edition list of the tool.
* Installed versions of the edition are then recursively discovered.
*
* @param editionFolder The folder where the editions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>).
* @param tool The respective tool for which we are discovering editions.
*/
private void discoverInstalledEditions(Path editionFolder, CleanupIdeTool tool) {
List<Path> subfolders = this.context.getFileAccess().listChildren(editionFolder, Files::isDirectory);
for (Path subfolder : subfolders) {
CleanupIdeToolEdition edition = new CleanupIdeToolEdition(subfolder.getFileName().toString(), this.context.getFileAccess().toRealPath(subfolder));
tool.getEditions().add(edition);
discoverInstalledVersions(subfolder, edition);
}
}

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 parameter names are one level off: this method receives the tool folder (the caller passes toolFolder), not an edition folder - and the loop variable that actually is the edition folder is called subfolder. In the review of #1957 the request was to name these editionFolder and versionFolder.

Suggested change
/**
* This method discovers all installed editions of a tool at $IDE_ROOT/_ide/software/<repository>/<tool> and saves them to the edition list of the tool.
* Installed versions of the edition are then recursively discovered.
*
* @param editionFolder The folder where the editions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>).
* @param tool The respective tool for which we are discovering editions.
*/
private void discoverInstalledEditions(Path editionFolder, CleanupIdeTool tool) {
List<Path> subfolders = this.context.getFileAccess().listChildren(editionFolder, Files::isDirectory);
for (Path subfolder : subfolders) {
CleanupIdeToolEdition edition = new CleanupIdeToolEdition(subfolder.getFileName().toString(), this.context.getFileAccess().toRealPath(subfolder));
tool.getEditions().add(edition);
discoverInstalledVersions(subfolder, edition);
}
}
/**
* This method discovers all installed editions of a tool at $IDE_ROOT/_ide/software/<repository>/<tool> and saves them to the edition list of the tool.
* Installed versions of the edition are then recursively discovered.
*
* @param toolFolder The folder where the editions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>).
* @param tool The respective tool for which we are discovering editions.
*/
private void discoverInstalledEditions(Path toolFolder, CleanupIdeTool tool) {
List<Path> editionFolders = this.context.getFileAccess().listChildren(toolFolder, Files::isDirectory);
for (Path editionFolder : editionFolders) {
Path editionPath = this.context.getFileAccess().toRealPath(editionFolder);
CleanupIdeToolEdition edition = new CleanupIdeToolEdition(editionFolder.getFileName().toString(), editionPath);
tool.getEditions().add(edition);
discoverInstalledVersions(editionFolder, edition);
}
}

Comment on lines +120 to +134
/**
* This method discovers all installed versions of an edition of a tool at $IDE_ROOT/_ide/software/<repository>/<tool>/<edition> and saves them to the version
* list of the edition.
*
* @param versionFolder The folder where the versions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>/<edition>).
* @param edition The respective edition for which we are discovering versions.
*/
private void discoverInstalledVersions(Path versionFolder, CleanupIdeToolEdition edition) {
List<Path> subfolders = this.context.getFileAccess().listChildren(versionFolder, Files::isDirectory);
for (Path subfolder : subfolders) {
CleanupIdeToolEditionVersion version = new CleanupIdeToolEditionVersion(subfolder.getFileName().toString(),
this.context.getFileAccess().toRealPath(subfolder));
edition.getVersions().add(version);
}
}

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.

Same here: the parameter is the edition folder, not a version folder.

Suggested change
/**
* This method discovers all installed versions of an edition of a tool at $IDE_ROOT/_ide/software/<repository>/<tool>/<edition> and saves them to the version
* list of the edition.
*
* @param versionFolder The folder where the versions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>/<edition>).
* @param edition The respective edition for which we are discovering versions.
*/
private void discoverInstalledVersions(Path versionFolder, CleanupIdeToolEdition edition) {
List<Path> subfolders = this.context.getFileAccess().listChildren(versionFolder, Files::isDirectory);
for (Path subfolder : subfolders) {
CleanupIdeToolEditionVersion version = new CleanupIdeToolEditionVersion(subfolder.getFileName().toString(),
this.context.getFileAccess().toRealPath(subfolder));
edition.getVersions().add(version);
}
}
/**
* This method discovers all installed versions of an edition of a tool at $IDE_ROOT/_ide/software/<repository>/<tool>/<edition> and saves them to the version
* list of the edition.
*
* @param editionFolder The folder where the versions are saved in ($IDE_ROOT/_ide/software/<repository>/<tool>/<edition>).
* @param edition The respective edition for which we are discovering versions.
*/
private void discoverInstalledVersions(Path editionFolder, CleanupIdeToolEdition edition) {
List<Path> versionFolders = this.context.getFileAccess().listChildren(editionFolder, Files::isDirectory);
for (Path versionFolder : versionFolders) {
Path versionPath = this.context.getFileAccess().toRealPath(versionFolder);
CleanupIdeToolEditionVersion version = new CleanupIdeToolEditionVersion(versionFolder.getFileName().toString(), versionPath);
edition.getVersions().add(version);
}
}

Comment on lines +271 to +278
// Ask for confirmation. Automatically confirmed if the global force option is provided.
try {
this.context.askToContinue("Do you want to continue?");

} catch (CliAbortException e) {
LOG.info("Installed Tools will not be deleted.");
return;
}

@quando632 quando632 Jul 30, 2026

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 the only place in the main sources that catches CliAbortException. Swallowing it means ide cleanup exits with code 0 when the user answers "no", instead of ProcessResult.ABORT a calling script cannot distinguish "aborted by user" from "completed". Letting it propagate gives the standard Aborted by end-user. handling for free.

Suggested change
// Ask for confirmation. Automatically confirmed if the global force option is provided.
try {
this.context.askToContinue("Do you want to continue?");
} catch (CliAbortException e) {
LOG.info("Installed Tools will not be deleted.");
return;
}
// Ask for confirmation. Automatically confirmed in batch mode with the global force option.
this.context.askToContinue("Do you want to continue?");

The import of com.devonfw.tools.ide.cli.CliAbortException in line 11 then becomes unused and has to be removed, otherwise checkstyle fails.

Comment on lines +244 to +252
if (!logOutputVersion.isBlank()) {
// If at least one version of the edition should be deleted
if (versionsDeleted < edition.getVersions().size()) {
logOutputVersion += "\t\t + " + (edition.getVersions().size() - versionsDeleted) + " more version(s) of this edition will not be deleted\n";
}
logOutputEdition += "\t - " + edition.editionName + "\n" + logOutputVersion;
editionsDeleted++;
totalEditionsDeleted++;
}

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.

editionsDeleted / totalEditionsDeleted are incremented whenever any version of the edition is deleted, so they count "affected" rather than "deleted" editions. For a tool with edition A (1 of 2 versions deleted) and edition B (nothing deleted) the output says "1 more edition(s) of this tool will not be deleted", although in fact both editions are kept. The Summary: line in 268 has the same problem.

Suggested change
if (!logOutputVersion.isBlank()) {
// If at least one version of the edition should be deleted
if (versionsDeleted < edition.getVersions().size()) {
logOutputVersion += "\t\t + " + (edition.getVersions().size() - versionsDeleted) + " more version(s) of this edition will not be deleted\n";
}
logOutputEdition += "\t - " + edition.editionName + "\n" + logOutputVersion;
editionsDeleted++;
totalEditionsDeleted++;
}
if (!logOutputVersion.isBlank()) {
// If at least one version of the edition should be deleted
if (versionsDeleted < edition.getVersions().size()) {
logOutputVersion += "\t\t + " + (edition.getVersions().size() - versionsDeleted) + " more version(s) of this edition will not be deleted\n";
}
logOutputEdition += "\t - " + edition.editionName + "\n" + logOutputVersion;
if (edition.isDelete()) {
editionsDeleted++;
totalEditionsDeleted++;
}
}

totalToolsDeleted in line 260 needs the same guard with tool.isDelete(). Note this interacts with the suggestion to keep the delete flag only on CleanupIdeToolEditionVersion - in that design the counters would be derived from "all children deleted" instead.


// Iterate through IDEasy projects and scan software in software folder. Save found software to list
for (Path ideasyProject : ideasyProjects) {
if (ideasyProject.getFileName().toString().equals("_ide")) {

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.

IdeContext.FOLDER_UNDERSCORE_IDE exists for this - using the constant also makes the usage findable from the IDE. IdeContext is already imported.

Suggested change
if (ideasyProject.getFileName().toString().equals("_ide")) {
if (ideasyProject.getFileName().toString().equals(IdeContext.FOLDER_UNDERSCORE_IDE)) {

(Becomes obsolete if the central IdeContext.findProjects() suggested above is implemented.)

Caylipp added 3 commits July 30, 2026 14:02
- add common abstract base class for installed software items
- centralize software item name and path
- rename cleanup model classes for reuse
- keep deletion state only on software versions
- delete unused versions before empty parent folders
- remove redundant deletion state from tools and editions
- add reusable project discovery to IdeContext
- exclude non project folders centrally
- reuse project discovery in cleanup and GUI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commandlet ide sub-command enhancement New feature or request

Projects

Status: Team Review

Development

Successfully merging this pull request may close these issues.

Implement Base Functionality of Cleanup Commandlet

6 participants