Skip to content

Commit ccd01c4

Browse files
committed
Integrate update notifications into VS Code
1 parent 069c523 commit ccd01c4

15 files changed

+161
-75
lines changed

ci/vscode.patch

+67-5
Original file line numberDiff line numberDiff line change
@@ -444,17 +444,18 @@ index d0f6e6b18a..1966fd297d 100644
444444
-
445445
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
446446
new file mode 100644
447-
index 0000000000..3a62205b38
447+
index 0000000000..1e6bca3b52
448448
--- /dev/null
449449
+++ b/src/vs/server/browser/client.ts
450-
@@ -0,0 +1,162 @@
450+
@@ -0,0 +1,224 @@
451451
+import { Emitter } from 'vs/base/common/event';
452452
+import { URI } from 'vs/base/common/uri';
453453
+import { localize } from 'vs/nls';
454454
+import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
455455
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
456456
+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
457457
+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
458+
+import { ILogService } from 'vs/platform/log/common/log';
458459
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
459460
+import { Registry } from 'vs/platform/registry/common/platform';
460461
+import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
@@ -575,6 +576,67 @@ index 0000000000..3a62205b38
575576
+ }
576577
+ });
577578
+ }
579+
+
580+
+ const applyUpdate = async (): Promise<void> => {
581+
+ (services.get(ILogService) as ILogService).debug("Applying update...");
582+
+
583+
+ const response = await fetch("./update/apply", {
584+
+ headers: { "content-type": "application/json" },
585+
+ });
586+
+ if (response.status !== 200) {
587+
+ throw new Error("Unexpected response");
588+
+ }
589+
+
590+
+ const json = await response.json();
591+
+ if (!json.isLatest) {
592+
+ throw new Error("Update failed");
593+
+ }
594+
+
595+
+ (services.get(INotificationService) as INotificationService).info(`Updated to ${json.version}`);
596+
+ };
597+
+
598+
+ const getUpdate = async (): Promise<void> => {
599+
+ (services.get(ILogService) as ILogService).debug("Checking for update...");
600+
+
601+
+ const response = await fetch("./update", {
602+
+ headers: { "content-type": "application/json" },
603+
+ });
604+
+ if (response.status !== 200) {
605+
+ throw new Error("unexpected response");
606+
+ }
607+
+
608+
+ const json = await response.json();
609+
+ if (json.isLatest) {
610+
+ return;
611+
+ }
612+
+
613+
+ (services.get(INotificationService) as INotificationService).notify({
614+
+ severity: Severity.Info,
615+
+ message: `code-server has an update: ${json.version}`,
616+
+ actions: {
617+
+ primary: [{
618+
+ id: 'update',
619+
+ label: 'Apply Update',
620+
+ tooltip: '',
621+
+ class: undefined,
622+
+ enabled: true,
623+
+ checked: true,
624+
+ dispose: () => undefined,
625+
+ run: applyUpdate,
626+
+ }],
627+
+ }
628+
+ });
629+
+ };
630+
+
631+
+ const updateLoop = (): void => {
632+
+ getUpdate().catch((error) => {
633+
+ (services.get(ILogService) as ILogService).warn(error);
634+
+ }).finally(() => {
635+
+ setTimeout(updateLoop, 300000);
636+
+ });
637+
+ };
638+
+
639+
+ updateLoop();
578640
+};
579641
+
580642
+export interface Query {
@@ -2968,7 +3030,7 @@ index bbb72e9511..0785d3391d 100644
29683030
-registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { whenReady = Promise.resolve(); });
29693031
+registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
29703032
diff --git a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
2971-
index 79455414c0..a407593b4d 100644
3033+
index 79455414c0..8931c1355a 100644
29723034
--- a/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
29733035
+++ b/src/vs/workbench/services/extensions/worker/extensionHostWorkerMain.ts
29743036
@@ -14,7 +14,11 @@
@@ -2978,8 +3040,8 @@ index 79455414c0..a407593b4d 100644
29783040
- catchError: true
29793041
+ catchError: true,
29803042
+ paths: {
2981-
+ '@coder/node-browser': `../node_modules/@coder/node-browser/out/client/client.js`,
2982-
+ '@coder/requirefs': `../node_modules/@coder/requirefs/out/requirefs.js`,
3043+
+ '@coder/node-browser': `{{BASE}}/static/{{COMMIT}}/lib/vscode/node_modules/@coder/node-browser/out/client/client.js`,
3044+
+ '@coder/requirefs': `{{BASE}}/static/{{COMMIT}}/lib/vscode/node_modules/@coder/requirefs/out/requirefs.js`,
29833045
+ }
29843046
});
29853047

