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

Support for full semantic versioning in LSP artifacts #329

Merged
merged 3 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion plugin/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
<classpathentry excluding="main/resources/|test/resources/" kind="src" path="src"/>
<classpathentry kind="src" path="target/generated-sources"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
</classpath>
1 change: 1 addition & 0 deletions plugin/.settings/org.eclipse.jdt.core.prefs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
1 change: 1 addition & 0 deletions plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Bundle-Classpath: target/classes/,
target/dependency/jackson-databind-2.17.2.jar,
target/dependency/jakarta.inject-api-2.0.1.jar,
target/dependency/json-utils-2.28.26.jar,
target/dependency/maven-artifact-3.9.9.jar,
target/dependency/metrics-spi-2.28.26.jar,
target/dependency/netty-nio-client-2.28.26.jar,
target/dependency/nimbus-jose-jwt-9.41.2.jar,
Expand Down
8 changes: 6 additions & 2 deletions plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.41.2</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>3.9.9</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down Expand Up @@ -134,7 +139,7 @@
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependency</outputDirectory>
<includeGroupIds>software.amazon.awssdk,com.fasterxml.jackson,com.nimbusds,jakarta.inject,commons-codec,org.apache.httpcomponents,org.reactivestreams</includeGroupIds>
<includeGroupIds>software.amazon.awssdk,com.fasterxml.jackson,com.nimbusds,jakarta.inject,commons-codec,org.apache.httpcomponents,org.reactivestreams,org.apache.maven</includeGroupIds>
</configuration>
</execution>
<execution>
Expand Down Expand Up @@ -207,7 +212,6 @@
</execution>
</executions>
<configuration>
<argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
<testSourceDirectory>${project.build.testSourceDirectory}</testSourceDirectory>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

import java.nio.file.Paths;

import org.eclipse.osgi.service.resolver.VersionRange;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;

import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException;

