Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#786: Upgrade commandlet #1002

Open
wants to merge 35 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
65f66cc
created the to be implemented class
salimbouch Dec 16, 2024
2046171
added mavenrepo
salimbouch Dec 19, 2024
ed00c87
implemented untested logic to maven repo download
salimbouch Jan 5, 2025
4a0386d
minor changes
salimbouch Jan 7, 2025
94b12b8
Merge branch 'main' of https://github.com/devonfw/ideasy into upgrade…
salimbouch Jan 8, 2025
6fd391a
Merge branch 'upgrade-commandlet' of https://github.com/salimbouch/ID…
salimbouch Jan 8, 2025
0e145e3
implemented ugrade comdlet
salimbouch Jan 8, 2025
3892233
minor change
salimbouch Jan 12, 2025
753758d
Merge branch 'main' of https://github.com/devonfw/ideasy into upgrade…
salimbouch Jan 13, 2025
fd6a287
added script to upgrade
salimbouch Jan 13, 2025
3f71234
fixed snapshot comparison, fixed bug in path construction
salimbouch Jan 15, 2025
b754cef
added test
salimbouch Jan 20, 2025
82934bb
Merge branch 'main' of https://github.com/devonfw/ideasy into upgrade…
salimbouch Jan 20, 2025
bc7b28d
added commandlet to nls
salimbouch Jan 20, 2025
b5525df
added javadoc
salimbouch Jan 20, 2025
607210b
added missing jdoc
salimbouch Jan 20, 2025
a082f0f
Merge branch 'main' of https://github.com/devonfw/ideasy into upgrade…
salimbouch Jan 22, 2025
3d4f73c
Merge branch 'upgrade-commandlet' of https://github.com/salimbouch/ID…
salimbouch Jan 22, 2025
703b0a9
implemented requested changes
salimbouch Jan 22, 2025
bc519fb
fixed test
salimbouch Jan 22, 2025
5a1f7f9
Merge branch 'main' into upgrade-commandlet
hohwille Jan 27, 2025
d79b4f4
added maven repo test
salimbouch Jan 27, 2025
6f67b25
Merge branch 'main' into upgrade-commandlet
salimbouch Jan 27, 2025
6dbb90c
Merge branch 'main' into upgrade-commandlet
salimbouch Jan 28, 2025
e2c9b97
devonfw#786: discussed changes
salimbouch Jan 29, 2025
ea5e6bf
devonfw#786: fixed test
salimbouch Jan 29, 2025
cc3109b
devonfw#786: review change
salimbouch Jan 29, 2025
c6d5737
Merge branch 'main' into upgrade-commandlet
jan-vcapgemini Jan 30, 2025
4bbd25e
Merge branch 'upgrade-commandlet' of https://github.com/salimbouch/ID…
hohwille Jan 30, 2025
2ab733d
#786: reworked ide upgrade
hohwille Jan 30, 2025
167f8e8
#786: solved upgrade of unstable release versions (e.g. beta)
hohwille Jan 30, 2025
f4f43d3
#786: graalvm workarounds
hohwille Jan 30, 2025
e12261a
#786: make test OS agnostic
hohwille Jan 30, 2025
cc5265a
#786: fix test
hohwille Jan 30, 2025
e11b708
Merge branch 'main' into salimbouch-upgrade-commandlet
hohwille Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<groupId>com.devonfw.tools.IDEasy.dev</groupId>
<artifactId>ide</artifactId>
<version>dev-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>com.devonfw.tools.IDEasy</groupId>
<artifactId>ide-cli</artifactId>
Expand Down Expand Up @@ -246,8 +245,9 @@
<imageName>${imageName}</imageName>
<buildArgs>
<arg>--enable-url-protocols=http,https</arg>
<arg>--initialize-at-build-time=org.apache.commons</arg>
<arg>-march=compatibility</arg>
<arg>--initialize-at-build-time=org.apache.commons</arg>
<arg>-H:IncludeResourceBundles=com.sun.org.apache.xml.internal.res.XMLErrorResources,com.sun.org.apache.xerces.internal.impl.msg.XMLMessages</arg>
</buildArgs>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public CommandletManagerImpl(IdeContext context) {
add(new BuildCommandlet(context));
add(new InstallPluginCommandlet(context));
add(new UninstallPluginCommandlet(context));
add(new UpgradeCommandlet(context));
add(new Gh(context));
add(new Helm(context));
add(new Java(context));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.devonfw.tools.ide.commandlet;

import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.os.WindowsPathSyntax;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.process.ProcessMode;
import com.devonfw.tools.ide.repo.MavenRepository;
import com.devonfw.tools.ide.version.IdeVersion;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
* {@link Commandlet} to upgrade the version of IDEasy
*/
public class UpgradeCommandlet extends Commandlet {

private static final VersionIdentifier LATEST_SNAPSHOT = VersionIdentifier.of("*-SNAPSHOT");
public static final String IDEASY = "ideasy";

/**
* The constructor.
*
* @param context the {@link IdeContext}.
*/
public UpgradeCommandlet(IdeContext context) {

super(context);
addKeyword(getName());
}

@Override
public String getName() {

return "upgrade";
}

/**
* Compares two snapshot versions to determine if the latest is newer. Handles versions in the following formats: - Current version format:
* "2024.12.002-beta-12_18_02-SNAPSHOT" - Latest version format: "2025.01.001-beta-20250118.022832-8"
* <p>
* First compares base versions (e.g. 2024.12.002 with 2025.01.001), then timestamps if base versions are equal. Returns false if version formats are
* unexpected to avoid unintended upgrades.
*
* @param currentVersion The current snapshot version
* @param latestVersion The latest snapshot version to compare against
* @return true if latestVersion is newer than currentVersion, false otherwise or if formats are invalid
*/
protected boolean isSnapshotNewer(String currentVersion, String latestVersion) {

try {
// Validate input formats
if (currentVersion == null || latestVersion == null || !currentVersion.contains("-") || !latestVersion.contains(
"-")) {
return false;
}

// First compare base versions (2024.12.002 with 2025.01.001)
String currentBase = currentVersion.substring(0, currentVersion.indexOf('-'));
String latestBase = latestVersion.substring(0, latestVersion.indexOf('-'));

VersionIdentifier currentBaseVersion = VersionIdentifier.of(currentBase);
VersionIdentifier latestBaseVersion = VersionIdentifier.of(latestBase);

// If base versions are different, use regular version comparison
if (!currentBaseVersion.compareVersion(latestBaseVersion).isEqual()) {
return currentBaseVersion.compareVersion(latestBaseVersion).isLess();
}

// Validate timestamp formats
String[] currentParts = currentVersion.split("-");
String[] latestParts = latestVersion.split("-");
if (currentParts.length < 3 || latestParts.length < 3) {
return false;
}

// Extract timestamps
String currentTimestamp = currentParts[2].split("-")[0].replace("_", ""); // "010102"
String[] latestTimestampParts = latestParts[2].split("\\.");
if (latestTimestampParts.length < 1) {
return false;
}

// Get year from base version (2024.12.002 -> 2024)
String year = currentBase.substring(0, 4);

// Parse current date/time using extracted year (currentTimestamp format: MMDDXX)
LocalDateTime currentTime = LocalDateTime.parse(year + currentTimestamp + "00", // YYYYMMDDHHmm
DateTimeFormatter.ofPattern("yyyyMMddHHmm"));

// Parse latest date/time (format: YYYYMMDD.HHMMSS)
LocalDateTime latestTime = LocalDateTime.parse(latestTimestampParts[0] + "000000",
DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

return latestTime.isAfter(currentTime);
} catch (Exception e) {
throw new IllegalStateException("Failed to compare current and latest Snapshot.", e);
}
}

@Override
public void run() {

String version = IdeVersion.get();
if (IdeVersion.VERSION_UNDEFINED.equals(version)) {
this.context.warning("You are using IDEasy version {} what indicates local development - skipping upgrade.", version);
return;
}
VersionIdentifier currentVersion = VersionIdentifier.of(version);
MavenRepository mavenRepo = this.context.getMavenSoftwareRepository();
VersionIdentifier configuredVersion;
if (version.contains("SNAPSHOT")) {
configuredVersion = LATEST_SNAPSHOT;
} else if (currentVersion.getDevelopmentPhase().isStable()) {
configuredVersion = VersionIdentifier.LATEST;
} else {
configuredVersion = VersionIdentifier.LATEST_UNSTABLE;
}
this.context.debug("Trying to determine the latest version of IDEasy ({})", configuredVersion);
VersionIdentifier resolvedVersion = mavenRepo.resolveVersion(IDEASY, IDEASY, configuredVersion);

boolean upgradeAvailable = resolvedVersion.isGreater(currentVersion);
if (upgradeAvailable) {
this.context.info("Upgrading IDEasy from version {} to {}", version, resolvedVersion);
try {
this.context.info("Downloading new version...");
Path downloadTarget = mavenRepo.download(IDEASY, IDEASY, resolvedVersion);
Path extractionTarget = this.context.getIdeRoot().resolve(IdeContext.FOLDER_IDE);
if (this.context.getSystemInfo().isWindows()) {
handleUpgradeOnWindows(downloadTarget, extractionTarget);
} else {
this.context.info("Extracting files...");
this.context.getFileAccess().extract(downloadTarget, extractionTarget);
this.context.success("Successfully upgraded to version {}", resolvedVersion);
}
} catch (Exception e) {
throw new IllegalStateException("Failed to upgrade version.", e);
}
} else {
this.context.info("Your have IDEasy {} installed what is already the latest version.", version);
}
}

private void handleUpgradeOnWindows(Path downloadTarget, Path extractionTarget) throws IOException {

ProcessContext pc = this.context.newProcess().executable("bash")
.addArgs("-c",
"'sleep 10;tar xvfz \"" + WindowsPathSyntax.MSYS.format(downloadTarget) + "\" -C \"" + WindowsPathSyntax.MSYS.format(extractionTarget) + "\"'");
pc.run(ProcessMode.BACKGROUND_SILENT);
this.context.interaction("To prevent windows file locking errors, "
+ "we perform an asynchronous upgrade in background now.\n"
+ "Please wait a minute for the upgrade to complete before running IDEasy commands.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.devonfw.tools.ide.repo.CustomToolRepository;
import com.devonfw.tools.ide.repo.CustomToolRepositoryImpl;
import com.devonfw.tools.ide.repo.DefaultToolRepository;
import com.devonfw.tools.ide.repo.MavenRepository;
import com.devonfw.tools.ide.repo.ToolRepository;
import com.devonfw.tools.ide.step.Step;
import com.devonfw.tools.ide.step.StepImpl;
Expand Down Expand Up @@ -128,6 +129,8 @@ public abstract class AbstractIdeContext implements IdeContext {

private CustomToolRepository customToolRepository;

private MavenRepository mavenRepository;

private DirectoryMerger workspaceMerger;

protected UrlMetadata urlMetadata;
Expand Down Expand Up @@ -207,6 +210,7 @@ public AbstractIdeContext(IdeStartContextImpl startContext, Path workingDirector
}

this.defaultToolRepository = new DefaultToolRepository(this);
this.mavenRepository = new MavenRepository(this);
}

private Path findIdeRoot(Path ideHomePath) {
Expand Down Expand Up @@ -354,6 +358,12 @@ public ToolRepository getDefaultToolRepository() {
return this.defaultToolRepository;
}

@Override
public MavenRepository getMavenSoftwareRepository() {

return this.mavenRepository;
}

@Override
public CustomToolRepository getCustomToolRepository() {

Expand Down Expand Up @@ -439,6 +449,7 @@ public Path getSettingsGitRepository() {
return settingsPath;
}

@Override
public boolean isSettingsRepositorySymlinkOrJunction() {

Path settingsPath = getSettingsPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.devonfw.tools.ide.os.WindowsPathSyntax;
import com.devonfw.tools.ide.process.ProcessContext;
import com.devonfw.tools.ide.repo.CustomToolRepository;
import com.devonfw.tools.ide.repo.MavenRepository;
import com.devonfw.tools.ide.repo.ToolRepository;
import com.devonfw.tools.ide.step.Step;
import com.devonfw.tools.ide.tool.mvn.Mvn;
Expand Down Expand Up @@ -273,6 +274,11 @@ default void requireOnline(String purpose) {
*/
CustomToolRepository getCustomToolRepository();

/**
* @return the {@link MavenRepository}.
*/
MavenRepository getMavenSoftwareRepository();

/**
* @return the {@link Path} to the IDE instance directory. You can have as many IDE instances on the same computer as independent tenants for different
* isolated projects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public AbstractToolRepository(IdeContext context) {
*/
protected abstract UrlDownloadFileMetadata getMetadata(String tool, String edition, VersionIdentifier version);


@Override
public Path download(String tool, String edition, VersionIdentifier version) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.devonfw.tools.ide.repo;

import java.util.Collections;
import java.util.Set;

import com.devonfw.tools.ide.os.OperatingSystem;
import com.devonfw.tools.ide.os.SystemArchitecture;
import com.devonfw.tools.ide.tool.mvn.MvnArtifact;
import com.devonfw.tools.ide.url.model.file.UrlDownloadFileMetadata;
import com.devonfw.tools.ide.version.VersionIdentifier;

/**
* {@link UrlDownloadFileMetadata} representing Metadata of a maven artifact.
*/
public class MavenArtifactMetadata implements UrlDownloadFileMetadata {

private final MvnArtifact mvnArtifact;

private final VersionIdentifier version;

private final OperatingSystem os;

private final SystemArchitecture arch;

MavenArtifactMetadata(MvnArtifact mvnArtifact) {

this(mvnArtifact, null, null);
}

MavenArtifactMetadata(MvnArtifact mvnArtifact, OperatingSystem os, SystemArchitecture arch) {

this.mvnArtifact = mvnArtifact;
this.version = VersionIdentifier.of(mvnArtifact.getVersion());
this.os = os;
this.arch = arch;
}

/**
* @return the {@link MvnArtifact}.
*/
public MvnArtifact getMvnArtifact() {

return this.mvnArtifact;
}

@Override
public String getTool() {

return this.mvnArtifact.getGroupId();
}

@Override
public String getEdition() {

return this.mvnArtifact.getArtifactId();
}

@Override
public VersionIdentifier getVersion() {

return this.version;
}

@Override
public Set<String> getUrls() {

return Collections.singleton(this.mvnArtifact.getDownloadUrl());
}

@Override
public OperatingSystem getOs() {

return this.os;
}

@Override
public SystemArchitecture getArch() {

return this.arch;
}

@Override
public String getChecksum() {

return null;
}
}
Loading