@@ -72,9 +72,8 @@ export class Remote {
7272 }
7373
7474 // Find the workspace from the URI scheme provided!
75- let workspace : Workspace
7675 try {
77- workspace = await getWorkspaceByOwnerAndName ( parts [ 0 ] , parts [ 1 ] )
76+ this . storage . workspace = await getWorkspaceByOwnerAndName ( parts [ 0 ] , parts [ 1 ] )
7877 } catch ( error ) {
7978 if ( ! axios . isAxiosError ( error ) ) {
8079 throw error
@@ -120,10 +119,10 @@ export class Remote {
120119
121120 const disposables : vscode . Disposable [ ] = [ ]
122121 // Register before connection so the label still displays!
123- disposables . push ( this . registerLabelFormatter ( `${ workspace . owner_name } /${ workspace . name } ` ) )
122+ disposables . push ( this . registerLabelFormatter ( `${ this . storage . workspace . owner_name } /${ this . storage . workspace . name } ` ) )
124123
125124 let buildComplete : undefined | ( ( ) => void )
126- if ( workspace . latest_build . status === "stopped" ) {
125+ if ( this . storage . workspace . latest_build . status === "stopped" ) {
127126 this . vscodeProposed . window . withProgress (
128127 {
129128 location : vscode . ProgressLocation . Notification ,
@@ -135,15 +134,18 @@ export class Remote {
135134 buildComplete = r
136135 } ) ,
137136 )
138- workspace = {
139- ...workspace ,
140- latest_build : await startWorkspace ( workspace . id ) ,
137+ this . storage . workspace = {
138+ ...this . storage . workspace ,
139+ latest_build : await startWorkspace ( this . storage . workspace . id ) ,
141140 }
142141 }
143142
144143 // If a build is running we should stream the logs to the user so they can
145144 // watch what's going on!
146- if ( workspace . latest_build . status === "pending" || workspace . latest_build . status === "starting" ) {
145+ if (
146+ this . storage . workspace . latest_build . status === "pending" ||
147+ this . storage . workspace . latest_build . status === "starting"
148+ ) {
147149 const writeEmitter = new vscode . EventEmitter < string > ( )
148150 // We use a terminal instead of an output channel because it feels more
149151 // familiar to a user!
@@ -160,11 +162,11 @@ export class Remote {
160162 } as Partial < vscode . Pseudoterminal > as any ,
161163 } )
162164 // This fetches the initial bunch of logs.
163- const logs = await getWorkspaceBuildLogs ( workspace . latest_build . id , new Date ( ) )
165+ const logs = await getWorkspaceBuildLogs ( this . storage . workspace . latest_build . id , new Date ( ) )
164166 logs . forEach ( ( log ) => writeEmitter . fire ( log . output + "\r\n" ) )
165167 terminal . show ( true )
166168 // This follows the logs for new activity!
167- let path = `/api/v2/workspacebuilds/${ workspace . latest_build . id } /logs?follow=true`
169+ let path = `/api/v2/workspacebuilds/${ this . storage . workspace . latest_build . id } /logs?follow=true`
168170 if ( logs . length ) {
169171 path += `&after=${ logs [ logs . length - 1 ] . id } `
170172 }
@@ -198,15 +200,15 @@ export class Remote {
198200 } )
199201 } )
200202 writeEmitter . fire ( "Build complete" )
201- workspace = await getWorkspace ( workspace . id )
202- terminal . hide ( )
203+ this . storage . workspace = await getWorkspace ( this . storage . workspace . id )
204+ terminal . dispose ( )
203205
204206 if ( buildComplete ) {
205207 buildComplete ( )
206208 }
207209 }
208210
209- const agents = workspace . latest_build . resources . reduce ( ( acc , resource ) => {
211+ const agents = this . storage . workspace . latest_build . resources . reduce ( ( acc , resource ) => {
210212 return acc . concat ( resource . agents || [ ] )
211213 } , [ ] as WorkspaceAgent [ ] )
212214
@@ -250,7 +252,7 @@ export class Remote {
250252 await fs . writeFile ( this . storage . getUserSettingsPath ( ) , jsonc . applyEdits ( settingsContent , edits ) )
251253
252254 const workspaceUpdate = new vscode . EventEmitter < Workspace > ( )
253- const watchURL = new URL ( `${ this . storage . getURL ( ) } /api/v2/workspaces/${ workspace . id } /watch` )
255+ const watchURL = new URL ( `${ this . storage . getURL ( ) } /api/v2/workspaces/${ this . storage . workspace . id } /watch` )
254256 const eventSource = new EventSource ( watchURL . toString ( ) , {
255257 headers : {
256258 "Coder-Session-Token" : await this . storage . getSessionToken ( ) ,
@@ -262,11 +264,48 @@ export class Remote {
262264 eventSource . addEventListener ( "error" , ( ) => {
263265 // TODO: Add debug output that we got an error here!
264266 } )
267+
268+ const workspaceUpdatedStatus = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , 999 )
269+ disposables . push ( workspaceUpdatedStatus )
270+
271+ let hasShownOutdatedNotification = false
272+ const refreshWorkspaceUpdatedStatus = ( newWorkspace : Workspace ) => {
273+ // If the newly gotten workspace was updated, then we show a notification
274+ // to the user that they should update.
275+ if ( newWorkspace . outdated ) {
276+ if ( ! this . storage . workspace ?. outdated || ! hasShownOutdatedNotification ) {
277+ hasShownOutdatedNotification = true
278+ vscode . window
279+ . showInformationMessage ( "A new version of your workspace is available." , "Update" )
280+ . then ( ( action ) => {
281+ if ( action === "Update" ) {
282+ vscode . commands . executeCommand ( "coder.workspace.update" , newWorkspace )
283+ }
284+ } )
285+ }
286+ }
287+ if ( ! newWorkspace . outdated ) {
288+ vscode . commands . executeCommand ( "setContext" , "coder.workspace.updatable" , false )
289+ workspaceUpdatedStatus . hide ( )
290+ return
291+ }
292+ workspaceUpdatedStatus . name = "Coder Workspace Update"
293+ workspaceUpdatedStatus . text = "$(fold-up) Update Workspace"
294+ workspaceUpdatedStatus . command = "coder.workspace.update"
295+ // Important for hiding the "Update Workspace" command.
296+ vscode . commands . executeCommand ( "setContext" , "coder.workspace.updatable" , true )
297+ workspaceUpdatedStatus . show ( )
298+ }
299+ // Show an initial status!
300+ refreshWorkspaceUpdatedStatus ( this . storage . workspace )
301+
265302 eventSource . addEventListener ( "data" , ( event : MessageEvent < string > ) => {
266303 const workspace = JSON . parse ( event . data ) as Workspace
267304 if ( ! workspace ) {
268305 return
269306 }
307+ refreshWorkspaceUpdatedStatus ( workspace )
308+ this . storage . workspace = workspace
270309 workspaceUpdate . fire ( workspace )
271310 if ( workspace . latest_build . status === "stopping" || workspace . latest_build . status === "stopped" ) {
272311 const action = this . vscodeProposed . window . showInformationMessage (
@@ -283,6 +322,13 @@ export class Remote {
283322 }
284323 this . reloadWindow ( )
285324 }
325+ // If a new build is initialized for a workspace, we automatically
326+ // reload the window. Then the build log will appear, and startup
327+ // will continue as expected.
328+ if ( workspace . latest_build . status === "starting" ) {
329+ this . reloadWindow ( )
330+ return
331+ }
286332 } )
287333
288334 if ( agent . status === "connecting" ) {
@@ -352,7 +398,7 @@ export class Remote {
352398 } )
353399
354400 // Register the label formatter again because SSH overrides it!
355- let label = `${ workspace . owner_name } /${ workspace . name } `
401+ let label = `${ this . storage . workspace . owner_name } /${ this . storage . workspace . name } `
356402 if ( agents . length > 1 ) {
357403 label += `/${ agent . name } `
358404 }
0 commit comments