src/browser/pages/app.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
crossorigin="use-credentials"
1616
/>
1717
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/code-server.png" />
18-
<link href="{{BASE}}/static/{{COMMIT}}/dist/app.css" rel="stylesheet" />
18+
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
1919
<meta id="coder-options" data-settings="{{OPTIONS}}" />
2020
</head>
2121
<body>

src/browser/pages/error.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
crossorigin="use-credentials"
1616
/>
1717
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/code-server.png" />
18-
<link href="{{BASE}}/static/{{COMMIT}}/dist/app.css" rel="stylesheet" />
18+
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
1919
<meta id="coder-options" data-settings="{{OPTIONS}}" />
2020
</head>
2121
<body>
@@ -26,7 +26,7 @@ <h2 class="header">{{ERROR_HEADER}}</h2>
2626
{{ERROR_BODY}}
2727
</div>
2828
<div class="links">
29-
<a class="link" href="{{BASE}}">go home</a>
29+
<a class="link" href="{{BASE}}{{TO}}">go home</a>
3030
</div>
3131
</div>
3232
</div>

src/browser/pages/home.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
crossorigin="use-credentials"
1616
/>
1717
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/code-server.png" />
18-
<link href="{{BASE}}/static/{{COMMIT}}/dist/app.css" rel="stylesheet" />
18+
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
1919
<meta id="coder-options" data-settings="{{OPTIONS}}" />
2020
</head>
2121
<body>

src/browser/pages/login.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
crossorigin="use-credentials"
1919
/>
2020
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/code-server.png" />
21-
<link href="{{BASE}}/static/{{COMMIT}}/dist/app.css" rel="stylesheet" />
21+
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
2222
<meta id="coder-options" data-settings="{{OPTIONS}}" />
2323
</head>
2424
<body>

src/browser/pages/update.css

+14
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,17 @@
1010
color: red;
1111
margin-top: 16px;
1212
}
13+
14+
.update-form > .links {
15+
margin-top: 20px;
16+
}
17+
18+
.update-form > .links > .link {
19+
color: rgb(87, 114, 245);
20+
text-align: center;
21+
text-decoration: none;
22+
}
23+
24+
.update-form > .links > .link:hover {
25+
text-decoration: underline;
26+
}

src/browser/pages/update.html

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
crossorigin="use-credentials"
1616
/>
1717
<link rel="apple-touch-icon" href="{{BASE}}/static/{{COMMIT}}/src/browser/media/code-server.png" />
18-
<link href="{{BASE}}/static/{{COMMIT}}/dist/app.css" rel="stylesheet" />
18+
<link href="{{BASE}}/static/{{COMMIT}}/dist/pages/app.css" rel="stylesheet" />
1919
<meta id="coder-options" data-settings="{{OPTIONS}}" />
2020
</head>
2121
<body>
@@ -26,9 +26,11 @@ <h1 class="main">Update</h1>
2626
<div class="sub">Update code-server.</div>
2727
</div>
2828
<div class="content">
29-
<form class="update-form" method="post">
29+
<form class="update-form" action="{{BASE}}/update/apply">
3030
{{UPDATE_STATUS}} {{ERROR}}
31-
<a class="cancel -button" href="{{BASE}}">Cancel</a>
31+
<div class="links">
32+
<a class="link" href="{{BASE}}{{TO}}">go home</a>
33+
</div>
3234
</form>
3335
</div>
3436
</div>

