aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-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}