Skip to content

Conversation

@maybeec
Copy link
Member

@maybeec maybeec commented Jan 8, 2026

Description

Fixes issue #1602: Being offline can block IDE startup when a tool update is available but not cached.

Changes

Core Fix

  • AbstractToolRepository.java: Enhanced doDownload() to check if download is already cached before throwing CliOfflineException in offline mode
  • LocalToolCommandlet.java:
    • Modified performToolInstallation() to return VersionIdentifier indicating which version was actually installed
    • Added proper offline fallback logic that returns existing installed version when download fails
    • Fixed installation path calculation to use actual installed version instead of requested version

Supporting Changes

  • Python.java: Updated override to match new performToolInstallation() signature
  • PackageManagerBasedLocalToolCommandlet.java: Updated override to match new signature

Tests

Added comprehensive test coverage in InstallCommandletTest.java:

  • testInstallCommandletOfflineWithCachedDownload: Fresh install with cached download succeeds
  • testInstallCommandletOfflineWithoutCachedDownload: Fresh install without cache fails appropriately
  • testInstallCommandletOfflineUpdateWithoutCachedDownload: Update scenario keeps old version when new version not cached

Documentation

Testing

All offline scenario tests pass successfully:

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

Checklist

  • Code compiles successfully
  • New tests added and passing
  • Coding conventions followed (AssertJ for assertions, proper error handling)
  • CHANGELOG.adoc updated
  • Design reviewed and refactored to avoid duplication
  • Commit message follows format: #1602: Being offline can block ide startup

Related Issue

Closes #1602

@github-project-automation github-project-automation bot moved this to 🆕 New in IDEasy board Jan 8, 2026
@maybeec maybeec added update related to updating software or the entire ide bugfix PR that fixes a bug issue labels Jan 8, 2026
@maybeec maybeec moved this from 🆕 New to Team Review in IDEasy board Jan 8, 2026
@coveralls
Copy link
Collaborator

coveralls commented Jan 8, 2026

Pull Request Test Coverage Report for Build 21014707628

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 61 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.04%) to 70.399%

Files with Coverage Reduction New Missed Lines %
com/devonfw/tools/ide/tool/python/Python.java 3 73.53%
com/devonfw/tools/ide/tool/repository/AbstractToolRepository.java 28 67.24%
com/devonfw/tools/ide/tool/LocalToolCommandlet.java 30 80.76%
Totals Coverage Status
Change from base Build 21013786095: 0.04%
Covered Lines: 10451
Relevant Lines: 14260

💛 - Coveralls

Copy link
Member

@hohwille hohwille left a comment

Choose a reason for hiding this comment

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

@maybeec thanks for your PR. This is a really great improvement that I like to merge.
I was not really aware of this problem that we check for offline and throw an exception before we check that the download may already been cached....
FYI: I always told the development team that I consider it as an anti-pattern to do an explicit online check and throw an offline exception prior to invoking network activity.
I created invokeNetworkTask as a reasonable alternative to get what we really want.
Example:

return this.context.getNetworkStatus().invokeNetworkTask(() -> {
URL xmlUrl = new URL(url);
try (InputStream is = xmlUrl.openStream()) {
return documentBuilder.parse(is);
}
}, url);

This way we simply perform the network operation and if we get a network issue (e.g. IOException) then this is stored as the latest network error and considered for our offline/online status. Our artificial online check is just pinging github.com and it may be unlikely that this fails. However, I have already seen situation where that failed due to ZScaler interfering while some other tool download worked fine.

* {@code resolvedVersion} (returning the existing installed version instead).
*/
protected void performToolInstallation(ToolInstallRequest request, Path installationPath) {
protected VersionIdentifier performToolInstallation(ToolInstallRequest request, Path installationPath, VersionIdentifier resolvedVersion) {
Copy link
Member

Choose a reason for hiding this comment

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

wouldn't it be easier to not change this signature and do the try-catch outside of performToolInstallation?

BTW: using exceptions for control flow is not nice but I everything else would require way more refactoring - so I guess this should be fine here.

@github-project-automation github-project-automation bot moved this from Team Review to 👀 In review in IDEasy board Jan 15, 2026
@hohwille
Copy link
Member

Maybe we can include this great feature into 2026.01.001...

Comment on lines +203 to +209
VersionIdentifier actualInstalledVersion = performToolInstallation(request, installationPath, resolvedVersion);
// If offline and could not download, actualInstalledVersion will be the old version, not resolvedVersion
// In that case, we need to recalculate the installation path for the actually installed version
if (!actualInstalledVersion.equals(resolvedVersion)) {
installationPath = getInstallationPath(edition, actualInstalledVersion);
}
return createToolInstallation(installationPath, actualInstalledVersion, true, processContext, additionalInstallation);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
VersionIdentifier actualInstalledVersion = performToolInstallation(request, installationPath, resolvedVersion);
// If offline and could not download, actualInstalledVersion will be the old version, not resolvedVersion
// In that case, we need to recalculate the installation path for the actually installed version
if (!actualInstalledVersion.equals(resolvedVersion)) {
installationPath = getInstallationPath(edition, actualInstalledVersion);
}
return createToolInstallation(installationPath, actualInstalledVersion, true, processContext, additionalInstallation);
try {
performToolInstallation(request, installationPath);
} catch (CliOfflineException e) {
// If we are offline and cannot download, check if we can continue with an existing installation
ToolEditionAndVersion installed = request.getInstalled();
if ((installed != null) && (installed.getResolvedVersion() != null)) {
this.context.warning("Cannot download {} in version {} because we are offline. Continuing with already installed version {}.", this.tool, resolvedVersion, installed.getResolvedVersion());
// If offline and could not download, actualInstalledVersion will be the old version, not resolvedVersion
// In that case, we need to recalculate the installation path for the actually installed version
installationPath = getInstallationPath(edition, installed.getResolvedVersion());
}
// No existing installation available, re-throw the exception
throw e;
}
return createToolInstallation(installationPath, actualInstalledVersion, true, processContext, additionalInstallation);

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

Labels

bugfix PR that fixes a bug issue update related to updating software or the entire ide

Projects

Status: 👀 In review

Development

Successfully merging this pull request may close these issues.

Being offline can block ide startup

3 participants