From 4c8d231b1cdd0df040c509b945dcdca1a5961f0b Mon Sep 17 00:00:00 2001 From: Huiwen Date: Tue, 3 Jun 2025 09:15:07 +0000 Subject: [PATCH 1/4] [JetBrains] Improve ports listener leak --- .../AbstractGitpodPortForwardingService.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt index b17993601bef78..469a21f8d62d06 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt @@ -13,6 +13,7 @@ import com.intellij.util.application import com.jetbrains.rd.platform.codeWithMe.portForwarding.* import com.jetbrains.rd.util.URI import com.jetbrains.rd.util.lifetime.Lifetime +import fleet.util.async.throttleLatest import io.gitpod.supervisor.api.Status import io.gitpod.supervisor.api.Status.PortsStatus import io.gitpod.supervisor.api.StatusServiceGrpc @@ -20,9 +21,12 @@ import io.grpc.stub.ClientCallStreamObserver import io.grpc.stub.ClientResponseObserver import kotlinx.coroutines.* import kotlinx.coroutines.future.asDeferred +import kotlinx.coroutines.flow.MutableSharedFlow import org.apache.http.client.utils.URIBuilder import java.util.* import java.util.concurrent.CompletableFuture +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext @Suppress("UnstableApiUsage") abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService { @@ -34,8 +38,22 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService private val perClientPortForwardingManager = service() private val ignoredPortsForNotificationService = service() private val lifetime = Lifetime.Eternal.createNested() + private val portStatusFlow = MutableSharedFlow() + + init { + // Start collecting port status updates with throttling + runJob(lifetime) { + portStatusFlow + .throttleLatest(1000) // Throttle to 1 second + .collect { response -> + withContext(Dispatchers.IO) { + syncPortsListWithClient(response) + } + } + } - init { start() } + start() + } private fun start() { if (application.isHeadlessEnvironment) return @@ -86,7 +104,11 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService } override fun onNext(response: Status.PortsStatusResponse) { - application.invokeLater { syncPortsListWithClient(response) } + application.invokeLater { + runJob(lifetime) { + portStatusFlow.emit(response) + } + } } override fun onCompleted() { From 5d51d5c236bd663a76599a628328fb48059831ef Mon Sep 17 00:00:00 2001 From: Huiwen Date: Wed, 4 Jun 2025 07:16:06 +0000 Subject: [PATCH 2/4] Fix rider build --- .../backend-plugin/build.gradle-stable.kts | 1 + .../AbstractGitpodPortForwardingService.kt | 11 ++++++----- .../remote/GitpodPortForwardingServiceImpl.kt | 5 ++++- .../GitpodRiderPortForwardingService.kt | 18 ++++++++++++++++++ .../resources-latest/META-INF/extensions.xml | 3 +++ .../main/resources-rider/META-INF/plugin.xml | 3 +++ .../resources-stable/META-INF/extensions.xml | 3 +++ .../src/main/resources/META-INF/plugin.xml | 3 --- 8 files changed, 38 insertions(+), 9 deletions(-) create mode 100644 components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/optional/GitpodRiderPortForwardingService.kt diff --git a/components/ide/jetbrains/backend-plugin/build.gradle-stable.kts b/components/ide/jetbrains/backend-plugin/build.gradle-stable.kts index 810a2b0edeea41..8aaa5ab6a6abab 100644 --- a/components/ide/jetbrains/backend-plugin/build.gradle-stable.kts +++ b/components/ide/jetbrains/backend-plugin/build.gradle-stable.kts @@ -38,6 +38,7 @@ project(":") { if (properties("platformType") == "RD") { print("Rider: exclude unnecessary files") sourceSets["main"].kotlin.exclude("**/GitpodForceUpdateMavenProjectsActivity.kt") + sourceSets["main"].kotlin.exclude("**/GitpodPortForwardingServiceImpl.kt") sourceSets["main"].kotlin.exclude("**/maven.xml") } } diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt index 469a21f8d62d06..9b415100ea9717 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/AbstractGitpodPortForwardingService.kt @@ -13,7 +13,6 @@ import com.intellij.util.application import com.jetbrains.rd.platform.codeWithMe.portForwarding.* import com.jetbrains.rd.util.URI import com.jetbrains.rd.util.lifetime.Lifetime -import fleet.util.async.throttleLatest import io.gitpod.supervisor.api.Status import io.gitpod.supervisor.api.Status.PortsStatus import io.gitpod.supervisor.api.StatusServiceGrpc @@ -41,10 +40,10 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService private val portStatusFlow = MutableSharedFlow() init { - // Start collecting port status updates with throttling + // Start collecting port status updates runJob(lifetime) { portStatusFlow - .throttleLatest(1000) // Throttle to 1 second + .let { flow -> applyThrottling(flow) } .collect { response -> withContext(Dispatchers.IO) { syncPortsListWithClient(response) @@ -55,6 +54,10 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService start() } + protected abstract fun runJob(lifetime: Lifetime, block: suspend CoroutineScope.() -> Unit): Job; + + protected abstract suspend fun applyThrottling(flow: kotlinx.coroutines.flow.Flow): kotlinx.coroutines.flow.Flow + private fun start() { if (application.isHeadlessEnvironment) return @@ -65,8 +68,6 @@ abstract class AbstractGitpodPortForwardingService : GitpodPortForwardingService observePortsListWhileProjectIsOpen() } - protected abstract fun runJob(lifetime: Lifetime, block: suspend CoroutineScope.() -> Unit): Job; - private fun observePortsListWhileProjectIsOpen() = runJob(lifetime) { while (isActive) { try { diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodPortForwardingServiceImpl.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodPortForwardingServiceImpl.kt index 58f8726b674498..0715eb36a2f656 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodPortForwardingServiceImpl.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodPortForwardingServiceImpl.kt @@ -6,10 +6,13 @@ package io.gitpod.jetbrains.remote import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.threading.coroutines.launch -import io.gitpod.jetbrains.remote.AbstractGitpodPortForwardingService import kotlinx.coroutines.CoroutineScope +import fleet.util.async.throttleLatest +import kotlinx.coroutines.flow.Flow @Suppress("UnstableApiUsage") class GitpodPortForwardingServiceImpl : AbstractGitpodPortForwardingService() { override fun runJob(lifetime: Lifetime, block: suspend CoroutineScope.() -> Unit) = lifetime.launch { block() } + + override suspend fun applyThrottling(flow: Flow): Flow = flow.throttleLatest(1000) } diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/optional/GitpodRiderPortForwardingService.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/optional/GitpodRiderPortForwardingService.kt new file mode 100644 index 00000000000000..00842c4391954c --- /dev/null +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/optional/GitpodRiderPortForwardingService.kt @@ -0,0 +1,18 @@ +// Copyright (c) 2025 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License.AGPL.txt in the project root for license information. + +package io.gitpod.jetbrains.remote.optional + +import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rd.util.threading.coroutines.launch +import io.gitpod.jetbrains.remote.AbstractGitpodPortForwardingService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow + +@Suppress("UnstableApiUsage") +class GitpodRiderPortForwardingService : AbstractGitpodPortForwardingService() { + override fun runJob(lifetime: Lifetime, block: suspend CoroutineScope.() -> Unit) = lifetime.launch { block() } + + override suspend fun applyThrottling(flow: Flow): Flow = flow +} diff --git a/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml b/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml index 9d4b9661deb26b..b031ffe112b4d6 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml +++ b/components/ide/jetbrains/backend-plugin/src/main/resources-latest/META-INF/extensions.xml @@ -6,5 +6,8 @@ + diff --git a/components/ide/jetbrains/backend-plugin/src/main/resources-rider/META-INF/plugin.xml b/components/ide/jetbrains/backend-plugin/src/main/resources-rider/META-INF/plugin.xml index 5f84f78fc57d4b..a5b6489628e1a7 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/resources-rider/META-INF/plugin.xml +++ b/components/ide/jetbrains/backend-plugin/src/main/resources-rider/META-INF/plugin.xml @@ -50,6 +50,9 @@ description="Disable the forced update of Maven projects when IDE opens." restartRequired="true"/> + + diff --git a/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml b/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml index afe1d021a87338..418c8345c00e0b 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml +++ b/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml @@ -55,9 +55,6 @@ preload="true"/> - From 92cb05513d6c4b9c28ac11a6bb7491137d28ade2 Mon Sep 17 00:00:00 2001 From: Huiwen Date: Mon, 9 Jun 2025 07:58:09 +0000 Subject: [PATCH 3/4] Pin rider 's backend plugin image --- .../pkg/components/ide-service/ide_config_configmap.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/install/installer/pkg/components/ide-service/ide_config_configmap.go b/install/installer/pkg/components/ide-service/ide_config_configmap.go index 023829ceb398bf..69dcc1d8b1c211 100644 --- a/install/installer/pkg/components/ide-service/ide_config_configmap.go +++ b/install/installer/pkg/components/ide-service/ide_config_configmap.go @@ -75,10 +75,12 @@ func GenerateIDEConfigmap(ctx *common.RenderContext) (*ide_config.IDEConfig, err CodeHelperImage: ctx.ImageName(ctx.Config.Repository, ide.CodeHelperIDEImage, ctx.VersionManifest.Components.Workspace.CodeHelperImage.Version), CodeWebExtensionImage: ctx.ImageName(ctx.Config.Repository, ide.CodeWebExtensionImage, ctx.VersionManifest.Components.Workspace.CodeWebExtensionImage.Version), - JetBrainsPluginImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginImage.Version), - JetBrainsPluginLatestImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginLatestImage.Version), - JetBrainsPluginRiderImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginRiderImage.Version), - JetBrainsPluginLatestRiderImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginLatestRiderImage.Version), + JetBrainsPluginImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginImage.Version), + JetBrainsPluginLatestImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginLatestImage.Version), + + // Pin Rider's backend-plugin version as we don't plan to upgrade Rider yet + JetBrainsPluginRiderImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, "commit-8cdc2a1a45c66ac9e003a97af8c57fad42d923f6-rider"), + JetBrainsPluginLatestRiderImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsBackendPluginImage, "commit-8cdc2a1a45c66ac9e003a97af8c57fad42d923f6-rider-latest"), JetBrainsLauncherImage: ctx.ImageName(ctx.Config.Repository, ide.JetBrainsLauncherImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsLauncherImage.Version), ResolvedJBImageLatest: JBImages{ IntelliJ: resolveLatestImage(ide.IntelliJDesktopIDEImage, "latest", ctx.VersionManifest.Components.Workspace.DesktopIdeImages.IntelliJLatestImage), From b98bdfe36f7743f2713d551b8ab394f785527d16 Mon Sep 17 00:00:00 2001 From: Huiwen Date: Wed, 11 Jun 2025 07:50:28 +0000 Subject: [PATCH 4/4] Fix rider image in preview env --- dev/preview/workflow/preview/patch-ide-configmap.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dev/preview/workflow/preview/patch-ide-configmap.js b/dev/preview/workflow/preview/patch-ide-configmap.js index 42202a40be96be..4d6be9857b8b6b 100644 --- a/dev/preview/workflow/preview/patch-ide-configmap.js +++ b/dev/preview/workflow/preview/patch-ide-configmap.js @@ -12,6 +12,8 @@ function replaceImage(image) { const imagesBuildFromMain = [ "jb-backend-plugin:commit-2d67254d5aa110bc2c76cd807b85b272e3d54d97-latest", "jb-backend-plugin:commit-4c69ad0670cc4cfbf43910e1db700ad90acd5ac6", + "jb-backend-plugin:commit-8cdc2a1a45c66ac9e003a97af8c57fad42d923f6-rider", + "jb-backend-plugin:commit-8cdc2a1a45c66ac9e003a97af8c57fad42d923f6-rider-latest", ]; // TODO(hw): remove me @@ -43,6 +45,11 @@ for (let ide in json.ideOptions.options) { json.ideOptions.options[ide].imageLayers = json.ideOptions.options[ide].imageLayers.map(replaceImage2); } + if (["rider"].includes(ide)) { + json.ideOptions.options[ide].pluginImage = replaceImage2(json.ideOptions.options[ide].pluginImage); + json.ideOptions.options[ide].imageLayers = json.ideOptions.options[ide].imageLayers.map(replaceImage2); + } + if (["code", "code1_85"].includes(ide)) { json.ideOptions.options[ide].image = replaceImage(json.ideOptions.options[ide].image); json.ideOptions.options[ide].imageLayers = json.ideOptions.options[ide].imageLayers.map(replaceImage);