4
4
5
5
package io.gitpod.jetbrains.remote
6
6
7
+ import com.intellij.ide.BrowserUtil
7
8
import com.intellij.ide.plugins.PluginManagerCore
8
9
import com.intellij.notification.NotificationAction
9
10
import com.intellij.notification.NotificationGroupManager
@@ -23,6 +24,8 @@ import io.gitpod.jetbrains.remote.utils.Retrier.retry
23
24
import io.gitpod.supervisor.api.*
24
25
import io.gitpod.supervisor.api.Info.WorkspaceInfoResponse
25
26
import io.gitpod.supervisor.api.Notification.*
27
+ import io.gitpod.supervisor.api.Status.PortsStatusRequest
28
+ import io.gitpod.supervisor.api.Status.PortsStatusResponse
26
29
import io.grpc.ManagedChannel
27
30
import io.grpc.ManagedChannelBuilder
28
31
import io.grpc.stub.ClientCallStreamObserver
@@ -46,6 +49,7 @@ import java.util.concurrent.CancellationException
46
49
import java.util.concurrent.CompletableFuture
47
50
import javax.websocket.DeploymentException
48
51
52
+
49
53
@Service
50
54
class GitpodManager : Disposable {
51
55
@@ -57,6 +61,7 @@ class GitpodManager : Disposable {
57
61
val devMode = System .getenv(" JB_DEV" ).toBoolean()
58
62
private val backendKind = System .getenv(" JETBRAINS_GITPOD_BACKEND_KIND" ) ? : " unknown"
59
63
private val backendQualifier = System .getenv(" JETBRAINS_BACKEND_QUALIFIER" ) ? : " unknown"
64
+ private val workspaceUrl = System .getenv(" GITPOD_WORKSPACE_URL" )
60
65
61
66
private val lifetime = Lifetime .Eternal .createNested()
62
67
@@ -199,6 +204,71 @@ class GitpodManager : Disposable {
199
204
}
200
205
}
201
206
207
+ private val portsObserveJob = GlobalScope .launch {
208
+ if (application.isHeadlessEnvironment) {
209
+ return @launch
210
+ }
211
+
212
+ val status = StatusServiceGrpc .newStub(supervisorChannel)
213
+ // while (isActive) {
214
+ try {
215
+ val f = CompletableFuture <Void >()
216
+ status.portsStatus(
217
+ PortsStatusRequest .newBuilder().build(),
218
+ object : ClientResponseObserver <PortsStatusRequest , PortsStatusResponse > {
219
+ override fun beforeStart (requestStream : ClientCallStreamObserver <PortsStatusRequest >) {
220
+ println (" [Andrea]: beforeStart" )
221
+ // TODO(ak): actually should be bound to cancellation of notifications job
222
+ lifetime.onTerminationOrNow {
223
+ requestStream.cancel(null , null )
224
+ }
225
+ }
226
+
227
+ override fun onNext (ps : PortsStatusResponse ) {
228
+ println (" [Andrea]: onNext" )
229
+ for (port in ps.portsList) {
230
+ println (port)
231
+ if (port.exposed.onExposed.number == 0 ) {
232
+ return
233
+ }
234
+
235
+ println (port)
236
+
237
+ if (port.served && port.exposed.url.isNullOrEmpty() == false ) {
238
+ val message = " Your application running on port " + port.localPort + " is available."
239
+ val notification = notificationGroup.createNotification(message, NotificationType .INFORMATION )
240
+ // TODO(andreafalzetti): add analytics event
241
+ val lambda = { BrowserUtil .browse(port.exposed.url) }
242
+ val action = NotificationAction .createSimpleExpiring(" Open in browser" , lambda)
243
+ notification.addAction(action)
244
+ notification.notify(null )
245
+ }
246
+ }
247
+
248
+ }
249
+
250
+ override fun onError (t : Throwable ) {
251
+ println (" [Andrea]: onError" )
252
+ f.completeExceptionally(t)
253
+ }
254
+
255
+ override fun onCompleted () {
256
+ f.complete(null )
257
+ }
258
+ })
259
+ } catch (t: Throwable ) {
260
+ if (t is CancellationException ) {
261
+ throw t
262
+ }
263
+ thisLogger().error(" gitpod: failed to stream ports status: " , t)
264
+ }
265
+ }
266
+ init {
267
+ lifetime.onTerminationOrNow {
268
+ portsObserveJob.cancel()
269
+ }
270
+ }
271
+
202
272
val pendingInfo = CompletableFuture <WorkspaceInfoResponse >()
203
273
private val infoJob = GlobalScope .launch {
204
274
if (application.isHeadlessEnvironment) {
0 commit comments