diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt index e81c0beff83..171bbfad041 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt @@ -81,10 +81,10 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH, .sortedByDescending { (_, semVer) -> semVer } } - fun getExistingLspArtifacts(versions: List, target: VersionTarget?): Boolean { - if (versions.isEmpty() || target?.contents == null) return false + fun getExistingLspArtifacts(targetVersion: Version, target: VersionTarget): Boolean { + if (target.contents == null) return false - val localLSPPath = lspArtifactsPath.resolve(versions.first().serverVersion.toString()) + val localLSPPath = lspArtifactsPath.resolve(targetVersion.serverVersion.toString()) if (!localLSPPath.exists()) return false val hasInvalidFiles = target.contents.any { content -> @@ -105,9 +105,9 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH, return !hasInvalidFiles } - suspend fun tryDownloadLspArtifacts(project: Project, versions: List, target: VersionTarget?): Path? { + suspend fun tryDownloadLspArtifacts(project: Project, targetVersion: Version, target: VersionTarget): Path? { val temporaryDownloadPath = Files.createTempDirectory("lsp-dl") - val downloadPath = lspArtifactsPath.resolve(versions.first().serverVersion.toString()) + val downloadPath = lspArtifactsPath.resolve(targetVersion.serverVersion.toString()) while (currentAttempt.get() < maxDownloadAttempts) { currentAttempt.incrementAndGet() @@ -119,13 +119,19 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH, AwsCoreBundle.message("amazonqFeatureDev.placeholder.downloading_and_extracting_lsp_artifacts"), cancellable = true ) { - if (downloadLspArtifacts(temporaryDownloadPath, target) && target != null && !target.contents.isNullOrEmpty()) { + if (downloadLspArtifacts(temporaryDownloadPath, target) && !target.contents.isNullOrEmpty()) { moveFilesFromSourceToDestination(temporaryDownloadPath, downloadPath) target.contents .mapNotNull { it.filename } .forEach { filename -> extractZipFile(downloadPath.resolve(filename), downloadPath) } logger.info { "Successfully downloaded and moved LSP artifacts to $downloadPath" } + val thirdPartyLicenses = targetVersion.thirdPartyLicenses + logger.info { + "Installing Amazon Q Language Server v${targetVersion.serverVersion} to: $downloadPath. " + + if (thirdPartyLicenses == null) "" else "Attribution notice can be found at $thirdPartyLicenses" + } + return@withBackgroundProgress downloadPath } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt index 7b4e74199c3..b1c929e1aee 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt @@ -71,13 +71,15 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION) } + val targetVersion = lspVersions.inRangeVersions.first() + // If there is an LSP Manifest with the same version - val target = getTargetFromLspManifest(lspVersions.inRangeVersions) + val target = getTargetFromLspManifest(targetVersion) // Get Local LSP files and check if we can re-use existing LSP Artifacts - val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(lspVersions.inRangeVersions, target)) { + val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) { artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first } else { - artifactHelper.tryDownloadLspArtifacts(project, lspVersions.inRangeVersions, target) + artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target) ?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED) } artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE) @@ -111,18 +113,18 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe ) } - private fun getTargetFromLspManifest(versions: List): VersionTarget { + private fun getTargetFromLspManifest(targetVersion: Version): VersionTarget { val currentOS = getCurrentOS() val currentArchitecture = getCurrentArchitecture() - val currentTarget = versions.first().targets?.find { target -> + val currentTarget = targetVersion.targets?.find { target -> target.platform == currentOS && target.arch == currentArchitecture } if (currentTarget == null) { logger.error { "Failed to obtain target for $currentOS and $currentArchitecture" } - throw LspException("Target not found in the current Version: ${versions.first().serverVersion}", LspException.ErrorCode.TARGET_NOT_FOUND) + throw LspException("Target not found in the current Version: ${targetVersion.serverVersion}", LspException.ErrorCode.TARGET_NOT_FOUND) } - logger.info { "Target found in the current Version: ${versions.first().serverVersion}" } + logger.info { "Target found in the current Version: ${targetVersion.serverVersion}" } return currentTarget } } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ManifestFetcher.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ManifestFetcher.kt index a0932ef1c7d..ea85bf52e52 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ManifestFetcher.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ManifestFetcher.kt @@ -155,6 +155,8 @@ data class Version( val isDelisted: Boolean? = null, @JsonProperty("targets") val targets: List? = emptyList(), + @JsonProperty("thirdPartyLicenses") + val thirdPartyLicenses: String? = null, ) data class Manifest( diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt index 4a161857de4..cd061a2473a 100644 --- a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt +++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt @@ -120,16 +120,14 @@ class ArtifactHelperTest { serverZipPath.parent.toFile().mkdirs() serverZipPath.toFile().createNewFile() - val versions = listOf( - Version(serverVersion = "1.0.0") - ) + val version = Version(serverVersion = "1.0.0") val target = VersionTarget(contents = contents) mockkStatic("software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.LspUtilsKt") every { generateSHA384Hash(any()) } returns "1234" - val result = artifactHelper.getExistingLspArtifacts(versions, target) + val result = artifactHelper.getExistingLspArtifacts(version, target) assertThat(result).isTrue() assertThat(serverZipPath.toFile().exists()).isTrue() @@ -144,63 +142,55 @@ class ArtifactHelperTest { serverZipPath.parent.toFile().mkdirs() serverZipPath.toFile().createNewFile() - val versions = listOf( - Version(serverVersion = "1.0.0") - ) + val version = Version(serverVersion = "1.0.0") val target = VersionTarget(contents = contents) mockkStatic("software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.LspUtilsKt") every { generateSHA384Hash(any()) } returns "1235" - val result = artifactHelper.getExistingLspArtifacts(versions, target) + val result = artifactHelper.getExistingLspArtifacts(version, target) assertThat(result).isFalse() assertThat(serverZipPath.toFile().exists()).isFalse() } - @Test - fun `getExistingLspArtifacts should return false if versions are empty`() { - val versions = emptyList() - assertThat(artifactHelper.getExistingLspArtifacts(versions, null)).isFalse() - } - @Test fun `getExistingLspArtifacts should return false if target does not have contents`() { - val versions = listOf( - Version(serverVersion = "1.0.0") - ) - assertThat(artifactHelper.getExistingLspArtifacts(versions, null)).isFalse() + val version = Version(serverVersion = "1.0.0") + val target1 = VersionTarget(contents = emptyList()) + val target2 = VersionTarget(contents = null) + assertThat(artifactHelper.getExistingLspArtifacts(version, target1)).isFalse() + assertThat(artifactHelper.getExistingLspArtifacts(version, target2)).isFalse() } @Test fun `getExistingLspArtifacts should return false if Lsp path does not exist`() { - val versions = listOf( - Version(serverVersion = "1.0.0") - ) - assertThat(artifactHelper.getExistingLspArtifacts(versions, null)).isFalse() + val versions = Version(serverVersion = "1.0.0") + val target = VersionTarget(contents = contents) + assertThat(artifactHelper.getExistingLspArtifacts(versions, target)).isFalse() } @Test fun `tryDownloadLspArtifacts should not download artifacts if target does not have contents`() { - val versions = listOf(Version(serverVersion = "2.0.0")) - assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, versions, null) }).isEqualTo(null) + val versions = Version(serverVersion = "2.0.0") + assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, versions, VersionTarget()) }).isEqualTo(null) assertThat(tempDir.resolve("2.0.0").toFile().exists()).isFalse() } @Test fun `tryDownloadLspArtifacts should throw error if failed to download`() { - val versions = listOf(Version(serverVersion = "1.0.0")) + val version = Version(serverVersion = "1.0.0") val spyArtifactHelper = spyk(artifactHelper) every { spyArtifactHelper.downloadLspArtifacts(any(), any()) } returns false - assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, versions, null) }).isEqualTo(null) + assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, version, VersionTarget(contents = contents)) }).isEqualTo(null) } @Test fun `tryDownloadLspArtifacts should throw error after attempts are exhausted`() { - val versions = listOf(Version(serverVersion = "1.0.0")) + val version = Version(serverVersion = "1.0.0") val target = VersionTarget(contents = contents) val spyArtifactHelper = spyk(artifactHelper) @@ -209,7 +199,7 @@ class ArtifactHelperTest { every { moveFilesFromSourceToDestination(any(), any()) } just Runs every { extractZipFile(any(), any()) } just Runs - assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, versions, target) }).isEqualTo(null) + assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, version, target) }).isEqualTo(null) } @Test