Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
.sortedByDescending { (_, semVer) -> semVer }
}

fun getExistingLspArtifacts(versions: List<Version>, 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 ->
Expand All @@ -105,9 +105,9 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
return !hasInvalidFiles
}

suspend fun tryDownloadLspArtifacts(project: Project, versions: List<Version>, 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()
Expand All @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -111,18 +113,18 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe
)
}

private fun getTargetFromLspManifest(versions: List<Version>): 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ data class Version(
val isDelisted: Boolean? = null,
@JsonProperty("targets")
val targets: List<VersionTarget>? = emptyList(),
@JsonProperty("thirdPartyLicenses")
val thirdPartyLicenses: String? = null,
)

data class Manifest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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<Version>()
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)

Expand All @@ -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
Expand Down
Loading