src/node/app/api.ts

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ export class ApiHttpProvider extends HttpProvider {
5959

6060
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
6161
this.ensureAuthenticated(request)
62+
if (route.requestPath !== "/index.html") {
63+
throw new HttpError("Not found", HttpCode.NotFound)
64+
}
6265

6366
switch (route.base) {
6467
case ApiEndpoint.applications:

src/node/app/app.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ export class AppHttpProvider extends HttpProvider {
1616
return { redirect: "/login", query: { to: route.fullPath } }
1717
}
1818

19+
this.ensureMethod(request)
20+
if (route.requestPath !== "/index.html") {
21+
throw new HttpError("Not found", HttpCode.NotFound)
22+
}
23+
1924
// Run an existing app, but if it doesn't exist go ahead and start it.
2025
let app = this.api.getRunningApplication(route.base)
2126
let sessionId = app && app.sessionId
@@ -38,8 +43,4 @@ export class AppHttpProvider extends HttpProvider {
3843
response.content = response.content.replace(/{{APP_NAME}}/, name)
3944
return this.replaceTemplates(route, response, sessionId)
4045
}
41-
42-
public async handleWebSocket(): Promise<true> {
43-
throw new HttpError("Not found", HttpCode.NotFound)
44-
}
4546
}

src/node/app/dashboard.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ export class DashboardHttpProvider extends HttpProvider {
2020
}
2121

2222
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
23+
if (route.requestPath !== "/index.html") {
24+
throw new HttpError("Not found", HttpCode.NotFound)
25+
}
26+
2327
switch (route.base) {
2428
case "/delete": {
25-
this.ensureMethod(request, "POST")
2629
this.ensureAuthenticated(request)
30+
this.ensureMethod(request, "POST")
2731
const data = await this.getData(request)
2832
const p = data ? querystring.parse(data) : {}
2933
this.api.deleteSession(p.sessionId as string)
@@ -32,9 +36,7 @@ export class DashboardHttpProvider extends HttpProvider {
3236

3337
case "/": {
3438
this.ensureMethod(request)
35-
if (route.requestPath !== "/index.html") {
36-
throw new HttpError("Not found", HttpCode.NotFound)
37-
} else if (!this.authenticated(request)) {
39+
if (!this.authenticated(request)) {
3840
return { redirect: "/login", query: { to: this.options.base } }
3941
}
4042
return this.getRoot(route)
@@ -69,10 +71,6 @@ export class DashboardHttpProvider extends HttpProvider {
6971
return this.replaceTemplates(route, response)
7072
}
7173

72-
public async handleWebSocket(): Promise<true> {
73-
throw new HttpError("Not found", HttpCode.NotFound)
74-
}
75-
7674
private getRecentProjectRows(base: string, recents: RecentResponse): string {
7775
return recents.paths.length > 0 || recents.workspaces.length > 0
7876
? recents.paths.map((recent) => this.getRecentProjectRow(base, recent)).join("\n") +
@@ -151,7 +149,7 @@ export class DashboardHttpProvider extends HttpProvider {
151149
</div>
152150
<div class="item">
153151
${humanize(update.checked)}
154-
<a class="sub -link" href="${base}/update">Update now</a>
152+
<a class="sub -link" href="${base}/update?to=${this.options.base}">Update now</a>
155153
</div>
156154
<div class="item" >Current: ${this.update.currentVersion}</div>
157155
</div>`

src/node/app/login.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@ interface LoginPayload {
1818
*/
1919
export class LoginHttpProvider extends HttpProvider {
2020
public async handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
21-
if (this.options.auth !== AuthType.Password) {
21+
if (this.options.auth !== AuthType.Password || route.requestPath !== "/index.html") {
2222
throw new HttpError("Not found", HttpCode.NotFound)
2323
}
2424
switch (route.base) {
2525
case "/":
26-
if (route.requestPath !== "/index.html") {
27-
throw new HttpError("Not found", HttpCode.NotFound)
28-
}
29-
3026
switch (request.method) {
3127
case "POST":
28+
this.ensureMethod(request, ["GET", "POST"])
3229
return this.tryLogin(route, request)
3330
default:
3431
this.ensureMethod(request)
@@ -110,8 +107,4 @@ export class LoginHttpProvider extends HttpProvider {
110107

111108
throw new Error("Missing password")
112109
}
113-
114-
public async handleWebSocket(): Promise<undefined> {
115-
return undefined
116-
}
117110
}

src/node/app/static.ts

-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as http from "http"
2-
import { HttpCode, HttpError } from "../../common/http"
32
import { HttpProvider, HttpResponse, Route } from "../http"
43

54
/**
@@ -32,8 +31,4 @@ export class StaticHttpProvider extends HttpProvider {
3231
}
3332
return this.getResource(this.rootPath, ...split)
3433
}
35-
36-
public async handleWebSocket(): Promise<true> {
37-
throw new HttpError("Not found", HttpCode.NotFound)
38-
}
3934
}

0 commit comments

Comments
 (0)