Skip to content

Commit 41efa4a

Browse files
felladrinroboquat
authored andcommitted
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
1 parent 95e3287 commit 41efa4a

File tree

14 files changed

+301
-29
lines changed

14 files changed

+301
-29
lines changed

.gitpod.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ tasks:
4646
read -r -p "Press enter to continue Java gradle task"
4747
fi
4848
leeway exec --package components/supervisor-api/java:lib --package components/gitpod-protocol/java:lib -- ./gradlew --build-cache build
49-
leeway exec --package components/ide/jetbrains/backend-plugin:plugin --package components/ide/jetbrains/gateway-plugin:publish --parallel -- ./gradlew --build-cache buildPlugin
49+
leeway exec --package components/ide/jetbrains/backend-plugin:plugin-latest --package components/ide/jetbrains/gateway-plugin:publish --parallel -- ./gradlew --build-cache buildPlugin
5050
- name: TypeScript
5151
before: scripts/branch-namespace.sh
5252
init: yarn --network-timeout 100000 && yarn build

components/ide/jetbrains/backend-plugin/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
.vscode
44
bin
55
build
6+
gradle-local.properties
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
packages:
2-
- name: plugin
2+
- name: plugin-stable
33
type: generic
44
deps:
55
- components/supervisor-api/java:lib
@@ -8,11 +8,32 @@ packages:
88
- "**/*.kt"
99
- "build.gradle.kts"
1010
- "gradle.properties"
11+
- "gradle-stable.properties"
1112
- "gradle/wrapper/*"
1213
- "gradlew"
1314
- "settings.gradle.kts"
1415
- "src/main/resources/*"
16+
- "src/main/resources-stable/*"
1517
config:
1618
commands:
17-
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "runPluginVerifier"]
18-
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "buildPlugin"]
19+
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=stable", "runPluginVerifier"]
20+
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=stable", "buildPlugin"]
21+
- name: plugin-latest
22+
type: generic
23+
deps:
24+
- components/supervisor-api/java:lib
25+
- components/gitpod-protocol/java:lib
26+
srcs:
27+
- "**/*.kt"
28+
- "build.gradle.kts"
29+
- "gradle.properties"
30+
- "gradle-latest.properties"
31+
- "gradle/wrapper/*"
32+
- "gradlew"
33+
- "settings.gradle.kts"
34+
- "src/main/resources/*"
35+
- "src/main/resources-latest/*"
36+
config:
37+
commands:
38+
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=latest", "runPluginVerifier"]
39+
- ["./gradlew", "-PsupervisorApiProjectPath=components-supervisor-api-java--lib/", "-PgitpodProtocolProjectPath=components-gitpod-protocol-java--lib/", "-PenvironmentName=latest", "buildPlugin"]

components/ide/jetbrains/backend-plugin/build.gradle.kts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,37 @@ fun properties(key: String) = project.findProperty(key).toString()
1010
plugins {
1111
// Java support
1212
id("java")
13-
// Kotlin support
14-
id("org.jetbrains.kotlin.jvm") version "1.5.10"
13+
// Kotlin support - check the latest version at https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
14+
id("org.jetbrains.kotlin.jvm") version "1.7.0"
1515
// gradle-intellij-plugin - read more: https://github.com/JetBrains/gradle-intellij-plugin
16-
id("org.jetbrains.intellij") version "1.0"
16+
id("org.jetbrains.intellij") version "1.6.0"
1717
// detekt linter - read more: https://detekt.github.io/detekt/gradle.html
1818
id("io.gitlab.arturbosch.detekt") version "1.17.1"
1919
// ktlint linter - read more: https://github.com/JLLeitschuh/ktlint-gradle
2020
id("org.jlleitschuh.gradle.ktlint") version "10.0.0"
21+
// Gradle Properties Plugin - read more: https://github.com/stevesaliman/gradle-properties-plugin
22+
id("net.saliman.properties") version "1.5.2"
2123
}
2224

2325
group = properties("pluginGroup")
2426
version = properties("version")
2527

