aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/net.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/net.ts')
-rw-r--r--editors/code/src/net.ts54
1 files changed, 33 insertions, 21 deletions
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 492213937..681eaa9c9 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -1,8 +1,14 @@
1import fetch from "node-fetch"; 1// Replace with `import fetch from "node-fetch"` once this is fixed in rollup:
2// https://github.com/rollup/plugins/issues/491
3const fetch = require("node-fetch") as typeof import("node-fetch")["default"];
4
2import * as vscode from "vscode"; 5import * as vscode from "vscode";
3import * as fs from "fs";
4import * as stream from "stream"; 6import * as stream from "stream";
7import * as crypto from "crypto";
8import * as fs from "fs";
9import * as zlib from "zlib";
5import * as util from "util"; 10import * as util from "util";
11import * as path from "path";
6import { log, assert } from "./util"; 12import { log, assert } from "./util";
7 13
8const pipeline = util.promisify(stream.pipeline); 14const pipeline = util.promisify(stream.pipeline);
@@ -58,22 +64,30 @@ export interface GithubRelease {
58 }>; 64 }>;
59} 65}
60 66
67interface DownloadOpts {
68 progressTitle: string;
69 url: string;
70 dest: string;
71 mode?: number;
72 gunzip?: boolean;
73}
74
75export async function download(opts: DownloadOpts) {
76 // Put artifact into a temporary file (in the same dir for simplicity)
77 // to prevent partially downloaded files when user kills vscode
78 const dest = path.parse(opts.dest);
79 const randomHex = crypto.randomBytes(5).toString("hex");
80 const tempFile = path.join(dest.dir, `${dest.name}${randomHex}`);
61 81
62export async function download(
63 downloadUrl: string,
64 destinationPath: string,
65 progressTitle: string,
66 { mode }: { mode?: number } = {},
67) {
68 await vscode.window.withProgress( 82 await vscode.window.withProgress(
69 { 83 {
70 location: vscode.ProgressLocation.Notification, 84 location: vscode.ProgressLocation.Notification,
71 cancellable: false, 85 cancellable: false,
72 title: progressTitle 86 title: opts.progressTitle
73 }, 87 },
74 async (progress, _cancellationToken) => { 88 async (progress, _cancellationToken) => {
75 let lastPercentage = 0; 89 let lastPercentage = 0;
76 await downloadFile(downloadUrl, destinationPath, mode, (readBytes, totalBytes) => { 90 await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => {
77 const newPercentage = (readBytes / totalBytes) * 100; 91 const newPercentage = (readBytes / totalBytes) * 100;
78 progress.report({ 92 progress.report({
79 message: newPercentage.toFixed(0) + "%", 93 message: newPercentage.toFixed(0) + "%",
@@ -84,18 +98,15 @@ export async function download(
84 }); 98 });
85 } 99 }
86 ); 100 );
101
102 await fs.promises.rename(tempFile, opts.dest);
87} 103}
88 104
89/**
90 * Downloads file from `url` and stores it at `destFilePath` with `destFilePermissions`.
91 * `onProgress` callback is called on recieveing each chunk of bytes
92 * to track the progress of downloading, it gets the already read and total
93 * amount of bytes to read as its parameters.
94 */
95async function downloadFile( 105async function downloadFile(
96 url: string, 106 url: string,
97 destFilePath: fs.PathLike, 107 destFilePath: fs.PathLike,
98 mode: number | undefined, 108 mode: number | undefined,
109 gunzip: boolean,
99 onProgress: (readBytes: number, totalBytes: number) => void 110 onProgress: (readBytes: number, totalBytes: number) => void
100): Promise<void> { 111): Promise<void> {
101 const res = await fetch(url); 112 const res = await fetch(url);
@@ -119,13 +130,14 @@ async function downloadFile(
119 }); 130 });
120 131
121 const destFileStream = fs.createWriteStream(destFilePath, { mode }); 132 const destFileStream = fs.createWriteStream(destFilePath, { mode });
133 const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body;
122 134
123 await pipeline(res.body, destFileStream); 135 await pipeline(srcStream, destFileStream);
124 return new Promise<void>(resolve => { 136
137 await new Promise<void>(resolve => {
125 destFileStream.on("close", resolve); 138 destFileStream.on("close", resolve);
126 destFileStream.destroy(); 139 destFileStream.destroy();
127 140 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
128 // Details on workaround: https://github.com/rust-analyzer/rust-analyzer/pull/3092#discussion_r378191131 141 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
129 // Issue at nodejs repo: https://github.com/nodejs/node/issues/31776
130 }); 142 });
131} 143}