|
1 | 1 | import fetch from "node-fetch";
|
2 | 2 | import * as vscode from "vscode";
|
3 | 3 | import * as stream from "stream";
|
| 4 | +import * as crypto from "crypto"; |
4 | 5 | import * as fs from "fs";
|
5 |
| -import * as os from "os"; |
6 |
| -import * as path from "path"; |
7 | 6 | import * as util from "util";
|
| 7 | +import * as path from "path"; |
8 | 8 | import { log, assert } from "./util";
|
9 | 9 |
|
10 | 10 | const pipeline = util.promisify(stream.pipeline);
|
@@ -68,32 +68,33 @@ interface DownloadOpts {
|
68 | 68 | }
|
69 | 69 |
|
70 | 70 | export async function download(opts: DownloadOpts) {
|
71 |
| - // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode |
72 |
| - await withTempDir(async tempDir => { |
73 |
| - const tempFile = path.join(tempDir, path.basename(opts.dest)); |
74 |
| - |
75 |
| - await vscode.window.withProgress( |
76 |
| - { |
77 |
| - location: vscode.ProgressLocation.Notification, |
78 |
| - cancellable: false, |
79 |
| - title: opts.progressTitle |
80 |
| - }, |
81 |
| - async (progress, _cancellationToken) => { |
82 |
| - let lastPercentage = 0; |
83 |
| - await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { |
84 |
| - const newPercentage = (readBytes / totalBytes) * 100; |
85 |
| - progress.report({ |
86 |
| - message: newPercentage.toFixed(0) + "%", |
87 |
| - increment: newPercentage - lastPercentage |
88 |
| - }); |
89 |
| - |
90 |
| - lastPercentage = newPercentage; |
| 71 | + // Put artifact into a temporary file (in the same dir for simplicity) |
| 72 | + // to prevent partially downloaded files when user kills vscode |
| 73 | + const dest = path.parse(opts.dest); |
| 74 | + const randomHex = crypto.randomBytes(5).toString("hex"); |
| 75 | + const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`); |
| 76 | + |
| 77 | + await vscode.window.withProgress( |
| 78 | + { |
| 79 | + location: vscode.ProgressLocation.Notification, |
| 80 | + cancellable: false, |
| 81 | + title: opts.progressTitle |
| 82 | + }, |
| 83 | + async (progress, _cancellationToken) => { |
| 84 | + let lastPercentage = 0; |
| 85 | + await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => { |
| 86 | + const newPercentage = (readBytes / totalBytes) * 100; |
| 87 | + progress.report({ |
| 88 | + message: newPercentage.toFixed(0) + "%", |
| 89 | + increment: newPercentage - lastPercentage |
91 | 90 | });
|
92 |
| - } |
93 |
| - ); |
94 | 91 |
|
95 |
| - await moveFile(tempFile, opts.dest); |
96 |
| - }); |
| 92 | + lastPercentage = newPercentage; |
| 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 | 138 | // https://github.com/rust-analyzer/rust-analyzer/issues/3167
|
138 | 139 | });
|
139 | 140 | }
|
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 |
| -} |
0 commit comments