28+
val environmentName = properties("environmentName")
29+
30+
project(":") {
31+
kotlin {
32+
var excludedPackage = "stable"
33+
if (environmentName == excludedPackage) excludedPackage = "latest"
34+
sourceSets["main"].kotlin.exclude("io/gitpod/jetbrains/remote/${excludedPackage}/**")
35+
}
36+
37+
sourceSets {
38+
main {
39+
resources.srcDirs("src/main/resources-${environmentName}")
40+
}
41+
}
42+
}
43+
2644
// Configure project's dependencies
2745
repositories {
2846
mavenCentral()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
2+
# for insight into build numbers and IntelliJ Platform versions.
3+
pluginSinceBuild=222
4+
pluginUntilBuild=222.*
5+
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
6+
# See https://jb.gg/intellij-platform-builds-list for available build versions.
7+
pluginVerifierIdeVersions=2022.2
8+
# Version from "com.jetbrains.intellij.idea" which can be found at https://www.jetbrains.com/intellij-repository/snapshots
9+
platformVersion=222-EAP-SNAPSHOT
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
2+
# for insight into build numbers and IntelliJ Platform versions.
3+
pluginSinceBuild=221
4+
pluginUntilBuild=221.*
5+
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
6+
# See https://jb.gg/intellij-platform-builds-list for available build versions.
7+
pluginVerifierIdeVersions=2022.1
8+
# Version from "com.jetbrains.intellij.idea" which can be found at https://www.jetbrains.com/intellij-repository/snapshots
9+
platformVersion=221-EAP-SNAPSHOT

components/ide/jetbrains/backend-plugin/gradle.properties

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
version=0.0.1
2+
# Supported environments: stable, latest (via https://github.com/stevesaliman/gradle-properties-plugin)
3+
environmentName=latest
24
# IntelliJ Platform Artifacts Repositories
35
# -> https://plugins.jetbrains.com/docs/intellij/intellij-artifacts.html
46
pluginGroup=io.gitpod.jetbrains
57
pluginName=gitpod-remote
6-
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
7-
# for insight into build numbers and IntelliJ Platform versions.
8-
pluginSinceBuild=213
9-
pluginUntilBuild=221.*
10-
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
11-
# See https://jb.gg/intellij-platform-builds-list for available build versions.
12-
pluginVerifierIdeVersions=2021.3, 2022.1
13-
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
8+
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#intellij-extension-type
149
platformType=IU
15-
platformVersion=221-EAP-SNAPSHOT
1610
platformDownloadSources=true
1711
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
1812
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License-AGPL.txt in the project root for license information.
4+
5+
package io.gitpod.jetbrains.remote.latest
6+
7+
import com.intellij.openapi.Disposable
8+
import com.intellij.openapi.client.ClientProjectSession
9+
import com.intellij.openapi.diagnostic.thisLogger
10+
import com.intellij.remoteDev.util.onTerminationOrNow
11+
import com.intellij.util.application
12+
import com.jetbrains.rd.util.lifetime.Lifetime
13+
import com.jetbrains.rdserver.terminal.BackendTerminalManager
14+
import io.gitpod.jetbrains.remote.GitpodManager
15+
import io.gitpod.supervisor.api.Status
16+
import io.gitpod.supervisor.api.StatusServiceGrpc
17+
import io.gitpod.supervisor.api.TerminalOuterClass
18+
import io.gitpod.supervisor.api.TerminalServiceGrpc
19+
import io.grpc.stub.ClientCallStreamObserver
20+
import io.grpc.stub.ClientResponseObserver
21+
import org.jetbrains.plugins.terminal.ShellTerminalWidget
22+
import org.jetbrains.plugins.terminal.TerminalView
23+
import java.util.*
24+
import java.util.concurrent.CompletableFuture
25+
import java.util.concurrent.TimeUnit
26+
27+
@Suppress("UnstableApiUsage")
28+
class GitpodTerminalService(session: ClientProjectSession) : Disposable {
29+
private companion object {
30+
/** Indicates if this service is already running, because we shouldn't run it more than once. */
31+
var isRunning = false
32+
}
33+
34+
private val lifetime = Lifetime.Eternal.createNested()
35+
private val terminalView = TerminalView.getInstance(session.project)
36+
private val backendTerminalManager = BackendTerminalManager.getInstance(session.project)
37+
private val terminalServiceFutureStub = TerminalServiceGrpc.newFutureStub(GitpodManager.supervisorChannel)
38+
private val statusServiceStub = StatusServiceGrpc.newStub(GitpodManager.supervisorChannel)
39+
40+
override fun dispose() {
41+
lifetime.terminate()
42+
}
43+
44+
init {
45+
run()
46+
}
47+
48+
private fun run() {
49+
if (application.isHeadlessEnvironment || isRunning) return
50+
51+
isRunning = true
52+
53+
val task = application.executeOnPooledThread {
54+
val terminals = getSupervisorTerminalsList()
55+
val tasks = getSupervisorTasksList()
56+
57+
application.invokeLater {
58+
createTerminalsAttachedToTasks(terminals, tasks)
59+
}
60+
}
61+
62+
lifetime.onTerminationOrNow {
63+
task.cancel(true)
64+
}
65+
}
66+
67+
private fun createSharedTerminalAndExecuteCommand(title: String, command: String) {
68+
val registeredTerminals = terminalView.widgets.toMutableList()
69+
70+
backendTerminalManager.createNewSharedTerminal(UUID.randomUUID().toString(), title)
71+
72+
for (widget in terminalView.widgets) {
73+
if (!registeredTerminals.contains(widget)) {
74+
widget.terminalTitle.change {
75+
applicationTitle = title
76+
}
77+
(widget as ShellTerminalWidget).executeCommand(command)
78+
}
79+
}
80+
}
81+
82+
private fun createTerminalsAttachedToTasks(
83+
terminals: List<TerminalOuterClass.Terminal>,
84+
tasks: List<Status.TaskStatus>
85+
) {
86+
if (tasks.isEmpty()) return
87+
88+
val aliasToTerminalMap: MutableMap<String, TerminalOuterClass.Terminal> = mutableMapOf()
89+
90+
for (terminal in terminals) {
91+
val terminalAlias = terminal.alias
92+
aliasToTerminalMap[terminalAlias] = terminal
93+
}
94+
95+
for (task in tasks) {
96+
val terminalAlias = task.terminal
97+
val terminal = aliasToTerminalMap[terminalAlias]
98+
99+
if (terminal != null) {
100+
createAttachedSharedTerminal(terminal)
101+
}
102+
}
103+
}
104+
105+
private tailrec fun getSupervisorTasksList(): List<Status.TaskStatus> {
106+
var tasksList: List<Status.TaskStatus>? = null
107+
108+
try {
109+
val completableFuture = CompletableFuture<List<Status.TaskStatus>>()
110+
111+
val taskStatusRequest = Status.TasksStatusRequest.newBuilder().setObserve(true).build()
112+
113+
val taskStatusResponseObserver = object :
114+
ClientResponseObserver<Status.TasksStatusRequest, Status.TasksStatusResponse> {
115+
override fun beforeStart(request: ClientCallStreamObserver<Status.TasksStatusRequest>) {
116+
lifetime.onTerminationOrNow {
117+
request.cancel(null, null)
118+
}
119+
}
120+
121+
override fun onNext(response: Status.TasksStatusResponse) {
122+
for (task in response.tasksList) {
123+
if (task.state === Status.TaskState.opening) {
124+
return
125+
}
126+
}
127+
completableFuture.complete(response.tasksList)
128+
}
129+
130+
override fun onCompleted() {}
131+
132+
override fun onError(throwable: Throwable) {
133+
completableFuture.completeExceptionally(throwable)
134+
}
135+
}
136+
137+
statusServiceStub.tasksStatus(taskStatusRequest, taskStatusResponseObserver)
138+
139+
tasksList = completableFuture.get()
140+
} catch (throwable: Throwable) {
141+
if (throwable is InterruptedException) {
142+
throw throwable
143+
}
144+
145+
thisLogger().error(
146+
"Got an error while trying to get tasks list from Supervisor. Trying again in on second.",
147+
throwable
148+
)
149+
}
150+
151+
return if (tasksList != null) {
152+
tasksList
153+
} else {
154+
TimeUnit.SECONDS.sleep(1)
155+
getSupervisorTasksList()
156+
}
157+
}
158+
159+
private tailrec fun getSupervisorTerminalsList(): List<TerminalOuterClass.Terminal> {
160+
var terminalsList: List<TerminalOuterClass.Terminal>? = null
161+
162+
try {
163+
val listTerminalsRequest = TerminalOuterClass.ListTerminalsRequest.newBuilder().build()
164+
165+
val listTerminalsResponseFuture = terminalServiceFutureStub.list(listTerminalsRequest)
166+
167+
lifetime.onTerminationOrNow {
168+
listTerminalsResponseFuture.cancel(true)
169+
}
170+
171+
val listTerminalsResponse = listTerminalsResponseFuture.get()
172+
173+
terminalsList = listTerminalsResponse.terminalsList
174+
} catch (throwable: Throwable) {
175+
if (throwable is InterruptedException) {
176+
throw throwable
177+
}
178+
179+
thisLogger().error(
180+
"Got an error while trying to get terminals list from Supervisor. Trying again in on second.",
181+
throwable
182+
)
183+
}
184+
185+
return if (terminalsList != null) {
186+
terminalsList
187+
} else {
188+
TimeUnit.SECONDS.sleep(1)
189+
getSupervisorTerminalsList()
190+
}
191+
}
192+
193+
private fun createAttachedSharedTerminal(supervisorTerminal: TerminalOuterClass.Terminal) {
194+
createSharedTerminalAndExecuteCommand(
195+
supervisorTerminal.title,
196+
"gp tasks attach ${supervisorTerminal.alias}"
197+
)
198+
}
199+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the GNU Affero General Public License (AGPL).
33
// See License-AGPL.txt in the project root for license information.
44

5-
package io.gitpod.jetbrains.remote
5+
package io.gitpod.jetbrains.remote.stable
66

77
import com.intellij.openapi.Disposable
88
import com.intellij.openapi.application.runInEdt
@@ -24,7 +24,7 @@ import org.jetbrains.plugins.terminal.TerminalToolWindowFactory
2424
import org.jetbrains.plugins.terminal.TerminalView
2525
import java.util.concurrent.CompletableFuture
2626

27-
@Suppress("UnstableApiUsage", "EXPERIMENTAL_IS_NOT_ENABLED", "OPT_IN_IS_NOT_ENABLED")
27+
@Suppress("UnstableApiUsage", "EXPERIMENTAL_IS_NOT_ENABLED")
2828
@OptIn(DelicateCoroutinesApi::class)
2929
class GitpodTerminalService(private val project: Project) : Disposable {
3030
private val lifetime = Lifetime.Eternal.createNested()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
Licensed under the GNU Affero General Public License (AGPL).
4+
See License-AGPL.txt in the project root for license information.
5+
-->
6+
<idea-plugin>
7+
<extensions defaultExtensionNs="com.intellij">
8+
<projectService serviceImplementation="io.gitpod.jetbrains.remote.latest.GitpodTerminalService" client="guest" preload="true"/>
9+
</extensions>
10+
</idea-plugin>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!--
2+
Copyright (c) 2022 Gitpod GmbH. All rights reserved.
3+
Licensed under the GNU Affero General Public License (AGPL).
4+
See License-AGPL.txt in the project root for license information.
5+
-->
6+
<idea-plugin>
7+
<extensions defaultExtensionNs="com.intellij">
8+
<projectService serviceImplementation="io.gitpod.jetbrains.remote.stable.GitpodTerminalService" preload="true"/>
9+
</extensions>
10+
</idea-plugin>

components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
Licensed under the GNU Affero General Public License (AGPL).
44
See License-AGPL.txt in the project root for license information.
55
-->
6+
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
7+
<xi:include href="/META-INF/extensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
68

7-
<idea-plugin>
89
<id>io.gitpod.jetbrains.remote</id>
910
<name>Gitpod Remote</name>
1011
<vendor>Gitpod</vendor>
@@ -26,7 +27,6 @@
2627
<notificationGroup id="Gitpod Notifications" displayType="BALLOON" isLogByDefault="false" />
2728
<httpRequestHandler implementation="io.gitpod.jetbrains.remote.GitpodCLIService"/>
2829
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodClientProjectSessionTracker" client="guest" preload="true"/>
29-
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodTerminalService" preload="true"/>
3030
<projectService serviceImplementation="io.gitpod.jetbrains.remote.GitpodProjectManager" preload="true"/>
3131
<gateway.customization.name implementation="io.gitpod.jetbrains.remote.GitpodGatewayClientCustomizationProvider"/>
3232
</extensions>

0 commit comments

Comments
 (0)