From 584f8d925406ed16bc9e813315436fa2b273ead9 Mon Sep 17 00:00:00 2001 From: Anton Kosyakov Date: Wed, 6 Apr 2022 16:41:17 +0000 Subject: [PATCH] fix #6508: Allow users to define Jetbrains plugins to be installed on a given project --- .../gitpod-protocol/data/gitpod-schema.json | 15 ++ .../gitpod-protocol/go/gitpod-config-types.go | 10 + .../ide/jetbrains/backend-plugin/BUILD.yaml | 1 + .../backend-plugin/gradle.properties | 6 +- .../gitpod/jetbrains/remote/GitpodBranding.kt | 41 --- .../jetbrains/remote/GitpodCLIService.kt | 4 + .../gitpod/jetbrains/remote/GitpodManager.kt | 12 +- .../jetbrains/remote/GitpodProjectManager.kt | 10 +- .../src/main/resources/META-INF/plugin.xml | 7 +- components/ide/jetbrains/image/startup.sh | 12 +- .../ide/jetbrains/image/status/BUILD.yaml | 3 + components/ide/jetbrains/image/status/go.mod | 25 +- components/ide/jetbrains/image/status/go.sum | 32 ++- components/ide/jetbrains/image/status/main.go | 246 ++++++++++++++++-- 14 files changed, 327 insertions(+), 97 deletions(-) delete mode 100644 components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodBranding.kt diff --git a/components/gitpod-protocol/data/gitpod-schema.json b/components/gitpod-protocol/data/gitpod-schema.json index e6a733fc2b6d80..7becc3365cf851 100644 --- a/components/gitpod-protocol/data/gitpod-schema.json +++ b/components/gitpod-protocol/data/gitpod-schema.json @@ -247,6 +247,21 @@ } } }, + "jetbrains": { + "type": "object", + "description": "Configure JetBrains integration", + "deprecationMessage": "The 'jetbrains' property is experimental.", + "additionalProperties": false, + "properties": { + "plugins": { + "type": "array", + "description": "List of plugins which should be installed for users of this workspace. From the JetBrains Marketplace page, find a page of the required plugin, select 'Versions' tab, click any version to copy pluginId (short name such as org.rust.lang) of the plugin you want to install.", + "items": { + "type": "string" + } + } + } + }, "experimentalNetwork": { "type": "boolean", "deprecationMessage": "The 'experimentalNetwork' property is deprecated.", diff --git a/components/gitpod-protocol/go/gitpod-config-types.go b/components/gitpod-protocol/go/gitpod-config-types.go index 3a4a9fc3bc8801..e8e02376d2cfc0 100644 --- a/components/gitpod-protocol/go/gitpod-config-types.go +++ b/components/gitpod-protocol/go/gitpod-config-types.go @@ -52,6 +52,9 @@ type GitpodConfig struct { // Configure VS Code integration Vscode *Vscode `yaml:"vscode,omitempty"` + // Configure JetBrains integration + JetBrains *JetBrains `yaml:"jetbrains,omitempty"` + // Path to where the IDE's workspace should be opened. WorkspaceLocation string `yaml:"workspaceLocation,omitempty"` } @@ -142,6 +145,13 @@ type Vscode struct { Extensions []string `yaml:"extensions,omitempty"` } +// Configure JetBrains integration +type JetBrains struct { + + // List of plugins which should be installed for users of this workspace. From the JetBrains Marketplace page, find a page of the required plugin, select 'Versions' tab, click any version to copy pluginId (short name such as org.rust.lang) of the plugin you want to install. + Plugins []string `yaml:"plugins,omitempty"` +} + func (strct *Github) MarshalJSON() ([]byte, error) { buf := bytes.NewBuffer(make([]byte, 0)) buf.WriteString("{") diff --git a/components/ide/jetbrains/backend-plugin/BUILD.yaml b/components/ide/jetbrains/backend-plugin/BUILD.yaml index 9cc4e4017a26e0..de621855869f41 100644 --- a/components/ide/jetbrains/backend-plugin/BUILD.yaml +++ b/components/ide/jetbrains/backend-plugin/BUILD.yaml @@ -14,4 +14,5 @@ packages: - "src/main/resources/*" config: commands: + - ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "runPluginVerifier"] - ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "buildPlugin"] diff --git a/components/ide/jetbrains/backend-plugin/gradle.properties b/components/ide/jetbrains/backend-plugin/gradle.properties index d355d2740a4274..9eddc33c33a1a4 100644 --- a/components/ide/jetbrains/backend-plugin/gradle.properties +++ b/components/ide/jetbrains/backend-plugin/gradle.properties @@ -6,13 +6,13 @@ pluginName=gitpod-remote # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. pluginSinceBuild=213 -pluginUntilBuild=213.* +pluginUntilBuild=221.* # Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl # See https://jb.gg/intellij-platform-builds-list for available build versions. -pluginVerifierIdeVersions=2021.3.1 +pluginVerifierIdeVersions=2021.3, 2022.1 # IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties platformType=IU -platformVersion=213.6777.52 +platformVersion=221.4994-EAP-CANDIDATE-SNAPSHOT platformDownloadSources=true # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodBranding.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodBranding.kt deleted file mode 100644 index 0c84243d5f70a1..00000000000000 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodBranding.kt +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2022 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 - -import com.intellij.openapi.components.service -import com.intellij.remoteDev.customization.GatewayBranding -import io.gitpod.jetbrains.remote.icons.GitpodIcons -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.future.await -import kotlinx.coroutines.launch -import javax.swing.Icon - -class GitpodBranding : GatewayBranding { - - val manager = service() - - /* - TODO(ak) GITPOD_WORSPACE_ID is a subject to change - ideally we should not rely on it, but here `getName` is sync - alternatively we could precompute another env var based on supervisor info endpoint - before starting backend - */ - private var name = System.getenv("GITPOD_WORKSPACE_ID") ?: "Gitpod" - init { - GlobalScope.launch { - val info = manager.pendingInfo.await() - name = info.workspaceId - } - } - - override fun getIcon(): Icon { - return GitpodIcons.Logo - } - - override fun getName(): String { - return name - } - -} \ No newline at end of file diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodCLIService.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodCLIService.kt index c9355a9ab0c37c..a8fd8be4145cf6 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodCLIService.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodCLIService.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.client.ClientSessionsManager import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.util.application import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.http.FullHttpRequest import io.netty.handler.codec.http.QueryStringDecoder @@ -25,6 +26,9 @@ class GitpodCLIService : RestService() { override fun getServiceName() = SERVICE_NAME override fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String? { + if (application.isHeadlessEnvironment) { + return "not supported in headless mode" + } val operation = getStringParameter("op", urlDecoder) if (operation == "open") { val fileStr = getStringParameter("file", urlDecoder) diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt index 0751e064f8fc0d..4f46e722068409 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodManager.kt @@ -13,11 +13,13 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.extensions.PluginId import com.intellij.remoteDev.util.onTerminationOrNow +import com.intellij.util.application import com.jetbrains.rd.util.lifetime.Lifetime import git4idea.config.GitVcsApplicationSettings import io.gitpod.gitpodprotocol.api.GitpodClient import io.gitpod.gitpodprotocol.api.GitpodServerLauncher import io.gitpod.jetbrains.remote.services.HeartbeatService +import io.gitpod.jetbrains.remote.utils.Retrier.retry import io.gitpod.supervisor.api.* import io.gitpod.supervisor.api.Info.WorkspaceInfoResponse import io.gitpod.supervisor.api.Notification.* @@ -40,7 +42,6 @@ import java.time.Duration import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture import javax.websocket.DeploymentException -import io.gitpod.jetbrains.remote.utils.Retrier.retry @Service class GitpodManager : Disposable { @@ -60,6 +61,9 @@ class GitpodManager : Disposable { init { GlobalScope.launch { + if (application.isHeadlessEnvironment) { + return@launch + } try { val backendPort = BuiltInServerManager.getInstance().waitForStart().port val httpClient = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS) @@ -93,6 +97,9 @@ class GitpodManager : Disposable { private val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("Gitpod Notifications") private val notificationsJob = GlobalScope.launch { + if (application.isHeadlessEnvironment) { + return@launch + } val notifications = NotificationServiceGrpc.newStub(supervisorChannel) val futureNotifications = NotificationServiceGrpc.newFutureStub(supervisorChannel) while (isActive) { @@ -156,6 +163,9 @@ class GitpodManager : Disposable { val pendingInfo = CompletableFuture() private val infoJob = GlobalScope.launch { + if (application.isHeadlessEnvironment) { + return@launch + } try { // TODO(ak) replace retry with proper handling of grpc errors val infoResponse = retry(3) { diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodProjectManager.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodProjectManager.kt index 61ada23605629e..bff55823fdbc3f 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodProjectManager.kt +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodProjectManager.kt @@ -12,7 +12,6 @@ import com.intellij.openapi.project.ModuleListener import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.roots.ProjectRootManager import com.intellij.util.application @@ -26,10 +25,17 @@ class GitpodProjectManager( private val project: Project ) { + init { + configureSdks() + } + /** * It is a workaround for https://youtrack.jetbrains.com/issue/GTW-88 */ - init { + private fun configureSdks() { + if (application.isHeadlessEnvironment) { + return + } val pendingSdk = CompletableFuture() application.invokeLaterOnWriteThread { application.runWriteAction { 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 6fc647c9fa8298..4f4a2bcca020f9 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 @@ -8,11 +8,13 @@ io.gitpod.jetbrains.remote Gitpod Remote Gitpod + Provides integrations within a Gitpod workspace. + + com.intellij.modules.platform - @@ -22,9 +24,6 @@ - diff --git a/components/ide/jetbrains/image/startup.sh b/components/ide/jetbrains/image/startup.sh index 007cf2b7942adc..429a529224f7eb 100755 --- a/components/ide/jetbrains/image/startup.sh +++ b/components/ide/jetbrains/image/startup.sh @@ -8,14 +8,6 @@ set -euo pipefail # kill background jobs when the script exits trap "jobs -p | xargs -r kill" SIGINT SIGTERM EXIT -/ide-desktop/status "$1" "$2" & - -echo "Desktop IDE: Waiting for the content initializer ..." -until curl -sS "$SUPERVISOR_ADDR"/_supervisor/v1/status/content/wait/true | grep '"available":true' > /dev/null; do - sleep 1 -done -echo "Desktop IDE: Content available." - # instead put them into /ide-desktop/backend/bin/idea64.vmoptions # otherwise JB will complain to a user on each startup # by default remote dev already set -Xmx2048m, see /ide-desktop/backend/plugins/remote-dev-server/bin/launcher.sh @@ -33,6 +25,4 @@ export IJ_HOST_SYSTEM_BASE_DIR=/workspace/.cache/JetBrains # Enable host status endpoint export CWM_HOST_STATUS_OVER_HTTP_TOKEN=gitpod -/ide-desktop/backend/bin/remote-dev-server.sh run "$GITPOD_REPO_ROOT" - -echo "Desktop IDE startup script exited" +/ide-desktop/status "$1" "$2" \ No newline at end of file diff --git a/components/ide/jetbrains/image/status/BUILD.yaml b/components/ide/jetbrains/image/status/BUILD.yaml index d55c21cf29bca6..eb28e751aed174 100644 --- a/components/ide/jetbrains/image/status/BUILD.yaml +++ b/components/ide/jetbrains/image/status/BUILD.yaml @@ -9,6 +9,9 @@ packages: - CGO_ENABLED=0 - GOOS=linux deps: + - components/gitpod-protocol/go:lib - components/supervisor-api/go:lib + - components/common-go:lib config: packaging: app + buildCommand: ["go", "build", "-trimpath", "-ldflags", "-buildid= -w -s -X 'github.com/gitpod-io/gitpod/jetbrains/status.Version=commit-${__git_commit}'"] diff --git a/components/ide/jetbrains/image/status/go.mod b/components/ide/jetbrains/image/status/go.mod index d4ac213f2187c3..5f714badc5f72f 100644 --- a/components/ide/jetbrains/image/status/go.mod +++ b/components/ide/jetbrains/image/status/go.mod @@ -2,18 +2,33 @@ module github.com/gitpod-io/gitpod/jetbrains/status go 1.17 -replace github.com/gitpod-io/gitpod/supervisor/api => ../../../../supervisor-api/go // leeway - require github.com/gitpod-io/gitpod/supervisor/api v0.0.0-00010101000000-000000000000 require ( + github.com/golang/mock v1.6.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 // indirect +) + +require ( + github.com/gitpod-io/gitpod/common-go v0.0.0-00010101000000-000000000000 + github.com/gitpod-io/gitpod/gitpod-protocol v0.0.0-00010101000000-000000000000 github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 // indirect - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect - golang.org/x/text v0.3.5 // indirect + github.com/hashicorp/go-version v1.4.0 + golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced // indirect google.golang.org/grpc v1.39.1 google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v2 v2.4.0 ) + +replace github.com/gitpod-io/gitpod/common-go => ../../../../common-go // leeway + +replace github.com/gitpod-io/gitpod/gitpod-protocol => ../../../../gitpod-protocol/go // leeway + +replace github.com/gitpod-io/gitpod/supervisor/api => ../../../../supervisor-api/go // leeway diff --git a/components/ide/jetbrains/image/status/go.sum b/components/ide/jetbrains/image/status/go.sum index c1b38f9458f055..29b0051eed0e02 100644 --- a/components/ide/jetbrains/image/status/go.sum +++ b/components/ide/jetbrains/image/status/go.sum @@ -43,6 +43,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -67,6 +69,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -109,24 +113,38 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 h1:ajue7SzQMywqRjg2fK7dcpc0QhFGpTR2plWfV4EZWR4= github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0/go.mod h1:r1hZAcvfFXuYmcKyCJI9wlyOPIZUJl6FCB8Cpca/NLE= +github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4= +github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37 h1:marA1XQDC7N870zmSFIoHZpIUduK80USeY0Rkuflgp4= +github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37/go.mod h1:ZafdZgk/axhT1cvZAPOhw+95nz2I/Ra5qMlU4gTRwIo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -201,8 +219,9 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= +golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -228,6 +247,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -245,16 +265,18 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -298,6 +320,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -390,10 +413,13 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/components/ide/jetbrains/image/status/main.go b/components/ide/jetbrains/image/status/main.go index 0f99933c2af2be..0cc71906b0a251 100644 --- a/components/ide/jetbrains/image/status/main.go +++ b/components/ide/jetbrains/image/status/main.go @@ -1,32 +1,55 @@ -// Copyright (c) 2021 Gitpod GmbH. All rights reserved. +// Copyright (c) 2022 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 main import ( + "bytes" "context" "encoding/json" + "errors" "fmt" + "io" "io/ioutil" - "log" "net/http" "net/url" "os" + "os/exec" + "path/filepath" + "regexp" "time" - supervisor "github.com/gitpod-io/gitpod/supervisor/api" + "github.com/hashicorp/go-version" "golang.org/x/xerrors" "google.golang.org/grpc" + yaml "gopkg.in/yaml.v2" + + "github.com/gitpod-io/gitpod/common-go/log" + gitpod "github.com/gitpod-io/gitpod/gitpod-protocol" + supervisor "github.com/gitpod-io/gitpod/supervisor/api" ) const defaultBackendPort = "63342" -// proxy for the Code With Me status endpoints that transforms it into the supervisor status format. +var ( + // ServiceName is the name we use for tracing/logging. + ServiceName = "jetbrains-startup" + // Version of this service - set during build. + Version = "" +) + +const BackendPath = "/ide-desktop/backend" +const RemoteDevServer = BackendPath + "/bin/remote-dev-server.sh" +const ProductInfoPath = BackendPath + "/product-info.json" + +// JB startup entrypoint func main() { + log.Init(ServiceName, Version, true, false) + startTime := time.Now() + if len(os.Args) < 3 { - fmt.Printf("Usage: %s []\n", os.Args[0]) - os.Exit(1) + log.Fatalf("Usage: %s []\n", os.Args[0]) } port := os.Args[1] kind := os.Args[2] @@ -35,7 +58,32 @@ func main() { label = os.Args[3] } - errlog := log.New(os.Stderr, "JetBrains IDE status: ", log.LstdFlags) + backendVersion, err := resolveBackendVersion() + if err != nil { + log.WithError(err).Error("failed to resolve backend version") + return + } + + // wait until content ready + contentStatus, wsInfo, err := resolveWorkspaceInfo(context.Background()) + if err != nil || wsInfo == nil || contentStatus == nil || !contentStatus.Available { + log.WithError(err).WithField("wsInfo", wsInfo).WithField("cstate", contentStatus).Error("resolve workspace info failed") + return + } + log.WithField("cost", time.Now().Local().Sub(startTime).Milliseconds()).Info("content available") + + version_2022_1, _ := version.NewVersion("2022.1") + if version_2022_1.LessThanOrEqual(backendVersion) { + err = installPlugins(wsInfo) + installPluginsCost := time.Now().Local().Sub(startTime).Milliseconds() + if err != nil { + log.WithError(err).WithField("cost", installPluginsCost).Error("installing repo plugins: done") + } else { + log.WithField("cost", installPluginsCost).Info("installing repo plugins: done") + } + } + + go run(wsInfo) http.HandleFunc("/joinLink", func(w http.ResponseWriter, r *http.Request) { backendPort := r.URL.Query().Get("backendPort") @@ -44,7 +92,7 @@ func main() { } jsonLink, err := resolveJsonLink(backendPort) if err != nil { - errlog.Printf("cannot resolve join link: %v\n", err) + log.WithError(err).Error("cannot resolve join link") http.Error(w, err.Error(), http.StatusServiceUnavailable) return } @@ -55,9 +103,9 @@ func main() { if backendPort == "" { backendPort = defaultBackendPort } - jsonLink, err := resolveGatewayLink(backendPort) + jsonLink, err := resolveGatewayLink(backendPort, wsInfo) if err != nil { - errlog.Printf("cannot resolve gateway link: %v\n", err) + log.WithError(err).Error("cannot resolve gateway link") http.Error(w, err.Error(), http.StatusServiceUnavailable) return } @@ -68,9 +116,9 @@ func main() { if backendPort == "" { backendPort = defaultBackendPort } - gatewayLink, err := resolveGatewayLink(backendPort) + gatewayLink, err := resolveGatewayLink(backendPort, wsInfo) if err != nil { - errlog.Printf("cannot resolve gateway link: %v\n", err) + log.WithError(err).Error("cannot resolve gateway link") http.Error(w, err.Error(), http.StatusServiceUnavailable) return } @@ -96,11 +144,7 @@ type Response struct { Projects []Projects `json:"projects"` } -func resolveGatewayLink(backendPort string) (string, error) { - wsInfo, err := resolveWorkspaceInfo(context.Background()) - if err != nil { - return "", err - } +func resolveGatewayLink(backendPort string, wsInfo *supervisor.WorkspaceInfoResponse) (string, error) { gitpodUrl, err := url.Parse(wsInfo.GitpodHost) if err != nil { return "", err @@ -141,19 +185,167 @@ func resolveJsonLink(backendPort string) (string, error) { return jsonResp.Projects[0].JoinLink, nil } -func resolveWorkspaceInfo(ctx context.Context) (*supervisor.WorkspaceInfoResponse, error) { - supervisorAddr := os.Getenv("SUPERVISOR_ADDR") - if supervisorAddr == "" { - supervisorAddr = "localhost:22999" +func resolveWorkspaceInfo(ctx context.Context) (*supervisor.ContentStatusResponse, *supervisor.WorkspaceInfoResponse, error) { + resolve := func(ctx context.Context) (contentStatus *supervisor.ContentStatusResponse, wsInfo *supervisor.WorkspaceInfoResponse, err error) { + supervisorAddr := os.Getenv("SUPERVISOR_ADDR") + if supervisorAddr == "" { + supervisorAddr = "localhost:22999" + } + supervisorConn, err := grpc.Dial(supervisorAddr, grpc.WithInsecure()) + if err != nil { + err = errors.New("dial supervisor failed: " + err.Error()) + return + } + defer supervisorConn.Close() + if wsInfo, err = supervisor.NewInfoServiceClient(supervisorConn).WorkspaceInfo(ctx, &supervisor.WorkspaceInfoRequest{}); err != nil { + err = errors.New("get workspace info failed: " + err.Error()) + return + } + contentStatus, err = supervisor.NewStatusServiceClient(supervisorConn).ContentStatus(ctx, &supervisor.ContentStatusRequest{Wait: true}) + if err != nil { + err = errors.New("get content available failed: " + err.Error()) + } + return + } + // try resolve workspace info 10 times + for attempt := 0; attempt < 10; attempt++ { + if contentStatus, wsInfo, err := resolve(ctx); err != nil { + log.WithError(err).Error("resolve workspace info failed") + time.Sleep(1 * time.Second) + } else { + return contentStatus, wsInfo, err + } + } + return nil, nil, errors.New("failed with attempt 10 times") +} + +func run(wsInfo *supervisor.WorkspaceInfoResponse) { + var args []string + args = append(args, "run") + args = append(args, wsInfo.GetCheckoutLocation()) + cmd := exec.Command(RemoteDevServer, args...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + log.WithError(err).Error("failed to run") } - supervisorConn, err := grpc.Dial(supervisorAddr, grpc.WithInsecure()) + os.Exit(cmd.ProcessState.ExitCode()) +} + +/** +{ + "buildNumber" : "221.4994.44", + "customProperties" : [ ], + "dataDirectoryName" : "IntelliJIdea2022.1", + "launch" : [ { + "javaExecutablePath" : "jbr/bin/java", + "launcherPath" : "bin/idea.sh", + "os" : "Linux", + "startupWmClass" : "jetbrains-idea", + "vmOptionsFilePath" : "bin/idea64.vmoptions" + } ], + "name" : "IntelliJ IDEA", + "productCode" : "IU", + "svgIconPath" : "bin/idea.svg", + "version" : "2022.1", + "versionSuffix" : "EAP" +} +*/ +type ProductInfo struct { + Version string `json:"version"` +} + +func resolveBackendVersion() (*version.Version, error) { + f, err := os.Open(ProductInfoPath) + if err != nil { + return nil, err + } + defer f.Close() + content, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + var info ProductInfo + err = json.Unmarshal(content, &info) + if err != nil { + return nil, err + } + return version.NewVersion(info.Version) +} + +func installPlugins(wsInfo *supervisor.WorkspaceInfoResponse) error { + plugins, err := getPlugins(wsInfo.GetCheckoutLocation()) + if err != nil { + return err + } + if len(plugins) <= 0 { + return nil + } + r, w, err := os.Pipe() if err != nil { - return nil, xerrors.Errorf("failed connecting to supervisor: %w", err) + return err + } + defer r.Close() + + outC := make(chan string) + go func() { + var buf bytes.Buffer + _, _ = io.Copy(&buf, r) + outC <- buf.String() + }() + + var args []string + args = append(args, "installPlugins") + args = append(args, wsInfo.GetCheckoutLocation()) + args = append(args, plugins...) + cmd := exec.Command(RemoteDevServer, args...) + cmd.Stderr = os.Stderr + cmd.Stdout = io.MultiWriter(w, os.Stdout) + installErr := cmd.Run() + + // delete alien_plugins.txt to suppress 3rd-party plugins consent on startup to workaround backend startup freeze + w.Close() + out := <-outC + configR := regexp.MustCompile("IDE config directory: (\\S+)\n") + matches := configR.FindStringSubmatch(out) + if len(matches) == 2 { + configDir := matches[1] + err := os.Remove(configDir + "/alien_plugins.txt") + if err != nil { + log.WithError(err).Error("failed to suppress 3rd-party plugins consent") + } + } + + if installErr != nil { + return errors.New("failed to install repo plugins: " + installErr.Error()) + } + return nil +} + +func getPlugins(repoRoot string) (plugins []string, err error) { + if repoRoot == "" { + err = errors.New("repoRoot is empty") + return } - defer supervisorConn.Close() - wsinfo, err := supervisor.NewInfoServiceClient(supervisorConn).WorkspaceInfo(ctx, &supervisor.WorkspaceInfoRequest{}) + data, err := os.ReadFile(filepath.Join(repoRoot, ".gitpod.yml")) if err != nil { - return nil, xerrors.Errorf("failed getting workspace info from supervisor: %w", err) + // .gitpod.yml not exist is ok + if errors.Is(err, os.ErrNotExist) { + err = nil + return + } + err = errors.New("read .gitpod.yml file failed: " + err.Error()) + return + } + var config *gitpod.GitpodConfig + if err = yaml.Unmarshal(data, &config); err != nil { + err = errors.New("unmarshal .gitpod.yml file failed" + err.Error()) + return + } + if config == nil || config.JetBrains == nil { + err = errors.New("config.vscode field not exists") + return } - return wsinfo, nil + return config.JetBrains.Plugins, nil }