diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 399a39df..2ba92b3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: zulu - java-version: 11 + java-version: 17 cache: gradle # Set environment variables diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8d8848e..743314c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,12 +19,12 @@ jobs: with: ref: ${{ github.event.release.tag_name }} - # Setup Java 11 environment for the next steps + # Setup Java 17 environment for the next steps - name: Setup Java uses: actions/setup-java@v2 with: distribution: zulu - java-version: 11 + java-version: 17 cache: gradle # Set environment variables diff --git a/CHANGELOG.md b/CHANGELOG.md index 41075e53..204674a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ ## [Unreleased] ### Added +- support for Gateway 2022.2 + +### Changed +- Java 17 is now required to run the plugin +- adapted the code to the new SSH API provided by Gateway + +## [1.0.0] +### Added - initial scaffold for Gateway plugin - browser based authentication on Coder environments - REST client for Coder V2 public API diff --git a/build.gradle.kts b/build.gradle.kts index 93d8dc15..ea36a6c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -92,6 +92,11 @@ tasks { compilerVersion.set(properties("instrumentationCompiler")) } + // TODO - this fails with linkage error, remove when it works + buildSearchableOptions { + isEnabled = false + } + patchPluginXml { version.set(properties("pluginVersion")) sinceBuild.set(properties("pluginSinceBuild")) diff --git a/gradle.properties b/gradle.properties index 90215cd9..77f868d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,22 +3,22 @@ pluginGroup=com.coder.gateway pluginName=coder-gateway # SemVer format -> https://semver.org -pluginVersion=1.0.0 +pluginVersion=2.0.0 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. -pluginSinceBuild=213 -pluginUntilBuild=221.* +pluginSinceBuild=222.3345.108 +pluginUntilBuild=222.* # IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties # Gateway available build versions https://www.jetbrains.com/intellij-repository/snapshots and https://www.jetbrains.com/intellij-repository/releases platformType=GW -platformVersion=221.5921.22-CUSTOM-SNAPSHOT -instrumentationCompiler=221.5921.22 +platformVersion=222.3345.108-CUSTOM-SNAPSHOT +instrumentationCompiler=222.3345.108-CUSTOM-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 platformPlugins= -# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3 -javaVersion=11 +# Java language level used to compile sources and to generate the files for - Java 17 is required since 2022.2 +javaVersion=17 # Gradle Releases -> https://github.com/gradle/gradle/releases gradleVersion=7.4 # Opt-out flag for bundling Kotlin standard library. diff --git a/src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt b/src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt index 9214529f..e963a3fd 100644 --- a/src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt +++ b/src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt @@ -4,31 +4,34 @@ package com.coder.gateway import com.coder.gateway.models.RecentWorkspaceConnection import com.coder.gateway.services.CoderRecentWorkspaceConnectionsService -import com.coder.gateway.views.CoderGatewayConnectionComponent import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.rd.util.launchUnderBackgroundProgress import com.intellij.remote.AuthType import com.intellij.remote.RemoteCredentialsHolder import com.intellij.ssh.config.unified.SshConfig +import com.intellij.ssh.config.unified.SshConfigManager import com.jetbrains.gateway.api.ConnectionRequestor import com.jetbrains.gateway.api.GatewayConnectionHandle import com.jetbrains.gateway.api.GatewayConnectionProvider +import com.jetbrains.gateway.ssh.HighLevelHostAccessor +import com.jetbrains.gateway.ssh.HostDeployInputs import com.jetbrains.gateway.ssh.IdeInfo import com.jetbrains.gateway.ssh.IntelliJPlatformProduct -import com.jetbrains.gateway.ssh.SshCommandsExecutor import com.jetbrains.gateway.ssh.SshDeployFlowUtil -import com.jetbrains.gateway.ssh.SshDownloadMethod import com.jetbrains.gateway.ssh.SshMultistagePanelContext +import com.jetbrains.gateway.ssh.deploy.DeployTargetInfo.DeployWithDownload import com.jetbrains.rd.util.lifetime.LifetimeDefinition import kotlinx.coroutines.launch +import java.net.URI import java.time.Duration import java.time.LocalDateTime import java.time.format.DateTimeFormatter -import javax.swing.JComponent class CoderGatewayConnectionProvider : GatewayConnectionProvider { private val recentConnectionsService = service() + private val sshConfigService = service() + private val connections = mutableSetOf() private val localTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm") @@ -46,62 +49,54 @@ class CoderGatewayConnectionProvider : GatewayConnectionProvider { logger.warn("There is already a connection started on ${connection.workspaceHostname}") return null } - val clientLifetime = LifetimeDefinition() - - val credentials = RemoteCredentialsHolder().apply { - setHost(coderWorkspaceHostname) - userName = "coder" - authType = AuthType.OPEN_SSH - } - val sshConfiguration = SshConfig(true).apply { setHost(coderWorkspaceHostname) setUsername("coder") + port = 22 authType = AuthType.OPEN_SSH } - val ideConfig = IdeInfo( - product = IntelliJPlatformProduct.fromProductCode(ideProductCode)!!, - buildNumber = ideBuildNumber - ) - + val clientLifetime = LifetimeDefinition() clientLifetime.launchUnderBackgroundProgress("Coder Gateway Deploy", canBeCancelled = true, isIndeterminate = true, project = null) { - val context = SshMultistagePanelContext().apply { - deploy = true - sshConfig = sshConfiguration - remoteProjectPath = projectPath - remoteCommandsExecutor = SshCommandsExecutor.Companion.create(credentials) - downloadMethod = SshDownloadMethod.CustomizedLink - customDownloadLink = ideDownloadLink - ide = ideConfig - } + val context = SshMultistagePanelContext( + HostDeployInputs.FullySpecified( + remoteProjectPath = projectPath, + deployTarget = DeployWithDownload( + URI(ideDownloadLink), + null, + IdeInfo( + product = IntelliJPlatformProduct.fromProductCode(ideProductCode)!!, + buildNumber = ideBuildNumber + ) + ), + remoteInfo = HostDeployInputs.WithDeployedWorker( + HighLevelHostAccessor.create( + RemoteCredentialsHolder().apply { + setHost(coderWorkspaceHostname) + userName = "coder" + port = 22 + authType = AuthType.OPEN_SSH + }, + true + ), + HostDeployInputs.WithHostInfo(sshConfiguration) + ) + ) + ) launch { - @Suppress("UnstableApiUsage") - SshDeployFlowUtil.fullDeployCycle( - clientLifetime, - context, - Duration.ofMinutes(10) + @Suppress("UnstableApiUsage") SshDeployFlowUtil.fullDeployCycle( + clientLifetime, context, Duration.ofMinutes(10) ) } } recentConnectionsService.addRecentConnection( RecentWorkspaceConnection( - coderWorkspaceHostname, - projectPath, - localTimeFormatter.format(LocalDateTime.now()), - ideProductCode, - ideBuildNumber, - ideDownloadLink, - webTerminalLink, + coderWorkspaceHostname, projectPath, localTimeFormatter.format(LocalDateTime.now()), ideProductCode, ideBuildNumber, ideDownloadLink, webTerminalLink ) ) return object : GatewayConnectionHandle(clientLifetime) { - override fun createComponent(): JComponent { - return CoderGatewayConnectionComponent(clientLifetime, coderWorkspaceHostname) - } - override fun getTitle(): String { return "Connection to Coder Workspaces" } diff --git a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt index 4fcda058..a7d7c6b2 100644 --- a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt +++ b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt @@ -30,10 +30,11 @@ import com.jetbrains.gateway.ssh.CachingProductsJsonWrapper import com.jetbrains.gateway.ssh.DeployTargetOS import com.jetbrains.gateway.ssh.DeployTargetOS.OSArch import com.jetbrains.gateway.ssh.DeployTargetOS.OSKind +import com.jetbrains.gateway.ssh.HighLevelHostAccessor import com.jetbrains.gateway.ssh.IdeStatus import com.jetbrains.gateway.ssh.IdeWithStatus import com.jetbrains.gateway.ssh.IntelliJPlatformProduct -import com.jetbrains.gateway.ssh.guessOs +import com.jetbrains.gateway.ssh.deploy.guessOs import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel @@ -116,11 +117,15 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit logger.info("Retrieving available IDE's for ${selectedWorkspace.name} workspace...") val workspaceOS = if (selectedWorkspace.agentOS != null && selectedWorkspace.agentArch != null) withContext(Dispatchers.IO) { toDeployedOS(selectedWorkspace.agentOS, selectedWorkspace.agentArch) } else withContext(Dispatchers.IO) { try { - RemoteCredentialsHolder().apply { + val credentialsHolder = RemoteCredentialsHolder().apply { setHost("coder.${selectedWorkspace.name}") userName = "coder" authType = AuthType.OPEN_SSH - }.guessOs + } + HighLevelHostAccessor.create( + credentialsHolder, + true + ).hostCommandExecutor.guessOs() } catch (e: Exception) { logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e") null @@ -141,7 +146,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit IntelliJPlatformProduct.values() .filter { it.showInGateway } .flatMap { CachingProductsJsonWrapper.getAvailableIdes(it, workspaceOS) } - .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.downloadLink, ide.presentableVersion) } + .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.download, null, ide.presentableVersion) } } if (idesWithStatus.isEmpty()) { @@ -158,20 +163,20 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit return when (os) { OS.LINUX -> when (arch) { Arch.AMD64 -> DeployTargetOS(OSKind.Linux, OSArch.X86_64) - Arch.ARM64 -> DeployTargetOS(OSKind.Linux, OSArch.Aarch64) - Arch.ARMV7 -> DeployTargetOS(OSKind.Linux, OSArch.Unknown) + Arch.ARM64 -> DeployTargetOS(OSKind.Linux, OSArch.ARM_64) + Arch.ARMV7 -> DeployTargetOS(OSKind.Linux, OSArch.UNKNOWN) } OS.WINDOWS -> when (arch) { Arch.AMD64 -> DeployTargetOS(OSKind.Windows, OSArch.X86_64) - Arch.ARM64 -> DeployTargetOS(OSKind.Windows, OSArch.Aarch64) - Arch.ARMV7 -> DeployTargetOS(OSKind.Windows, OSArch.Unknown) + Arch.ARM64 -> DeployTargetOS(OSKind.Windows, OSArch.ARM_64) + Arch.ARMV7 -> DeployTargetOS(OSKind.Windows, OSArch.UNKNOWN) } OS.MAC -> when (arch) { Arch.AMD64 -> DeployTargetOS(OSKind.MacOs, OSArch.X86_64) - Arch.ARM64 -> DeployTargetOS(OSKind.MacOs, OSArch.Aarch64) - Arch.ARMV7 -> DeployTargetOS(OSKind.MacOs, OSArch.Unknown) + Arch.ARM64 -> DeployTargetOS(OSKind.MacOs, OSArch.ARM_64) + Arch.ARMV7 -> DeployTargetOS(OSKind.MacOs, OSArch.UNKNOWN) } } } @@ -187,7 +192,7 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit "project_path" to tfProject.text, "ide_product_code" to selectedIDE.product.productCode, "ide_build_number" to selectedIDE.buildNumber, - "ide_download_link" to selectedIDE.source, + "ide_download_link" to selectedIDE.download!!.link, "web_terminal_link" to "${terminalLink.url}" ) )