Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2eb4848

Browse files
authored
impl: visual text progress during Coder CLI downloading (#130)
This PR implements a mechanism to provide recurrent stats about thenumber of the KB and MB of Coder CLI downloaded.
1 parent8eb08e9 commit2eb4848

File tree

14 files changed

+166
-120
lines changed

14 files changed

+166
-120
lines changed

‎CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
##Unreleased
44

5+
###Added
6+
7+
- visual text progress during Coder CLI downloading
8+
59
###Changed
610

711
- the plugin will now remember the SSH connection state for each workspace, and it will try to automatically

‎src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import com.coder.toolbox.util.CoderProtocolHandler
99
importcom.coder.toolbox.util.DialogUi
1010
importcom.coder.toolbox.util.withPath
1111
importcom.coder.toolbox.views.Action
12-
importcom.coder.toolbox.views.AuthWizardPage
12+
importcom.coder.toolbox.views.CoderCliSetupWizardPage
1313
importcom.coder.toolbox.views.CoderSettingsPage
1414
importcom.coder.toolbox.views.NewEnvironmentPage
15-
importcom.coder.toolbox.views.state.AuthWizardState
15+
importcom.coder.toolbox.views.state.CoderCliSetupWizardState
1616
importcom.coder.toolbox.views.state.WizardStep
1717
importcom.jetbrains.toolbox.api.core.ui.icons.SvgIcon
1818
importcom.jetbrains.toolbox.api.core.ui.icons.SvgIcon.IconType
@@ -242,7 +242,7 @@ class CoderRemoteProvider(
242242
environments.value=LoadableState.Value(emptyList())
243243
isInitialized.update {false }
244244
client=null
245-
AuthWizardState.resetSteps()
245+
CoderCliSetupWizardState.resetSteps()
246246
}
247247

248248
overrideval svgIcon:SvgIcon=
@@ -301,7 +301,7 @@ class CoderRemoteProvider(
301301
*/
302302
overridesuspendfunhandleUri(uri:URI) {
303303
linkHandler.handle(
304-
uri,shouldDoAutoLogin(),
304+
uri,shouldDoAutoSetup(),
305305
{
306306
coderHeaderPage.isBusyCreatingNewEnvironment.update {
307307
true
@@ -343,17 +343,17 @@ class CoderRemoteProvider(
343343
* list.
344344
*/
345345
overridefungetOverrideUiPage():UiPage? {
346-
// Showsign in page if we have not configured the client yet.
346+
// Showthe setup page if we have not configured the client yet.
347347
if (client==null) {
348348
val errorBuffer= mutableListOf<Throwable>()
349-
// When coming back to the application,authenticate immediately.
350-
valautologin=shouldDoAutoLogin()
349+
// When coming back to the application,initializeSession immediately.
350+
valautoSetup=shouldDoAutoSetup()
351351
context.secrets.lastToken.let { lastToken->
352352
context.secrets.lastDeploymentURL.let { lastDeploymentURL->
353-
if (autologin&& lastDeploymentURL.isNotBlank()&& (lastToken.isNotBlank()||!settings.requireTokenAuth)) {
353+
if (autoSetup&& lastDeploymentURL.isNotBlank()&& (lastToken.isNotBlank()||!settings.requireTokenAuth)) {
354354
try {
355-
AuthWizardState.goToStep(WizardStep.LOGIN)
356-
returnAuthWizardPage(context, settingsPage, visibilityState,true, ::onConnect)
355+
CoderCliSetupWizardState.goToStep(WizardStep.CONNECT)
356+
returnCoderCliSetupWizardPage(context, settingsPage, visibilityState,true, ::onConnect)
357357
}catch (ex:Exception) {
358358
errorBuffer.add(ex)
359359
}
@@ -363,18 +363,19 @@ class CoderRemoteProvider(
363363
firstRun=false
364364

365365
// Login flow.
366-
val authWizard=AuthWizardPage(context, settingsPage, visibilityState, onConnect= ::onConnect)
366+
val setupWizardPage=
367+
CoderCliSetupWizardPage(context, settingsPage, visibilityState, onConnect= ::onConnect)
367368
// We might have navigated here due to a polling error.
368369
errorBuffer.forEach {
369-
authWizard.notify("Error encountered", it)
370+
setupWizardPage.notify("Error encountered", it)
370371
}
371372
// and now reset the errors, otherwise we show it every time on the screen
372-
returnauthWizard
373+
returnsetupWizardPage
373374
}
374375
returnnull
375376
}
376377

377-
privatefunshouldDoAutoLogin():Boolean= firstRun&& context.secrets.rememberMe==true
378+
privatefunshouldDoAutoSetup():Boolean= firstRun&& context.secrets.rememberMe==true
378379

379380
privatesuspendfunonConnect(client:CoderRestClient,cli:CoderCLIManager) {
380381
// Store the URL and token for use next time.

‎src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import java.net.HttpURLConnection
3232
importjava.net.URL
3333
importjava.nio.file.Files
3434
importjava.nio.file.Path
35-
importjava.nio.file.StandardCopyOption
35+
importjava.nio.file.StandardOpenOption
3636
importjava.util.zip.GZIPInputStream
3737
importjavax.net.ssl.HttpsURLConnection
3838

@@ -44,6 +44,8 @@ internal data class Version(
4444
@Json(name="version")valversion:String,
4545
)
4646

47+
privateconstvalDOWNLOADING_CODER_CLI="Downloading Coder CLI..."
48+
4749
/**
4850
* Do as much as possible to get a valid, up-to-date CLI.
4951
*
@@ -60,6 +62,7 @@ fun ensureCLI(
6062
context:CoderToolboxContext,
6163
deploymentURL:URL,
6264
buildVersion:String,
65+
showTextProgress: (String)->Unit
6366
):CoderCLIManager {
6467
val settings= context.settingsStore.readOnly()
6568
val cli=CoderCLIManager(deploymentURL, context.logger, settings)
@@ -76,9 +79,10 @@ fun ensureCLI(
7679

7780
// If downloads are enabled download the new version.
7881
if (settings.enableDownloads) {
79-
context.logger.info("Downloading Coder CLI...")
82+
context.logger.info(DOWNLOADING_CODER_CLI)
83+
showTextProgress(DOWNLOADING_CODER_CLI)
8084
try {
81-
cli.download()
85+
cli.download(buildVersion, showTextProgress)
8286
return cli
8387
}catch (e: java.nio.file.AccessDeniedException) {
8488
// Might be able to fall back to the data directory.
@@ -98,8 +102,9 @@ fun ensureCLI(
98102
}
99103

100104
if (settings.enableDownloads) {
101-
context.logger.info("Downloading Coder CLI...")
102-
dataCLI.download()
105+
context.logger.info(DOWNLOADING_CODER_CLI)
106+
showTextProgress(DOWNLOADING_CODER_CLI)
107+
dataCLI.download(buildVersion, showTextProgress)
103108
return dataCLI
104109
}
105110

@@ -137,7 +142,7 @@ class CoderCLIManager(
137142
/**
138143
* Download the CLI from the deployment if necessary.
139144
*/
140-
fundownload():Boolean {
145+
fundownload(buildVersion:String,showTextProgress: (String)->Unit):Boolean {
141146
val eTag= getBinaryETag()
142147
val conn= remoteBinaryURL.openConnection()asHttpURLConnection
143148
if (!settings.headerCommand.isNullOrBlank()) {
@@ -162,13 +167,27 @@ class CoderCLIManager(
162167
when (conn.responseCode) {
163168
HttpURLConnection.HTTP_OK-> {
164169
logger.info("Downloading binary to$localBinaryPath")
170+
Files.deleteIfExists(localBinaryPath)
165171
Files.createDirectories(localBinaryPath.parent)
166-
conn.inputStream.use {
167-
Files.copy(
168-
if (conn.contentEncoding=="gzip")GZIPInputStream(it)else it,
169-
localBinaryPath,
170-
StandardCopyOption.REPLACE_EXISTING,
171-
)
172+
val outputStream=Files.newOutputStream(
173+
localBinaryPath,
174+
StandardOpenOption.CREATE,
175+
StandardOpenOption.TRUNCATE_EXISTING
176+
)
177+
val sourceStream=if (conn.isGzip())GZIPInputStream(conn.inputStream)else conn.inputStream
178+
179+
val buffer=ByteArray(DEFAULT_BUFFER_SIZE)
180+
var bytesRead:Int
181+
var totalRead=0L
182+
183+
sourceStream.use { source->
184+
outputStream.use { sink->
185+
while (source.read(buffer).also { bytesRead= it }!=-1) {
186+
sink.write(buffer,0, bytesRead)
187+
totalRead+= bytesRead
188+
showTextProgress("${settings.defaultCliBinaryNameByOsAndArch}$buildVersion -${totalRead.toHumanReadableSize()} downloaded")
189+
}
190+
}
172191
}
173192
if (getOS()!=OS.WINDOWS) {
174193
localBinaryPath.toFile().setExecutable(true)
@@ -178,6 +197,7 @@ class CoderCLIManager(
178197

179198
HttpURLConnection.HTTP_NOT_MODIFIED-> {
180199
logger.info("Using cached binary at$localBinaryPath")
200+
showTextProgress("Using cached binary")
181201
returnfalse
182202
}
183203
}
@@ -190,6 +210,21 @@ class CoderCLIManager(
190210
throwResponseException("Unexpected response from$remoteBinaryURL", conn.responseCode)
191211
}
192212

213+
privatefun HttpURLConnection.isGzip():Boolean=this.contentEncoding.equals("gzip", ignoreCase=true)
214+
215+
fun Long.toHumanReadableSize():String {
216+
if (this<1024)return"$this B"
217+
218+
val kb=this/1024.0
219+
if (kb<1024)returnString.format("%.1f KB", kb)
220+
221+
val mb= kb/1024.0
222+
if (mb<1024)returnString.format("%.1f MB", mb)
223+
224+
val gb= mb/1024.0
225+
returnString.format("%.1f GB", gb)
226+
}
227+
193228
/**
194229
* Return the entity tag for the binary on disk, if any.
195230
*/
@@ -203,7 +238,7 @@ class CoderCLIManager(
203238
}
204239

205240
/**
206-
* Use the provided token toauthenticate the CLI.
241+
* Use the provided token toinitializeSession the CLI.
207242
*/
208243
funlogin(token:String):String {
209244
logger.info("Storing CLI credentials in$coderConfigPath")

‎src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,11 @@ open class CoderRestClient(
131131
}
132132

133133
/**
134-
* Authenticate and load information about the current user and the build
135-
* version.
134+
* Load information about the current user and the build version.
136135
*
137136
* @throws [APIResponseException].
138137
*/
139-
suspendfunauthenticate():User {
138+
suspendfuninitializeSession():User {
140139
me= me()
141140
buildVersion= buildInfo().version
142141
return me
@@ -149,7 +148,12 @@ open class CoderRestClient(
149148
suspendfunme():User {
150149
val userResponse= retroRestClient.me()
151150
if (!userResponse.isSuccessful) {
152-
throwAPIResponseException("authenticate", url, userResponse.code(), userResponse.parseErrorBody(moshi))
151+
throwAPIResponseException(
152+
"initializeSession",
153+
url,
154+
userResponse.code(),
155+
userResponse.parseErrorBody(moshi)
156+
)
153157
}
154158

155159
return userResponse.body()!!

‎src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import kotlin.time.Duration.Companion.seconds
2424
importkotlin.time.toJavaDuration
2525

2626
privateconstvalCAN_T_HANDLE_URI_TITLE="Can't handle URI"
27+
privateval noOpTextProgress: (String)->Unit= { _-> }
2728

2829
@Suppress("UnstableApiUsage")
2930
openclassCoderProtocolHandler(
@@ -143,7 +144,7 @@ open class CoderProtocolHandler(
143144
if (settings.requireTokenAuth) tokenelsenull,
144145
PluginManager.pluginInfo.version
145146
)
146-
client.authenticate()
147+
client.initializeSession()
147148
return client
148149
}
149150

@@ -304,7 +305,8 @@ open class CoderProtocolHandler(
304305
val cli= ensureCLI(
305306
context,
306307
deploymentURL.toURL(),
307-
restClient.buildInfo().version
308+
restClient.buildInfo().version,
309+
noOpTextProgress
308310
)
309311

310312
// We only need to log in if we are using token-based auth.

‎src/main/kotlin/com/coder/toolbox/views/AuthWizardPage.ktrenamed to‎src/main/kotlin/com/coder/toolbox/views/CoderCliSetupWizardPage.kt

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import com.coder.toolbox.cli.CoderCLIManager
55
importcom.coder.toolbox.sdk.CoderRestClient
66
importcom.coder.toolbox.sdk.ex.APIResponseException
77
importcom.coder.toolbox.util.toURL
8-
importcom.coder.toolbox.views.state.AuthContext
9-
importcom.coder.toolbox.views.state.AuthWizardState
8+
importcom.coder.toolbox.views.state.CoderCliSetupContext
9+
importcom.coder.toolbox.views.state.CoderCliSetupWizardState
1010
importcom.coder.toolbox.views.state.WizardStep
1111
importcom.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
1212
importcom.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
@@ -16,26 +16,26 @@ import kotlinx.coroutines.flow.update
1616
importkotlinx.coroutines.launch
1717
importjava.util.UUID
1818

19-
classAuthWizardPage(
19+
classCoderCliSetupWizardPage(
2020
privatevalcontext:CoderToolboxContext,
2121
privatevalsettingsPage:CoderSettingsPage,
2222
privatevalvisibilityState:MutableStateFlow<ProviderVisibilityState>,
23-
initialAutoLogin:Boolean =false,
23+
initialAutoSetup:Boolean =false,
2424
onConnect:suspend (
2525
client:CoderRestClient,
2626
cli:CoderCLIManager,
2727
)->Unit,
28-
) : CoderPage(context.i18n.ptrl("Authenticate toCoder"),false) {
29-
privatevalshouldAutoLogin=MutableStateFlow(initialAutoLogin)
28+
) : CoderPage(context.i18n.ptrl("Setting upCoder"),false) {
29+
privatevalshouldAutoSetup=MutableStateFlow(initialAutoSetup)
3030
privateval settingsAction=Action(context.i18n.ptrl("Settings"), actionBlock= {
3131
context.ui.showUiPage(settingsPage)
3232
})
3333

34-
privatevalsignInStep=SignInStep(context,this::notify)
34+
privatevaldeploymentUrlStep=DeploymentUrlStep(context,this::notify)
3535
privateval tokenStep=TokenStep(context)
3636
privateval connectStep=ConnectStep(
3737
context,
38-
shouldAutoLogin,
38+
shouldAutoSetup,
3939
this::notify,
4040
this::displaySteps,
4141
onConnect
@@ -50,9 +50,9 @@ class AuthWizardPage(
5050
privateval errorBuffer= mutableListOf<Throwable>()
5151

5252
init {
53-
if (shouldAutoLogin.value) {
54-
AuthContext.url= context.secrets.lastDeploymentURL.toURL()
55-
AuthContext.token= context.secrets.lastToken
53+
if (shouldAutoSetup.value) {
54+
CoderCliSetupContext.url= context.secrets.lastDeploymentURL.toURL()
55+
CoderCliSetupContext.token= context.secrets.lastToken
5656
}
5757
}
5858

@@ -67,22 +67,22 @@ class AuthWizardPage(
6767
}
6868

6969
privatefundisplaySteps() {
70-
when (AuthWizardState.currentStep()) {
70+
when (CoderCliSetupWizardState.currentStep()) {
7171
WizardStep.URL_REQUEST-> {
7272
fields.update {
73-
listOf(signInStep.panel)
73+
listOf(deploymentUrlStep.panel)
7474
}
7575
actionButtons.update {
7676
listOf(
77-
Action(context.i18n.ptrl("Sign In"), closesPage=false, actionBlock= {
78-
if (signInStep.onNext()) {
77+
Action(context.i18n.ptrl("Next"), closesPage=false, actionBlock= {
78+
if (deploymentUrlStep.onNext()) {
7979
displaySteps()
8080
}
8181
}),
8282
settingsAction
8383
)
8484
}
85-
signInStep.onVisible()
85+
deploymentUrlStep.onVisible()
8686
}
8787

8888
WizardStep.TOKEN_REQUEST-> {
@@ -106,7 +106,7 @@ class AuthWizardPage(
106106
tokenStep.onVisible()
107107
}
108108

109-
WizardStep.LOGIN-> {
109+
WizardStep.CONNECT-> {
110110
fields.update {
111111
listOf(connectStep.panel)
112112
}
@@ -115,7 +115,7 @@ class AuthWizardPage(
115115
settingsAction,
116116
Action(context.i18n.ptrl("Back"), closesPage=false, actionBlock= {
117117
connectStep.onBack()
118-
shouldAutoLogin.update {
118+
shouldAutoSetup.update {
119119
false
120120
}
121121
displaySteps()
@@ -150,7 +150,7 @@ class AuthWizardPage(
150150
context.cs.launch {
151151
context.ui.showSnackbar(
152152
UUID.randomUUID().toString(),
153-
context.i18n.ptrl("Error encounteredduring authentication"),
153+
context.i18n.ptrl("Error encounteredwhile setting up Coder"),
154154
context.i18n.pnotr(textError?:""),
155155
context.i18n.ptrl("Dismiss")
156156
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp