@@ -4,6 +4,7 @@ import com.coder.gateway.CoderGatewayBundle
4
4
import com.coder.gateway.cli.CoderCLIManager
5
5
import com.coder.gateway.icons.CoderIcons
6
6
import com.coder.gateway.models.WorkspaceProjectIDE
7
+ import com.coder.gateway.models.filterOutAvailableReleasedIdes
7
8
import com.coder.gateway.models.toIdeWithStatus
8
9
import com.coder.gateway.models.withWorkspaceProject
9
10
import com.coder.gateway.sdk.v2.models.Workspace
@@ -82,9 +83,12 @@ import javax.swing.SwingConstants
82
83
import javax.swing.event.DocumentEvent
83
84
84
85
// Just extracting the way we display the IDE info into a helper function.
85
- private fun displayIdeWithStatus (ideWithStatus : IdeWithStatus ): String = " ${ideWithStatus.product.productCode} ${ideWithStatus.presentableVersion} ${ideWithStatus.buildNumber} | ${ideWithStatus.status.name.lowercase(
86
- Locale .getDefault(),
87
- )} "
86
+ private fun displayIdeWithStatus (ideWithStatus : IdeWithStatus ): String =
87
+ " ${ideWithStatus.product.productCode} ${ideWithStatus.presentableVersion} ${ideWithStatus.buildNumber} | ${
88
+ ideWithStatus.status.name.lowercase(
89
+ Locale .getDefault(),
90
+ )
91
+ } "
88
92
89
93
/* *
90
94
* View for a single workspace. In particular, show available IDEs and a button
@@ -222,12 +226,21 @@ class CoderWorkspaceProjectIDEStepView(
222
226
cbIDE.renderer =
223
227
if (attempt > 1 ) {
224
228
IDECellRenderer (
225
- CoderGatewayBundle .message(" gateway.connector.view.coder.connect-ssh.retry" , attempt),
229
+ CoderGatewayBundle .message(
230
+ " gateway.connector.view.coder.connect-ssh.retry" ,
231
+ attempt
232
+ ),
226
233
)
227
234
} else {
228
235
IDECellRenderer (CoderGatewayBundle .message(" gateway.connector.view.coder.connect-ssh" ))
229
236
}
230
- val executor = createRemoteExecutor(CoderCLIManager (data.client.url).getBackgroundHostName(data.workspace, data.client.me, data.agent))
237
+ val executor = createRemoteExecutor(
238
+ CoderCLIManager (data.client.url).getBackgroundHostName(
239
+ data.workspace,
240
+ data.client.me,
241
+ data.agent
242
+ )
243
+ )
231
244
232
245
if (ComponentValidator .getInstance(tfProject).isEmpty) {
233
246
logger.info(" Installing remote path validator..." )
@@ -238,7 +251,10 @@ class CoderWorkspaceProjectIDEStepView(
238
251
cbIDE.renderer =
239
252
if (attempt > 1 ) {
240
253
IDECellRenderer (
241
- CoderGatewayBundle .message(" gateway.connector.view.coder.retrieve-ides.retry" , attempt),
254
+ CoderGatewayBundle .message(
255
+ " gateway.connector.view.coder.retrieve-ides.retry" ,
256
+ attempt
257
+ ),
242
258
)
243
259
} else {
244
260
IDECellRenderer (CoderGatewayBundle .message(" gateway.connector.view.coder.retrieve-ides" ))
@@ -247,9 +263,9 @@ class CoderWorkspaceProjectIDEStepView(
247
263
},
248
264
retryIf = {
249
265
it is ConnectionException ||
250
- it is TimeoutException ||
251
- it is SSHException ||
252
- it is DeployException
266
+ it is TimeoutException ||
267
+ it is SSHException ||
268
+ it is DeployException
253
269
},
254
270
onException = { attempt, nextMs, e ->
255
271
logger.error(" Failed to retrieve IDEs (attempt $attempt ; will retry in $nextMs ms)" )
@@ -311,7 +327,10 @@ class CoderWorkspaceProjectIDEStepView(
311
327
* Validate the remote path whenever it changes.
312
328
*/
313
329
private fun installRemotePathValidator (executor : HighLevelHostAccessor ) {
314
- val disposable = Disposer .newDisposable(ApplicationManager .getApplication(), CoderWorkspaceProjectIDEStepView ::class .java.name)
330
+ val disposable = Disposer .newDisposable(
331
+ ApplicationManager .getApplication(),
332
+ CoderWorkspaceProjectIDEStepView ::class .java.name
333
+ )
315
334
ComponentValidator (disposable).installOn(tfProject)
316
335
317
336
tfProject.document.addDocumentListener(
@@ -324,7 +343,12 @@ class CoderWorkspaceProjectIDEStepView(
324
343
val isPathPresent = validateRemotePath(tfProject.text, executor)
325
344
if (isPathPresent.pathOrNull == null ) {
326
345
ComponentValidator .getInstance(tfProject).ifPresent {
327
- it.updateInfo(ValidationInfo (" Can't find directory: ${tfProject.text} " , tfProject))
346
+ it.updateInfo(
347
+ ValidationInfo (
348
+ " Can't find directory: ${tfProject.text} " ,
349
+ tfProject
350
+ )
351
+ )
328
352
}
329
353
} else {
330
354
ComponentValidator .getInstance(tfProject).ifPresent {
@@ -333,7 +357,12 @@ class CoderWorkspaceProjectIDEStepView(
333
357
}
334
358
} catch (e: Exception ) {
335
359
ComponentValidator .getInstance(tfProject).ifPresent {
336
- it.updateInfo(ValidationInfo (" Can't validate directory: ${tfProject.text} " , tfProject))
360
+ it.updateInfo(
361
+ ValidationInfo (
362
+ " Can't validate directory: ${tfProject.text} " ,
363
+ tfProject
364
+ )
365
+ )
337
366
}
338
367
}
339
368
}
@@ -377,27 +406,34 @@ class CoderWorkspaceProjectIDEStepView(
377
406
}
378
407
379
408
logger.info(" Resolved OS and Arch for $name is: $workspaceOS " )
380
- val installedIdesJob =
381
- cs.async( Dispatchers . IO ) {
382
- executor.getInstalledIDEs().map { it.toIdeWithStatus() }
383
- }
384
- val idesWithStatusJob =
385
- cs.async( Dispatchers . IO ) {
386
- IntelliJPlatformProduct .entries
387
- .filter { it.showInGateway }
388
- .flatMap { CachingProductsJsonWrapper .getInstance().getAvailableIdes(it, workspaceOS) }
389
- .map { it.toIdeWithStatus() }
390
- }
409
+ val installedIdesJob = cs.async( Dispatchers . IO ) {
410
+ executor.getInstalledIDEs()
411
+ }
412
+ val availableToDownloadIdesJob = cs.async( Dispatchers . IO ) {
413
+ IntelliJPlatformProduct .entries
414
+ .filter { it.showInGateway }
415
+ .flatMap { CachingProductsJsonWrapper .getInstance().getAvailableIdes(it, workspaceOS) }
416
+ }
417
+
418
+ val installedIdes = installedIdesJob.await()
419
+ val availableIdes = availableToDownloadIdesJob.await()
391
420
392
- val installedIdes = installedIdesJob.await().sorted()
393
- val idesWithStatus = idesWithStatusJob.await().sorted()
394
421
if (installedIdes.isEmpty()) {
395
422
logger.info(" No IDE is installed in $name " )
396
423
}
397
- if (idesWithStatus .isEmpty()) {
424
+ if (availableIdes .isEmpty()) {
398
425
logger.warn(" Could not resolve any IDE for $name , probably $workspaceOS is not supported by Gateway" )
399
426
}
400
- return installedIdes + idesWithStatus
427
+
428
+ val remainingInstalledIdes = installedIdes.filterOutAvailableReleasedIdes(availableIdes)
429
+ if (remainingInstalledIdes.size < installedIdes.size) {
430
+ logger.info(
431
+ " Skipping the following list of installed IDEs because there is already a released version " +
432
+ " available for download: ${(installedIdes - remainingInstalledIdes).joinToString { " ${it.product.productCode} ${it.presentableVersion} " }} "
433
+ )
434
+ }
435
+ return remainingInstalledIdes.map { it.toIdeWithStatus() }.sorted() + availableIdes.map { it.toIdeWithStatus() }
436
+ .sorted()
401
437
}
402
438
403
439
private fun toDeployedOS (
@@ -455,7 +491,8 @@ class CoderWorkspaceProjectIDEStepView(
455
491
override fun getSelectedItem (): IdeWithStatus ? = super .getSelectedItem() as IdeWithStatus ?
456
492
}
457
493
458
- private class IDECellRenderer (message : String , cellIcon : Icon = AnimatedIcon .Default .INSTANCE ) : ListCellRenderer<IdeWithStatus> {
494
+ private class IDECellRenderer (message : String , cellIcon : Icon = AnimatedIcon .Default .INSTANCE ) :
495
+ ListCellRenderer <IdeWithStatus > {
459
496
private val loadingComponentRenderer: ListCellRenderer <IdeWithStatus > =
460
497
object : ColoredListCellRenderer <IdeWithStatus >() {
461
498
override fun customizeCellRenderer (
0 commit comments