aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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}