diff options
author | Veetaha <[email protected]> | 2020-06-24 11:19:14 +0100 |
---|---|---|
committer | Veetaha <[email protected]> | 2020-06-24 11:19:14 +0100 |
commit | 62ebaa822b0770558ffc3b8fba291996e2c4a5b0 (patch) | |
tree | e519095d84ea717801c9ae23ab5ff5c24a1c8ec9 /editors | |
parent | 5d84e27ba961e03c3f394b9a6ef2a035dcc183be (diff) |
Don't mess with messy temp dir and just download into extension dir
Temp dirs are messy. Dealing with them requires handling quite a bunch of
edge cases. As proposed by lnicola this seems better to just put the temp files
in the extension dir and not care much about suddenly leaving garbage.
Instead we get shorter and less platform-caveat-y code.
We will also assume users don't try to issue a download in different vscode windows simultaneously
Diffstat (limited to 'editors')
-rw-r--r-- | editors/code/src/net.ts | 80 |
1 files changed, 23 insertions, 57 deletions
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index e02fd6d4f..7c77530b8 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -2,8 +2,6 @@ import fetch from "node-fetch"; | |||
2 | import * as vscode from "vscode"; | 2 | import * as vscode from "vscode"; |
3 | import * as stream from "stream"; | 3 | import * as stream from "stream"; |
4 | import * as fs from "fs"; | 4 | import * as fs from "fs"; |
5 | import * as os from "os"; | ||
6 | import * as path from "path"; | ||
7 | import * as util from "util"; | 5 | import * as util from "util"; |
8 | import { log, assert } from "./util"; | 6 | import { log, assert } from "./util"; |
9 | 7 | ||
@@ -68,32 +66,31 @@ interface DownloadOpts { | |||
68 | } | 66 | } |
69 | 67 | ||
70 | export async function download(opts: DownloadOpts) { | 68 | export async function download(opts: DownloadOpts) { |
71 | // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode | 69 | // Put artifact into a temporary file (in the same dir for simplicity) |
72 | await withTempDir(async tempDir => { | 70 | // to prevent partially downloaded files when user kills vscode |
73 | const tempFile = path.join(tempDir, path.basename(opts.dest)); | 71 | const tempFile = `${opts.dest}.tmp`; |
74 | 72 | ||
75 | await vscode.window.withProgress( | 73 | await vscode.window.withProgress( |
76 | { | 74 | { |
77 | location: vscode.ProgressLocation.Notification, | 75 | location: vscode.ProgressLocation.Notification, |
78 | cancellable: false, | 76 | cancellable: false, |
79 | title: opts.progressTitle | 77 | title: opts.progressTitle |
80 | }, | 78 | }, |
81 | async (progress, _cancellationToken) => { | 79 | async (progress, _cancellationToken) => { |
82 | let lastPercentage = 0; | 80 | let lastPercentage = 0; |
83 | await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { | 81 | await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { |
84 | const newPercentage = (readBytes / totalBytes) * 100; | 82 | const newPercentage = (readBytes / totalBytes) * 100; |
85 | progress.report({ | 83 | progress.report({ |
86 | message: newPercentage.toFixed(0) + "%", | 84 | message: newPercentage.toFixed(0) + "%", |
87 | increment: newPercentage - lastPercentage | 85 | increment: newPercentage - lastPercentage |
88 | }); | ||
89 | |||
90 | lastPercentage = newPercentage; | ||
91 | }); | 86 | }); |
92 | } | ||
93 | ); | ||
94 | 87 | ||
95 | await moveFile(tempFile, opts.dest); | 88 | lastPercentage = newPercentage; |
96 | }); | 89 | }); |
90 | } | ||
91 | ); | ||
92 | |||
93 | await fs.promises.rename(tempFile, opts.dest); | ||
97 | } | 94 | } |
98 | 95 | ||
99 | /** | 96 | /** |
@@ -137,34 +134,3 @@ async function downloadFile( | |||
137 | // https://github.com/rust-analyzer/rust-analyzer/issues/3167 | 134 | // https://github.com/rust-analyzer/rust-analyzer/issues/3167 |
138 | }); | 135 | }); |
139 | } | 136 | } |
140 | |||
141 | async function withTempDir(scope: (tempDirPath: string) => Promise<void>) { | ||
142 | // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/ | ||
143 | |||
144 | // `.realpath()` should handle the cases where os.tmpdir() contains symlinks | ||
145 | const osTempDir = await fs.promises.realpath(os.tmpdir()); | ||
146 | |||
147 | const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer")); | ||
148 | |||
149 | try { | ||
150 | return await scope(tempDir); | ||
151 | } finally { | ||
152 | // We are good citizens :D | ||
153 | void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error); | ||
154 | } | ||
155 | }; | ||
156 | |||
157 | async function moveFile(src: fs.PathLike, dest: fs.PathLike) { | ||
158 | try { | ||
159 | await fs.promises.rename(src, dest); | ||
160 | } catch (err) { | ||
161 | if (err.code === 'EXDEV') { | ||
162 | // We are probably moving the file across partitions/devices | ||
163 | await fs.promises.copyFile(src, dest); | ||
164 | await fs.promises.unlink(src); | ||
165 | } else { | ||
166 | log.error(`Failed to rename the file ${src} -> ${dest}`, err); | ||
167 | throw err; | ||
168 | } | ||
169 | } | ||
170 | } | ||