Skip to content

When using the Latest Release of JetBrains IDEs, if the workspace has tasks defined on .gitpod.yml, the IDE will start with one terminal opened for each task #10595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 27, 2022
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
2 changes: 1 addition & 1 deletion .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ tasks:
read -r -p "Press enter to continue Java gradle task"
fi
leeway exec --package components/supervisor-api/java:lib --package components/gitpod-protocol/java:lib -- ./gradlew --build-cache build
leeway exec --package components/ide/jetbrains/backend-plugin:plugin --package components/ide/jetbrains/gateway-plugin:publish --parallel -- ./gradlew --build-cache buildPlugin
leeway exec --package components/ide/jetbrains/backend-plugin:plugin-latest --package components/ide/jetbrains/gateway-plugin:publish --parallel -- ./gradlew --build-cache buildPlugin
- name: TypeScript
before: scripts/branch-namespace.sh
init: yarn --network-timeout 100000 && yarn build
Expand Down
1 change: 1 addition & 0 deletions components/ide/jetbrains/backend-plugin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.vscode
bin
build
gradle-local.properties
27 changes: 24 additions & 3 deletions components/ide/jetbrains/backend-plugin/BUILD.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
packages:
- name: plugin
- name: plugin-stable
type: generic
deps:
- components/supervisor-api/java:lib
Expand All @@ -8,11 +8,32 @@ packages:
- "**/*.kt"
- "build.gradle.kts"
- "gradle.properties"
- "gradle-stable.properties"
- "gradle/wrapper/*"
- "gradlew"
- "settings.gradle.kts"
- "src/main/resources/*"
- "src/main/resources-stable/*"
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"]
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=stable", "runPluginVerifier"]
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=stable", "buildPlugin"]
- name: plugin-latest
type: generic
deps:
- components/supervisor-api/java:lib
- components/gitpod-protocol/java:lib
srcs:
- "**/*.kt"
- "build.gradle.kts"
- "gradle.properties"
- "gradle-latest.properties"
- "gradle/wrapper/*"
- "gradlew"
- "settings.gradle.kts"
- "src/main/resources/*"
- "src/main/resources-latest/*"
config:
commands:
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=latest", "runPluginVerifier"]
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=latest", "buildPlugin"]
24 changes: 21 additions & 3 deletions components/ide/jetbrains/backend-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,37 @@ fun properties(key: String) = project.findProperty(key).toString()
plugins {
// Java support
id("java")
// Kotlin support
id("org.jetbrains.kotlin.jvm") version "1.5.10"
// Kotlin support - check the latest version at https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
id("org.jetbrains.kotlin.jvm") version "1.7.0"
// gradle-intellij-plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin
id("org.jetbrains.intellij") version "1.0"
id("org.jetbrains.intellij") version "1.6.0"
// detekt linter - read more: https://detekt.github.io/detekt/gradle.html
id("io.gitlab.arturbosch.detekt") version "1.17.1"
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
// Gradle Properties Plugin - read more: https://github.com/stevesaliman/gradle-properties-plugin
id("net.saliman.properties") version "1.5.2"
}

group = properties("pluginGroup")
version = properties("version")

val environmentName = properties("environmentName")

project(":") {
kotlin {
var excludedPackage = "stable"
if (environmentName == excludedPackage) excludedPackage = "latest"
sourceSets["main"].kotlin.exclude("io/gitpod/jetbrains/remote/${excludedPackage}/**")
}

sourceSets {
main {
resources.srcDirs("src/main/resources-${environmentName}")
}
}
}

// Configure project's dependencies
repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
pluginSinceBuild=222
pluginUntilBuild=222.*
# 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=2022.2
# Version from "com.jetbrains.intellij.idea" which can be found at https://www.jetbrains.com/intellij-repository/snapshots
platformVersion=222-EAP-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
pluginSinceBuild=221
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=2022.1
# Version from "com.jetbrains.intellij.idea" which can be found at https://www.jetbrains.com/intellij-repository/snapshots
platformVersion=221-EAP-SNAPSHOT
12 changes: 3 additions & 9 deletions components/ide/jetbrains/backend-plugin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
version=0.0.1
# Supported environments: stable, latest (via https://github.com/stevesaliman/gradle-properties-plugin)
environmentName=latest
# IntelliJ Platform Artifacts Repositories
# -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
pluginGroup=io.gitpod.jetbrains
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=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, 2022.1
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
platformType=IU
platformVersion=221-EAP-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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// 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.latest

