aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVeetaha <[email protected]>2020-06-24 11:19:14 +0100
committerVeetaha <[email protected]>2020-06-24 11:19:14 +0100
commit62ebaa822b0770558ffc3b8fba291996e2c4a5b0 (patch)
treee519095d84ea717801c9ae23ab5ff5c24a1c8ec9
parent5d84e27ba961e03c3f394b9a6ef2a035dcc183be (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
-rw-r--r--editors/code/src/net.ts80
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";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import * as stream from "stream"; 3import * as stream from "stream";
4import * as fs from "fs"; 4import * as fs from "fs";
5import * as os from "os";
6import * as path from "path";
7import * as util from "util"; 5import * as util from "util";
8import { log, assert } from "./util"; 6import { log, assert } from "./util";
9 7
@@ -68,32 +66,31 @@ interface DownloadOpts {
68} 66}
69 67
70export async function download(opts: DownloadOpts) { 68export 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
141async 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
157async 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}