Skip to content

Commit 6ef5842

Browse files
authored
feat(amazonq): fallback to bundled flare assets if cannot be downloaded (#5772)
1 parent 535b8e2 commit 6ef5842

File tree

5 files changed

+59
-48
lines changed

5 files changed

+59
-48
lines changed

plugins/amazonq/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ val prepareBundledFlare by tasks.registering(Copy::class) {
115115

116116
val dest = layout.buildDirectory.dir("tmp/extractFlare")
117117
into(dest)
118-
119118
from(downloadFlareArtifacts.map { it.outputFiles.filterNot { file -> file.name.endsWith(".zip") } })
119+
120120
doLast {
121121
copy {
122122
into(dest)

plugins/amazonq/shared/jetbrains-community/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,12 @@ dependencies {
2424

2525
testFixturesApi(testFixtures(project(":plugin-core:jetbrains-community")))
2626
}
27+
28+
// hack because our test structure currently doesn't make complete sense
29+
tasks.prepareTestSandbox {
30+
val pluginXmlJar = project(":plugin-amazonq").tasks.jar
31+
32+
dependsOn(pluginXmlJar)
33+
intoChild(intellijPlatform.projectName.map { "$it/lib" })
34+
.from(pluginXmlJar)
35+
}

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import org.jetbrains.annotations.VisibleForTesting
1616
import software.aws.toolkits.core.utils.error
1717
import software.aws.toolkits.core.utils.getLogger
1818
import software.aws.toolkits.core.utils.info
19+
import software.aws.toolkits.core.utils.warn
20+
import software.aws.toolkits.jetbrains.AwsPlugin
21+
import software.aws.toolkits.jetbrains.AwsToolkit
1922
import java.nio.file.Path
2023

2124
@Service
@@ -54,36 +57,43 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe
5457
return mutex.withLock {
5558
coroutineScope {
5659
async {
57-
val manifest = manifestFetcher.fetch() ?: throw LspException(
58-
"Language Support is not available, as manifest is missing.",
59-
LspException.ErrorCode.MANIFEST_FETCH_FAILED
60-
)
61-
val lspVersions = getLSPVersionsFromManifestWithSpecifiedRange(manifest)
62-
63-
artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
64-
65-
if (lspVersions.inRangeVersions.isEmpty()) {
66-
// No versions are found which are in the given range. Fallback to local lsp artifacts.
67-
val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
68-
if (localLspArtifacts.isNotEmpty()) {
69-
return@async localLspArtifacts.first().first
60+
try {
61+
val manifest = manifestFetcher.fetch() ?: throw LspException(
62+
"Language Support is not available, as manifest is missing.",
63+
LspException.ErrorCode.MANIFEST_FETCH_FAILED
64+
)
65+
val lspVersions = getLSPVersionsFromManifestWithSpecifiedRange(manifest)
66+
67+
artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
68+
69+
if (lspVersions.inRangeVersions.isEmpty()) {
70+
// No versions are found which are in the given range. Fallback to local lsp artifacts.
71+
val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
72+
if (localLspArtifacts.isNotEmpty()) {
73+
return@async localLspArtifacts.first().first
74+
}
75+
throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
7076
}
71-
throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
72-
}
7377

74-
val targetVersion = lspVersions.inRangeVersions.first()
78+
val targetVersion = lspVersions.inRangeVersions.first()
7579

76-
// If there is an LSP Manifest with the same version
77-
val target = getTargetFromLspManifest(targetVersion)
78-
// Get Local LSP files and check if we can re-use existing LSP Artifacts
79-
val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
80-
artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
81-
} else {
82-
artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
83-
?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
80+
// If there is an LSP Manifest with the same version
81+
val target = getTargetFromLspManifest(targetVersion)
82+
// Get Local LSP files and check if we can re-use existing LSP Artifacts
83+
val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
84+
artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
85+
} else {
86+
artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
87+
?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
88+
}
89+
artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
90+
return@async artifactPath
91+
} catch (e: Exception) {
92+
logger.warn(e) { "Failed to resolve assets from Flare CDN" }
93+
val path = AwsToolkit.PLUGINS_INFO[AwsPlugin.Q]?.path?.resolve("flare") ?: error("not even bundled")
94+
logger.info { "Falling back to bundled assets at $path" }
95+
return@async path
8496
}
85-
artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
86-
return@async artifactPath
8797
}
8898
}.also {
8999
artifactDeferred = it

plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManagerTest.kt

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts
55

6+
import com.intellij.ide.plugins.PluginManagerCore
7+
import com.intellij.openapi.extensions.PluginId
68
import com.intellij.testFramework.ProjectExtension
79
import com.intellij.util.text.SemVer
810
import io.mockk.Runs
@@ -17,7 +19,6 @@ import kotlinx.coroutines.test.runTest
1719
import org.assertj.core.api.Assertions.assertThat
1820
import org.junit.jupiter.api.BeforeEach
1921
import org.junit.jupiter.api.Test
20-
import org.junit.jupiter.api.assertThrows
2122
import org.junit.jupiter.api.extension.RegisterExtension
2223
import org.junit.jupiter.api.io.TempDir
2324
import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactManager.SupportedManifestVersionRange
@@ -51,29 +52,27 @@ class ArtifactManagerTest {
5152
}
5253

5354
@Test
54-
fun `fetch artifact fetcher throws exception if manifest is null`() = runTest {
55+
fun `fetch artifact fetcher returns bundled if manifest is null`() = runTest {
5556
every { manifestFetcher.fetch() }.returns(null)
5657

57-
val exception = assertThrows<LspException> {
58-
artifactManager.fetchArtifact(projectExtension.project)
59-
}
60-
assertThat(exception)
61-
.hasFieldOrPropertyWithValue("errorCode", LspException.ErrorCode.MANIFEST_FETCH_FAILED)
58+
assertThat(artifactManager.fetchArtifact(projectExtension.project))
59+
.isEqualTo(
60+
PluginManagerCore.getPlugin(PluginId.getId("amazon.q"))?.pluginPath?.resolve("flare")
61+
)
6262
}
6363

6464
@Test
65-
fun `fetch artifact does not have any valid lsp versions`() = runTest {
65+
fun `fetch artifact does not have any valid lsp versions returns bundled`() = runTest {
6666
every { manifestFetcher.fetch() }.returns(Manifest())
6767

6868
every { artifactManager.getLSPVersionsFromManifestWithSpecifiedRange(any()) }.returns(
6969
ArtifactManager.LSPVersions(deListedVersions = emptyList(), inRangeVersions = emptyList())
7070
)
7171

72-
val exception = assertThrows<LspException> {
73-
artifactManager.fetchArtifact(projectExtension.project)
74-
}
75-
assertThat(exception)
76-
.hasFieldOrPropertyWithValue("errorCode", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
72+
assertThat(artifactManager.fetchArtifact(projectExtension.project))
73+
.isEqualTo(
74+
PluginManagerCore.getPlugin(PluginId.getId("amazon.q"))?.pluginPath?.resolve("flare")
75+
)
7776
}
7877

7978
@Test

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/AwsToolkit.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
package software.aws.toolkits.jetbrains
55

66
import com.intellij.ide.plugins.PluginManagerCore
7-
import com.intellij.openapi.application.ApplicationManager
87
import com.intellij.openapi.extensions.PluginDescriptor
98
import com.intellij.openapi.extensions.PluginId
109
import java.nio.file.Path
11-
import java.nio.file.Paths
1210
import java.util.EnumMap
1311

1412
object AwsToolkit {
@@ -37,12 +35,7 @@ data class PluginInfo(val id: String, val name: String) {
3735
val version: String?
3836
get() = descriptor?.version
3937
val path: Path?
40-
get() =
41-
if (ApplicationManager.getApplication().isUnitTestMode) {
42-
Paths.get(System.getProperty("plugin.path"))
43-
} else {
44-
descriptor?.pluginPath
45-
}
38+
get() = descriptor?.pluginPath
4639
}
4740

4841
enum class AwsPlugin {

0 commit comments

Comments
 (0)