Skip to content

Commit 554b6d6

Browse files
committed
Remove apply portion of update endpoint
It can still be used to check for updates but will not apply them. For now also remove the update check loop in VS Code since it's currently unused (update check is hardcoded off right now) and won't work anyway since it also applies the update which now won't work. In the future we should integrate the check into the browser update service.
1 parent e8f6d30 commit 554b6d6

File tree

4 files changed

+23
-365
lines changed

4 files changed

+23
-365
lines changed

ci/dev/vscode.patch

+2-58
Original file line numberDiff line numberDiff line change
@@ -679,18 +679,17 @@ index eab8591492..26668701f7 100644
679679
options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`);
680680
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
681681
new file mode 100644
682-
index 0000000000..649cf32f0a
682+
index 0000000000..8fb2a87303
683683
--- /dev/null
684684
+++ b/src/vs/server/browser/client.ts
685-
@@ -0,0 +1,264 @@
685+
@@ -0,0 +1,208 @@
686686
+import { Emitter } from 'vs/base/common/event';
687687
+import { URI } from 'vs/base/common/uri';
688688
+import { localize } from 'vs/nls';
689689
+import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
690690
+import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
691691
+import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
692692
+import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
693-
+import { ILogService } from 'vs/platform/log/common/log';
694693
+import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
695694
+import { Registry } from 'vs/platform/registry/common/platform';
696695
+import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
@@ -852,61 +851,6 @@ index 0000000000..649cf32f0a
852851
+ });
853852
+ }
854853
+
855-
+ const applyUpdate = async (): Promise<void> => {
856-
+ (services.get(ILogService) as ILogService).debug("Applying update...");
857-
+
858-
+ const response = await fetch(normalize(`${options.base}/update/apply`), {
859-
+ headers: { "content-type": "application/json" },
860-
+ });
861-
+ const json = await response.json();
862-
+ if (response.status !== 200 || json.error) {
863-
+ throw new Error(json.error || response.statusText);
864-
+ }
865-
+ (services.get(INotificationService) as INotificationService).info(`Updated to ${json.version}`);
866-
+ };
867-
+
868-
+ const getUpdate = async (): Promise<void> => {
869-
+ (services.get(ILogService) as ILogService).debug("Checking for update...");
870-
+
871-
+ const response = await fetch(normalize(`${options.base}/update`), {
872-
+ headers: { "content-type": "application/json" },
873-
+ });
874-
+ const json = await response.json();
875-
+ if (response.status !== 200 || json.error) {
876-
+ throw new Error(json.error || response.statusText);
877-
+ }
878-
+ if (json.isLatest) {
879-
+ return;
880-
+ }
881-
+
882-
+ (services.get(INotificationService) as INotificationService).notify({
883-
+ severity: Severity.Info,
884-
+ message: `code-server has an update: ${json.version}`,
885-
+ actions: {
886-
+ primary: [{
887-
+ id: 'update',
888-
+ label: 'Apply Update',
889-
+ tooltip: '',
890-
+ class: undefined,
891-
+ enabled: true,
892-
+ checked: true,
893-
+ dispose: () => undefined,
894-
+ run: applyUpdate,
895-
+ }],
896-
+ }
897-
+ });
898-
+ };
899-
+
900-
+ const updateLoop = (): void => {
901-
+ getUpdate().catch((error) => {
902-
+ (services.get(ILogService) as ILogService).warn(error);
903-
+ }).finally(() => {
904-
+ setTimeout(updateLoop, 300000);
905-
+ });
906-
+ };
907-
+
908-
+ updateLoop();
909-
+
910854
+ // This will be used to set the background color while VS Code loads.
911855
+ const theme = (services.get(IStorageService) as IStorageService).get("colorThemeData", StorageScope.GLOBAL);
912856
+ if (theme) {

src/browser/pages/update.html

-43
This file was deleted.

src/node/app/update.ts

+11-184
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
import { field, logger } from "@coder/logger"
2-
import * as cp from "child_process"
3-
import * as fs from "fs-extra"
42
import * as http from "http"
53
import * as https from "https"
6-
import * as os from "os"
74
import * as path from "path"
85
import * as semver from "semver"
9-
import { Readable, Writable } from "stream"
10-
import * as tar from "tar-fs"
116
import * as url from "url"
12-
import * as util from "util"
13-
import * as zlib from "zlib"
147
import { HttpCode, HttpError } from "../../common/http"
158
import { HttpProvider, HttpProviderOptions, HttpResponse, Route } from "../http"
169
import { settings as globalSettings, SettingsProvider, UpdateSettings } from "../settings"
17-
import { tmpdir } from "../util"
18-
import { ipcMain } from "../wrapper"
1910

2011
export interface Update {
2112
checked: number
@@ -27,7 +18,7 @@ export interface LatestResponse {
2718
}
2819

2920
/**
30-
* Update HTTP provider.
21+
* HTTP provider for checking updates (does not download/install them).
3122
*/
3223
export class UpdateHttpProvider extends HttpProvider {
3324
private update?: Promise<Update>
@@ -41,12 +32,6 @@ export class UpdateHttpProvider extends HttpProvider {
4132
* that fulfills `LatestResponse`.
4233
*/
4334
private readonly latestUrl = "https://api.github.com/repos/cdr/code-server/releases/latest",
44-
/**
45-
* The URL for downloading a version of code-server. {{VERSION}} and
46-
* {{RELEASE_NAME}} will be replaced (for example 2.1.0 and
47-
* code-server-2.1.0-linux-x86_64.tar.gz).
48-
*/
49-
private readonly downloadUrl = "https://github.com/cdr/code-server/releases/download/{{VERSION}}/{{RELEASE_NAME}}",
5035
/**
5136
* Update information will be stored here. If not provided, the global
5237
* settings will be used.
@@ -64,66 +49,30 @@ export class UpdateHttpProvider extends HttpProvider {
6449
throw new HttpError("Not found", HttpCode.NotFound)
6550
}
6651

67-
switch (route.base) {
68-
case "/check":
69-
this.getUpdate(true)
70-
if (route.query && route.query.to) {
71-
return {
72-
redirect: Array.isArray(route.query.to) ? route.query.to[0] : route.query.to,
73-
query: { to: undefined },
74-
}
75-
}
76-
return this.getRoot(route, request)
77-
case "/apply":
78-
return this.tryUpdate(route, request)
79-
case "/":
80-
return this.getRoot(route, request)
52+
if (!this.enabled) {
53+
throw new Error("update checks are disabled")
8154
}
8255

83-
throw new HttpError("Not found", HttpCode.NotFound)
84-
}
85-
86-
public async getRoot(
87-
route: Route,
88-
request: http.IncomingMessage,
89-
errorOrUpdate?: Update | Error,
90-
): Promise<HttpResponse> {
91-
if (request.headers["content-type"] === "application/json") {
92-
if (!this.enabled) {
56+
switch (route.base) {
57+
case "/check":
58+
case "/": {
59+
const update = await this.getUpdate(route.base === "/check")
9360
return {
9461
content: {
95-
isLatest: true,
62+
...update,
63+
isLatest: this.isLatestVersion(update),
9664
},
9765
}
9866
}
99-
const update = await this.getUpdate()
100-
return {
101-
content: {
102-
...update,
103-
isLatest: this.isLatestVersion(update),
104-
},
105-
}
10667
}
107-
const response = await this.getUtf8Resource(this.rootPath, "src/browser/pages/update.html")
108-
response.content = response.content
109-
.replace(
110-
/{{UPDATE_STATUS}}/,
111-
errorOrUpdate && !(errorOrUpdate instanceof Error)
112-
? `Updated to ${errorOrUpdate.version}`
113-
: await this.getUpdateHtml(),
114-
)
115-
.replace(/{{ERROR}}/, errorOrUpdate instanceof Error ? `<div class="error">${errorOrUpdate.message}</div>` : "")
116-
return this.replaceTemplates(route, response)
68+
69+
throw new HttpError("Not found", HttpCode.NotFound)
11770
}
11871

11972
/**
12073
* Query for and return the latest update.
12174
*/
12275
public async getUpdate(force?: boolean): Promise<Update> {
123-
if (!this.enabled) {
124-
throw new Error("updates are not enabled")
125-
}
126-
12776
// Don't run multiple requests at a time.
12877
if (!this.update) {
12978
this.update = this._getUpdate(force)
@@ -171,128 +120,6 @@ export class UpdateHttpProvider extends HttpProvider {
171120
}
172121
}
173122

174-
private async getUpdateHtml(): Promise<string> {
175-
if (!this.enabled) {
176-
return "Updates are disabled"
177-
}
178-
179-
const update = await this.getUpdate()
180-
if (this.isLatestVersion(update)) {
181-
return "No update available"
182-
}
183-
184-
return `<button type="submit" class="apply -button">Update to ${update.version}</button>`
185-
}
186-
187-
public async tryUpdate(route: Route, request: http.IncomingMessage): Promise<HttpResponse> {
188-
try {
189-
const update = await this.getUpdate()
190-
if (!this.isLatestVersion(update)) {
191-
await this.downloadAndApplyUpdate(update)
192-
return this.getRoot(route, request, update)
193-
}
194-
return this.getRoot(route, request)
195-
} catch (error) {
196-
// For JSON requests propagate the error. Otherwise catch it so we can
197-
// show the error inline with the update button instead of an error page.
198-
if (request.headers["content-type"] === "application/json") {
199-
throw error
200-
}
201-
return this.getRoot(route, error)
202-
}
203-
}
204-
205-
public async downloadAndApplyUpdate(update: Update, targetPath?: string): Promise<void> {
206-
const releaseName = await this.getReleaseName(update)
207-
const url = this.downloadUrl.replace("{{VERSION}}", update.version).replace("{{RELEASE_NAME}}", releaseName)
208-
209-
let downloadPath = path.join(tmpdir, "updates", releaseName)
210-
fs.mkdirp(path.dirname(downloadPath))
211-
212-
const response = await this.requestResponse(url)
213-
214-
try {
215-
downloadPath = await this.extractTar(response, downloadPath)
216-
logger.debug("Downloaded update", field("path", downloadPath))
217-
218-
// The archive should have a directory inside at the top level with the
219-
// same name as the archive.
220-
const directoryPath = path.join(downloadPath, path.basename(downloadPath))
221-
await fs.stat(directoryPath)
222-
223-
if (!targetPath) {
224-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
225-
targetPath = path.resolve(__dirname, "../../../")
226-
}
227-
228-
// Move the old directory to prevent potential data loss.
229-
const backupPath = path.resolve(targetPath, `../${path.basename(targetPath)}.${Date.now().toString()}`)
230-
logger.debug("Replacing files", field("target", targetPath), field("backup", backupPath))
231-
await fs.move(targetPath, backupPath)
232-
233-
// Move the new directory.
234-
await fs.move(directoryPath, targetPath)
235-
236-
await fs.remove(downloadPath)
237-
238-
if (process.send) {
239-
ipcMain().relaunch(update.version)
240-
}
241-
} catch (error) {
242-
response.destroy(error)
243-
throw error
244-
}
245-
}
246-
247-
private async extractTar(response: Readable, downloadPath: string): Promise<string> {
248-
downloadPath = downloadPath.replace(/\.tar\.gz$/, "")
249-
logger.debug("Extracting tar", field("path", downloadPath))
250-
251-
response.pause()
252-
await fs.remove(downloadPath)
253-
254-
const decompress = zlib.createGunzip()
255-
response.pipe(decompress as Writable)
256-
response.on("error", (error) => decompress.destroy(error))
257-
response.on("close", () => decompress.end())
258-
259-
const destination = tar.extract(downloadPath)
260-
decompress.pipe(destination)
261-
decompress.on("error", (error) => destination.destroy(error))
262-
decompress.on("close", () => destination.end())
263-
264-
await new Promise((resolve, reject) => {
265-
destination.on("finish", resolve)
266-
destination.on("error", reject)
267-
response.resume()
268-
})
269-
270-
return downloadPath
271-
}
272-
273-
/**
274-
* Given an update return the name for the packaged archived.
275-
*/
276-
public async getReleaseName(update: Update): Promise<string> {
277-
let target: string = os.platform()
278-
if (target === "linux") {
279-
const result = await util
280-
.promisify(cp.exec)("ldd --version")
281-
.catch((error) => ({
282-
stderr: error.message,
283-
stdout: "",
284-
}))
285-
if (/musl/.test(result.stderr) || /musl/.test(result.stdout)) {
286-
target = "alpine"
287-
}
288-
}
289-
let arch = os.arch()
290-
if (arch === "x64") {
291-
arch = "x86_64"
292-
}
293-
return `code-server-${update.version}-${target}-${arch}.tar.gz`
294-
}
295-
296123
private async request(uri: string): Promise<Buffer> {
297124
const response = await this.requestResponse(uri)
298125
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)