Skip to content

Commit 2ac4908

Browse files
committed
Fix: download Coder CLI only once
- the download manager for the Coder CLI is now able to skip the download of the binary if the same version is already present. - resolves #3
1 parent 53c2994 commit 2ac4908

File tree

7 files changed

+53
-12
lines changed

7 files changed

+53
-12
lines changed

src/main/kotlin/com/coder/gateway/models/CoderWorkspacesWizardModel.kt

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.coder.gateway.sdk.v2.models.Workspace
55
data class CoderWorkspacesWizardModel(
66
var coderURL: String = "https://localhost",
77
var token: String = "",
8+
var buildVersion: String = "",
89
var workspaces: List<Workspace> = mutableListOf(),
910
var selectedWorkspace: Workspace? = null
1011
)

src/main/kotlin/com/coder/gateway/sdk/CoderCLIDownloader.kt

+11-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ import java.io.InputStream
55
import java.net.URL
66
import java.nio.file.Files
77
import java.nio.file.Path
8+
import java.nio.file.Paths
89
import java.nio.file.StandardCopyOption
910

10-
class CoderCLIDownloader {
11+
class CoderCLIDownloader(private val buildVersion: String) {
1112

1213
fun downloadCLI(url: URL, outputName: String, ext: String): Path {
13-
val tempPath: Path = Files.createTempFile(outputName, ext)
14-
logger.info("Downloading $url to $tempPath")
14+
val filename = if (ext.isNullOrBlank()) "${outputName}-$buildVersion" else "${outputName}-${buildVersion}.${ext}"
15+
val cliPath = Paths.get(System.getProperty("java.io.tmpdir"), filename)
16+
if (Files.exists(cliPath)) {
17+
logger.info("${cliPath.toAbsolutePath()} already exists, skipping download")
18+
return cliPath
19+
}
20+
logger.info("Starting Coder CLI download to ${cliPath.toAbsolutePath()}")
1521
url.openStream().use {
16-
Files.copy(it as InputStream, tempPath, StandardCopyOption.REPLACE_EXISTING)
22+
Files.copy(it as InputStream, cliPath, StandardCopyOption.REPLACE_EXISTING)
1723
}
18-
return tempPath
24+
return cliPath
1925
}
2026

2127
companion object {

src/main/kotlin/com/coder/gateway/sdk/CoderCLIManager.kt

+4-5
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,17 @@ import com.intellij.openapi.diagnostic.Logger
44
import java.net.URL
55
import java.nio.file.Path
66

7-
class CoderCLIManager(private val url: URL) {
8-
private val coderCLIDownloader = CoderCLIDownloader()
7+
class CoderCLIManager(private val url: URL, private val buildVersion: String) {
8+
private val coderCLIDownloader = CoderCLIDownloader(buildVersion)
99

1010
fun download(): Path? {
1111
val os = getOS()
1212
val cliName = getCoderCLIForOS(os, getArch()) ?: return null
13-
logger.info("Resolved OS: $os with arch: ${getArch()}")
1413
val cliNameWitExt = if (os == OS.WINDOWS) "$cliName.exe" else cliName
15-
return coderCLIDownloader.downloadCLI(URL(url.protocol, url.host, url.port, "/bin/$cliNameWitExt"), cliName, if (os == OS.WINDOWS) ".exe" else "")
14+
return coderCLIDownloader.downloadCLI(URL(url.protocol, url.host, url.port, "/bin/$cliNameWitExt"), cliName, if (os == OS.WINDOWS) "exe" else "")
1615
}
1716

18-
fun getCoderCLIForOS(os: OS?, arch: Arch?): String? {
17+
private fun getCoderCLIForOS(os: OS?, arch: Arch?): String? {
1918
logger.info("Resolving coder cli for $os $arch")
2019
if (os == null) {
2120
return null

src/main/kotlin/com/coder/gateway/sdk/CoderRestClientService.kt

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.coder.gateway.sdk.convertors.InstantConverter
44
import com.coder.gateway.sdk.ex.AuthenticationException
55
import com.coder.gateway.sdk.v2.CoderV2RestFacade
66
import com.coder.gateway.sdk.v2.models.AgentGitSSHKeys
7+
import com.coder.gateway.sdk.v2.models.BuildInfo
78
import com.coder.gateway.sdk.v2.models.User
89
import com.coder.gateway.sdk.v2.models.Workspace
910
import com.google.gson.Gson
@@ -26,6 +27,7 @@ class CoderRestClientService {
2627
lateinit var coderURL: URL
2728
lateinit var sessionToken: String
2829
lateinit var me: User
30+
lateinit var buildVersion: String
2931

3032
/**
3133
* This must be called before anything else. It will authenticate with coder and retrieve a session token
@@ -66,6 +68,8 @@ class CoderRestClientService {
6668
coderURL = url
6769
sessionToken = token
6870
me = userResponse.body()!!
71+
buildVersion = buildInfo().version
72+
6973
return me
7074
}
7175

@@ -86,4 +90,12 @@ class CoderRestClientService {
8690

8791
return sshKeysResponse.body()!!
8892
}
93+
94+
fun buildInfo(): BuildInfo {
95+
val buildInfoResponse = retroRestClient.buildInfo().execute()
96+
if (!buildInfoResponse.isSuccessful) {
97+
throw java.lang.IllegalStateException("Could not retrieve build information for Coder instance $coderURL, reason:${buildInfoResponse.message()}")
98+
}
99+
return buildInfoResponse.body()!!
100+
}
89101
}

src/main/kotlin/com/coder/gateway/sdk/v2/CoderV2RestFacade.kt

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.coder.gateway.sdk.v2
22

33
import com.coder.gateway.sdk.v2.models.AgentGitSSHKeys
4+
import com.coder.gateway.sdk.v2.models.BuildInfo
45
import com.coder.gateway.sdk.v2.models.LoginWithPasswordRequest
56
import com.coder.gateway.sdk.v2.models.LoginWithPasswordResponse
67
import com.coder.gateway.sdk.v2.models.User
@@ -32,4 +33,7 @@ interface CoderV2RestFacade {
3233

3334
@GET("api/v2/workspaceagents/me/gitsshkey")
3435
fun sshKeys(): Call<AgentGitSSHKeys>
36+
37+
@GET("api/v2/buildinfo")
38+
fun buildInfo(): Call<BuildInfo>
3539
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.coder.gateway.sdk.v2.models
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
/**
6+
* Contains build information for a Coder instance.
7+
*
8+
* @param externalUrl a URL referencing the current Coder version.
9+
* For production builds, this will link directly to a release.
10+
* For development builds, this will link to a commit.
11+
*
12+
* @param version the semantic version of the build.
13+
*/
14+
data class BuildInfo(
15+
@SerializedName("external_url") val externalUrl: String,
16+
@SerializedName("version") val version: String
17+
)

src/main/kotlin/com/coder/gateway/views/steps/CoderAuthStepView.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
8484
return false
8585
}
8686
model.token = pastedToken
87+
model.buildVersion = coderClient.buildVersion
88+
8789
val authTask = object : Task.Modal(null, CoderGatewayBundle.message("gateway.connector.view.login.cli.downloader.dialog.title"), false) {
8890
override fun run(pi: ProgressIndicator) {
8991

@@ -93,7 +95,7 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
9395
fraction = 0.1
9496
}
9597

96-
val cliManager = CoderCLIManager(model.coderURL.toURL())
98+
val cliManager = CoderCLIManager(model.coderURL.toURL(), model.buildVersion)
9799
val cli = cliManager.download() ?: throw IllegalStateException("Could not download coder binary")
98100
if (getOS() != OS.WINDOWS) {
99101
pi.fraction = 0.4
@@ -105,7 +107,7 @@ class CoderAuthStepView : CoderWorkspacesWizardStep, Disposable {
105107
fraction = 0.5
106108
}
107109

108-
val loginOutput = ProcessExecutor().command(cli.toAbsolutePath().toString(), "login", model.coderURL, "--token", coderClient.sessionToken).readOutput(true).execute().outputUTF8()
110+
val loginOutput = ProcessExecutor().command(cli.toAbsolutePath().toString(), "login", model.coderURL, "--token", model.token).readOutput(true).execute().outputUTF8()
109111
logger.info("coder-cli login output: $loginOutput")
110112
pi.fraction = 0.8
111113
val sshConfigOutput = ProcessExecutor().command(cli.toAbsolutePath().toString(), "config-ssh").readOutput(true).execute().outputUTF8()

0 commit comments

Comments
 (0)