aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/net.ts
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-24 23:09:28 +0100
committerGitHub <[email protected]>2020-06-24 23:09:28 +0100
commit104fad65daaa6ab103ba8815244afa8243421594 (patch)
tree1318ecab53002e3bc0d61de25665ce3a146bde1f /editors/code/src/net.ts
parentbff7307b8c4cc8577ea2a58b8438afc70aab2bea (diff)
parentc1d39571c9ac27c80ca2c1feb5dd53fc6f325b34 (diff)
Merge #5025
5025: Don't mess with messy temp dir and just download into extension dir r=matklad a=Veetaha 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. Fixes #5019 Co-authored-by: Veetaha <[email protected]>
Diffstat (limited to 'editors/code/src/net.ts')
-rw-r--r--editors/code/src/net.ts84
1 files changed, 27 insertions, 57 deletions
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index e02fd6d4f..866092882 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,10 +1,10 @@
1import fetch from "node-fetch"; 1import 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 crypto from "crypto";
4import * as fs from "fs"; 5import * as fs from "fs";
5import * as os from "os";
6import * as path from "path";
7import * as util from "util"; 6import * as util from "util";
7import * as path from "path";
8import { log, assert } from "./util"; 8import { log, assert } from "./util";
9 9
10const pipeline = util.promisify(stream.pipeline); 10const pipeline = util.promisify(stream.pipeline);
@@ -68,32 +68,33 @@ interface DownloadOpts {
68} 68}
69 69
70export async function download(opts: DownloadOpts) { 70export async function download(opts: DownloadOpts) {
71 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode 71 // Put artifact into a temporary file (in the same dir for simplicity)
72 await withTempDir(async tempDir => { 72 // to prevent partially downloaded files when user kills vscode
73 const tempFile = path.join(tempDir, path.basename(opts.dest)); 73 const dest = path.parse(opts.dest);
74 74 const randomHex = crypto.randomBytes(5).toString("hex");
75 await vscode.window.withProgress( 75 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`);
76 { 76
77 location: vscode.ProgressLocation.Notification, 77 await vscode.window.withProgress(
78 cancellable: false, 78 {
79 title: opts.progressTitle 79 location: vscode.ProgressLocation.Notification,
80 }, 80 cancellable: false,
81 async (progress, _cancellationToken) => { 81 title: opts.progressTitle
82 let lastPercentage = 0; 82 },
83 await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { 83 async (progress, _cancellationToken) => {
84 const newPercentage = (readBytes / totalBytes) * 100; 84 let lastPercentage = 0;
85 progress.report({ 85 await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => {
86 message: newPercentage.toFixed(0) + "%", 86 const newPercentage = (readBytes / totalBytes) * 100;
87 increment: newPercentage - lastPercentage 87 progress.report({
88 }); 88 message: newPercentage.toFixed(0) + "%",
89 89 increment: newPercentage - lastPercentage
90 lastPercentage = newPercentage;
91 }); 90 });
92 }
93 );
94 91
95 await moveFile(tempFile, opts.dest); 92 lastPercentage = newPercentage;
96 }); 93 });
94 }
95 );
96
97 await fs.promises.rename(tempFile, opts.dest);
97} 98}
98 99
99/** 100/**
@@ -137,34 +138,3 @@ async function downloadFile(
137 // https://github.com/rust-analyzer/rust-analyzer/issues/3167 138 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
138 }); 139 });
139} 140}
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}