import com.intellij.openapi.Disposable
import com.intellij.openapi.client.ClientProjectSession
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.remoteDev.util.onTerminationOrNow
import com.intellij.util.application
import com.jetbrains.rd.util.lifetime.Lifetime
import com.jetbrains.rdserver.terminal.BackendTerminalManager
import io.gitpod.jetbrains.remote.GitpodManager
import io.gitpod.supervisor.api.Status
import io.gitpod.supervisor.api.StatusServiceGrpc
import io.gitpod.supervisor.api.TerminalOuterClass
import io.gitpod.supervisor.api.TerminalServiceGrpc
import io.grpc.stub.ClientCallStreamObserver
import io.grpc.stub.ClientResponseObserver
import org.jetbrains.plugins.terminal.ShellTerminalWidget
import org.jetbrains.plugins.terminal.TerminalView
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit

@Suppress("UnstableApiUsage")
class GitpodTerminalService(session: ClientProjectSession) : Disposable {
private companion object {
/** Indicates if this service is already running, because we shouldn't run it more than once. */
var isRunning = false
}

private val lifetime = Lifetime.Eternal.createNested()
private val terminalView = TerminalView.getInstance(session.project)
private val backendTerminalManager = BackendTerminalManager.getInstance(session.project)
private val terminalServiceFutureStub = TerminalServiceGrpc.newFutureStub(GitpodManager.supervisorChannel)
private val statusServiceStub = StatusServiceGrpc.newStub(GitpodManager.supervisorChannel)

override fun dispose() {
lifetime.terminate()
}

init {
run()
}

private fun run() {
if (application.isHeadlessEnvironment || isRunning) return

isRunning = true

val task = application.executeOnPooledThread {
val terminals = getSupervisorTerminalsList()
val tasks = getSupervisorTasksList()

application.invokeLater {
createTerminalsAttachedToTasks(terminals, tasks)
}
}

lifetime.onTerminationOrNow {
task.cancel(true)
}
}

private fun createSharedTerminalAndExecuteCommand(title: String, command: String) {
val registeredTerminals = terminalView.widgets.toMutableList()

backendTerminalManager.createNewSharedTerminal(UUID.randomUUID().toString(), title)

for (widget in terminalView.widgets) {
if (!registeredTerminals.contains(widget)) {
widget.terminalTitle.change {
applicationTitle = title
}
(widget as ShellTerminalWidget).executeCommand(command)
}
}
}

private fun createTerminalsAttachedToTasks(
terminals: List<TerminalOuterClass.Terminal>,
tasks: List<Status.TaskStatus>
) {
if (tasks.isEmpty()) return

val aliasToTerminalMap: MutableMap<String, TerminalOuterClass.Terminal> = mutableMapOf()

for (terminal in terminals) {
val terminalAlias = terminal.alias
aliasToTerminalMap[terminalAlias] = terminal
}

for (task in tasks) {
val terminalAlias = task.terminal
val terminal = aliasToTerminalMap[terminalAlias]

if (terminal != null) {
createAttachedSharedTerminal(terminal)
}
}
}

private tailrec fun getSupervisorTasksList(): List<Status.TaskStatus> {
var tasksList: List<Status.TaskStatus>? = null

try {
val completableFuture = CompletableFuture<List<Status.TaskStatus>>()

val taskStatusRequest = Status.TasksStatusRequest.newBuilder().setObserve(true).build()

val taskStatusResponseObserver = object :
ClientResponseObserver<Status.TasksStatusRequest, Status.TasksStatusResponse> {
override fun beforeStart(request: ClientCallStreamObserver<Status.TasksStatusRequest>) {
lifetime.onTerminationOrNow {
request.cancel(null, null)
}
}

override fun onNext(response: Status.TasksStatusResponse) {
for (task in response.tasksList) {
if (task.state === Status.TaskState.opening) {
return
}
}
completableFuture.complete(response.tasksList)
}

override fun onCompleted() {}

override fun onError(throwable: Throwable) {
completableFuture.completeExceptionally(throwable)
}
}

statusServiceStub.tasksStatus(taskStatusRequest, taskStatusResponseObserver)

tasksList = completableFuture.get()
} catch (throwable: Throwable) {
if (throwable is InterruptedException) {
throw throwable
}

thisLogger().error(
"Got an error while trying to get tasks list from Supervisor. Trying again in on second.",
throwable
)
}

return if (tasksList != null) {
tasksList
} else {
TimeUnit.SECONDS.sleep(1)
getSupervisorTasksList()
}
}

private tailrec fun getSupervisorTerminalsList(): List<TerminalOuterClass.Terminal> {
var terminalsList: List<TerminalOuterClass.Terminal>? = null

try {
val listTerminalsRequest = TerminalOuterClass.ListTerminalsRequest.newBuilder().build()

val listTerminalsResponseFuture = terminalServiceFutureStub.list(listTerminalsRequest)

lifetime.onTerminationOrNow {
listTerminalsResponseFuture.cancel(true)
}

val listTerminalsResponse = listTerminalsResponseFuture.get()

terminalsList = listTerminalsResponse.terminalsList
} catch (throwable: Throwable) {
if (throwable is InterruptedException) {
throw throwable
}

thisLogger().error(
"Got an error while trying to get terminals list from Supervisor. Trying again in on second.",
throwable
)
}

return if (terminalsList != null) {
terminalsList
} else {
TimeUnit.SECONDS.sleep(1)
getSupervisorTerminalsList()
}
}

private fun createAttachedSharedTerminal(supervisorTerminal: TerminalOuterClass.Terminal) {
createSharedTerminalAndExecuteCommand(
supervisorTerminal.title,
"gp tasks attach ${supervisorTerminal.alias}"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 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
package io.gitpod.jetbrains.remote.stable

import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runInEdt
Expand All @@ -24,7 +24,7 @@ import org.jetbrains.plugins.terminal.TerminalToolWindowFactory
import org.jetbrains.plugins.terminal.TerminalView
import java.util.concurrent.CompletableFuture

@Suppress("UnstableApiUsage", "EXPERIMENTAL_IS_NOT_ENABLED", "OPT_IN_IS_NOT_ENABLED")
@Suppress("UnstableApiUsage", "EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(DelicateCoroutinesApi::class)
class GitpodTerminalService(private val project: Project) : Disposable {
private val lifetime = Lifetime.Eternal.createNested()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!--
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.
-->
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<projectService serviceImplementation="io.gitpod.jetbrains.remote.latest.GitpodTerminalService" client="guest" preload="true"/>
</extensions>
</idea-plugin>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!--
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.
-->
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<projectService serviceImplementation="io.gitpod.jetbrains.remote.stable.GitpodTerminalService" preload="true"/>
</extensions>
</idea-plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
Licensed under the GNU Affero General Public License (AGPL).
See License-AGPL.txt in the project root for license information.
-->
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="/META-INF/extensions.xml" xpointer="xpointer(/idea-plugin/*)"/>

<idea-plugin>
<id>io.gitpod.jetbrains.remote</id>
<name>Gitpod Remote</name>
<vendor>Gitpod</vendor>
Expand All @@ -26,7 +27,6 @@
<notificationGroup id="Gitpod Notifications" displayType="BALLOON" isLogByDefault="false" />
<httpRequestHandler implementation="io.gitpod.jetbrains.remote.GitpodCLIService"/>
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodClientProjectSessionTracker" client="guest" preload="true"/>
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodTerminalService" preload="true"/>
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodProjectManager" preload="true"/>
<gateway.customization.name implementation="io.gitpod.jetbrains.remote.GitpodGatewayClientCustomizationProvider"/>
</extensions>
Expand Down
Loading