public final class LspConstants {
private LspConstants() {
Expand All @@ -25,5 +28,13 @@ private LspConstants() {
public static final String LSP_SUBDIRECTORY = "lsp";
public static final String AMAZONQ_LSP_SUBDIRECTORY = Paths.get(LSP_SUBDIRECTORY, "AmazonQ").toString();

public static final VersionRange LSP_SUPPORTED_VERSION_RANGE = new VersionRange("[3.0.0, 3.0.10)");
public static final VersionRange LSP_SUPPORTED_VERSION_RANGE = createVersionRange();

private static VersionRange createVersionRange() {
try {
return VersionRange.createFromVersionSpec("[3.0.0, 3.0.10)");
} catch (InvalidVersionSpecificationException e) {
throw new AmazonQPluginException("Failed to parse LSP supported version range", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
import java.util.zip.ZipFile;

import org.apache.commons.codec.digest.DigestUtils;
import org.osgi.framework.Version;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;

import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException;
import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
Expand Down Expand Up @@ -60,8 +61,8 @@ public static void copyDirectory(final Path source, final Path target) throws IO
});
}

public static Version parseVersion(final String versionString) {
return new Version(versionString);
public static ArtifactVersion parseVersion(final String versionString) {
return new DefaultArtifactVersion(versionString);
}

public static boolean validateHash(final Path file, final List<String> hashes, final boolean strict) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
import java.util.Optional;
import java.util.stream.Collectors;

import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;

import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException;
import software.aws.toolkits.eclipse.amazonq.exception.LspError;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspConstants;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspFetchResult;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.model.ArtifactVersion;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.model.ManifestArtifactVersion;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.model.Content;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.model.Manifest;
import software.aws.toolkits.eclipse.amazonq.lsp.manager.model.Target;
Expand Down Expand Up @@ -187,7 +188,7 @@ private void logMessageWithLicense(final String message, final String attributio
}
}

private Optional<ArtifactVersion> resolveVersion(final Manifest manifestFile, final PluginPlatform platform,
private Optional<ManifestArtifactVersion> resolveVersion(final Manifest manifestFile, final PluginPlatform platform,
final PluginArchitecture architecture, final Instant start) {
if (manifestFile == null) {
String failureReason = "No valid manifest version data was received. An error could have caused this. Please check logs.";
Expand All @@ -201,8 +202,8 @@ private Optional<ArtifactVersion> resolveVersion(final Manifest manifestFile, fi
.filter(version -> version.targets().stream()
.anyMatch(target -> hasRequiredTargetContent(target, platform, architecture)))
.max(Comparator.comparing(
artifactVersion -> Version.parseVersion(artifactVersion.serverVersion())
));
artifactVersion -> new DefaultArtifactVersion(artifactVersion.serverVersion())
));
}

private boolean hasRequiredTargetContent(final Target target, final PluginPlatform platform, final PluginArchitecture architecture) {
Expand All @@ -215,11 +216,11 @@ private boolean isCompatibleTarget(final Target target, final PluginPlatform pla
return target.platform().equalsIgnoreCase(platform.getValue()) && target.arch().equalsIgnoreCase(architecture.getValue());
}

private boolean isCompatibleVersion(final ArtifactVersion version) {
return versionRange.includes(ArtifactUtils.parseVersion(version.serverVersion())) && !version.isDelisted();
private boolean isCompatibleVersion(final ManifestArtifactVersion version) {
return versionRange.containsVersion(ArtifactUtils.parseVersion(version.serverVersion())) && !version.isDelisted();
}

private Optional<Target> resolveTarget(final Optional<ArtifactVersion> targetVersion, final PluginPlatform platform,
private Optional<Target> resolveTarget(final Optional<ManifestArtifactVersion> targetVersion, final PluginPlatform platform,
final PluginArchitecture architecture) {
return targetVersion.flatMap(version -> version.targets().stream()
.filter(target -> isCompatibleTarget(target, platform, architecture))
Expand Down Expand Up @@ -320,9 +321,9 @@ private Path getFallback(final String expectedLspVersion, final PluginPlatform p
// filter to get sorted list of compatible lsp versions that have a valid cache
var sortedCachedLspVersions = compatibleLspVersions.stream()
.filter(artifactVersion -> isValidCachedVersion(artifactVersion, expectedServerVersion, cachedVersions))
.sorted(Comparator.comparing(x -> Version.parseVersion(x.serverVersion()), Comparator.reverseOrder()))
.sorted((v1, v2) -> new DefaultArtifactVersion(v2.serverVersion())
.compareTo(new DefaultArtifactVersion(v1.serverVersion())))
.collect(Collectors.toList());

var fallbackDir = sortedCachedLspVersions.stream()
.map(x -> getValidLocalCacheDirectory(x, platform, architecture, destinationFolder))
.filter(Objects::nonNull).findFirst().orElse(null);
Expand All @@ -333,7 +334,7 @@ private Path getFallback(final String expectedLspVersion, final PluginPlatform p
* Validate the local cache directory of the given lsp version(matches expected hash)
* If valid return cache directory, else return null
*/
private Path getValidLocalCacheDirectory(final ArtifactVersion artifactVersion, final PluginPlatform platform,
private Path getValidLocalCacheDirectory(final ManifestArtifactVersion artifactVersion, final PluginPlatform platform,
final PluginArchitecture architecture, final Path destinationFolder) {
var target = resolveTarget(Optional.of(artifactVersion), platform, architecture);
if (!target.isPresent() || target.get().contents() == null || target.get().contents().isEmpty()) {
Expand All @@ -345,14 +346,14 @@ private Path getValidLocalCacheDirectory(final ArtifactVersion artifactVersion,
return hasValidCache ? cacheDir : null;
}

private boolean isValidCachedVersion(final ArtifactVersion lspVersion, final Version expectedServerVersion,
final List<Version> cachedVersions) {
private boolean isValidCachedVersion(final ManifestArtifactVersion lspVersion, final ArtifactVersion expectedServerVersion,
final List<ArtifactVersion> cachedVersions) {
var serverVersion = ArtifactUtils.parseVersion(lspVersion.serverVersion());

return cachedVersions.contains(serverVersion) && (serverVersion.compareTo(expectedServerVersion) <= 0);
}

private List<Version> getCachedVersions(final Path destinationFolder) {
private List<ArtifactVersion> getCachedVersions(final Path destinationFolder) {
try {
return Files.list(destinationFolder)
.filter(Files::isDirectory)
Expand All @@ -366,15 +367,15 @@ private List<Version> getCachedVersions(final Path destinationFolder) {
}
}

private Version getVersionedName(final String filename) {
private ArtifactVersion getVersionedName(final String filename) {
try {
return ArtifactUtils.parseVersion(filename);
} catch (Exception e) {
return null;
}
}

private List<ArtifactVersion> getCompatibleArtifactVersions() {
private List<ManifestArtifactVersion> getCompatibleArtifactVersions() {
return manifest.versions().stream()
.filter(version -> isCompatibleVersion(version))
.toList();
Expand All @@ -385,7 +386,8 @@ private void deleteDelistedVersions(final Path destinationFolder) {
var cachedVersions = getCachedVersions(destinationFolder);

// delete de-listed versions in the toolkit compatible version range
var delistedVersions = cachedVersions.stream().filter(x -> !compatibleVersions.contains(x) && versionRange.includes(x)).collect(Collectors.toList());
var delistedVersions = cachedVersions.stream()
.filter(x -> !compatibleVersions.contains(x) && versionRange.containsVersion(x)).collect(Collectors.toList());
if (delistedVersions.size() > 0) {
Activator.getLogger().info(String.format("Cleaning up %s cached de-listed versions for Amazon Q Language Server", delistedVersions.size()));
}
Expand All @@ -398,7 +400,7 @@ private void deleteExtraVersions(final Path destinationFolder) {
var cachedVersions = getCachedVersions(destinationFolder);
// delete extra versions in the compatible toolkit version range except highest 2 versions
var extraVersions = cachedVersions.stream()
.filter(x -> versionRange.includes(x))
.filter(x -> versionRange.containsVersion(x))
.sorted(Comparator.reverseOrder())
.skip(2)
.collect(Collectors.toList());
Expand All @@ -410,7 +412,7 @@ private void deleteExtraVersions(final Path destinationFolder) {
});
}

private void deleteCachedVersion(final Path destinationFolder, final Version version) {
private void deleteCachedVersion(final Path destinationFolder, final ArtifactVersion version) {
var versionPath = destinationFolder.resolve(version.toString());
ArtifactUtils.deleteDirectory(versionPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private Optional<Manifest> validateManifest(final String content) {
try {
var manifest = OBJECT_MAPPER.readValue(content, Manifest.class);
var version = ArtifactUtils.parseVersion(manifest.manifestSchemaVersion());
if (version.getMajor() == LspConstants.MANIFEST_MAJOR_VERSION) {
if (version.getMajorVersion() == LspConstants.MANIFEST_MAJOR_VERSION) {
return Optional.of(manifest);
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public record Manifest(
String artifactId,
String artifactDescription,
Boolean isManifestDeprecated,
@JsonSetter(nulls = Nulls.AS_EMPTY) List<ArtifactVersion> versions) {
@JsonSetter(nulls = Nulls.AS_EMPTY) List<ManifestArtifactVersion> versions) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;

public record ArtifactVersion(
public record ManifestArtifactVersion(
@JsonProperty(required = true) String serverVersion,
boolean isDelisted,
Runtime runtime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import java.net.http.HttpResponse;
import java.time.Duration;

import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.eclipse.mylyn.commons.ui.dialogs.AbstractNotificationPopup;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.Version;

import org.tukaani.xz.XZInputStream;

Expand All @@ -26,17 +26,17 @@

public final class UpdateUtils {
private static final String REQUEST_URL = "https://amazonq.eclipsetoolkit.amazonwebservices.com/artifacts.xml.xz";
private static Version mostRecentNotificationVersion;
private static Version remoteVersion;
private static Version localVersion;
private static ArtifactVersion mostRecentNotificationVersion;
private static ArtifactVersion remoteVersion;
private static ArtifactVersion localVersion;
private static final UpdateUtils INSTANCE = new UpdateUtils();

public static UpdateUtils getInstance() {
return INSTANCE;
}

private UpdateUtils() {
mostRecentNotificationVersion = Activator.getPluginStore().getObject(Constants.DO_NOT_SHOW_UPDATE_KEY, Version.class);
mostRecentNotificationVersion = Activator.getPluginStore().getObject(Constants.DO_NOT_SHOW_UPDATE_KEY, ArtifactVersion.class);
String localString = PluginClientMetadata.getInstance().getPluginVersion();
localVersion = ArtifactUtils.parseVersion(localString.substring(0, localString.lastIndexOf(".")));
}
Expand All @@ -62,7 +62,7 @@ public void checkForUpdate() {
}
}

private Version fetchRemoteArtifactVersion(final String repositoryUrl) {
private ArtifactVersion fetchRemoteArtifactVersion(final String repositoryUrl) {
HttpClient connection = HttpClientFactory.getInstance();
try {
HttpRequest request = HttpRequest.newBuilder()
Expand Down Expand Up @@ -117,7 +117,7 @@ private void showNotification() {
});
}

private static boolean remoteVersionIsGreater(final Version remote, final Version local) {
private static boolean remoteVersionIsGreater(final ArtifactVersion remote, final ArtifactVersion local) {
return remote.compareTo(local) > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ final void testPutAndGetKeySuccess(final String key) throws Exception {
assertEquals(value, pluginStore.get(key));
verifyNoInteractions(mockLogger);
}

@Test
void testPutOverridingValue() throws BackingStoreException {
String key = "testKey";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

package software.aws.toolkits.eclipse.amazonq.lsp.manager.fetcher;

import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.osgi.framework.Version;

import java.io.IOException;
import java.nio.file.FileSystem;
Expand Down Expand Up @@ -40,10 +40,19 @@ void testExtractFile(@TempDir final Path tempDir) throws IOException {

@Test
void testParseVersion() {
Version version = ArtifactUtils.parseVersion("1.2.3");
assertEquals(1, version.getMajor());
assertEquals(2, version.getMinor());
assertEquals(3, version.getMicro());
ArtifactVersion version = ArtifactUtils.parseVersion("1.2.3");
assertEquals(1, version.getMajorVersion());
assertEquals(2, version.getMinorVersion());
assertEquals(3, version.getIncrementalVersion());
}

@Test
void testParseVersionWithQualifier() {
ArtifactVersion version = ArtifactUtils.parseVersion("1.2.3-rc.1");
assertEquals(1, version.getMajorVersion());
assertEquals(2, version.getMinorVersion());
assertEquals(3, version.getIncrementalVersion());
assertEquals("rc.1", version.getQualifier());
}

@Test
Expand Down
Loading
Loading