diff options
Diffstat (limited to 'editors/code/src/net.ts')
-rw-r--r-- | editors/code/src/net.ts | 54 |
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 @@ | |||
1 | import 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 | ||
3 | const fetch = require("node-fetch") as typeof import("node-fetch")["default"]; | ||
4 | |||
2 | import * as vscode from "vscode"; | 5 | import * as vscode from "vscode"; |
3 | import * as fs from "fs"; | ||
4 | import * as stream from "stream"; | 6 | import * as stream from "stream"; |
7 | import * as crypto from "crypto"; | ||
8 | import * as fs from "fs"; | ||
9 | import * as zlib from "zlib"; | ||
5 | import * as util from "util"; | 10 | import * as util from "util"; |
11 | import * as path from "path"; | ||
6 | import { log, assert } from "./util"; | 12 | import { log, assert } from "./util"; |
7 | 13 | ||
8 | const pipeline = util.promisify(stream.pipeline); | 14 | const pipeline = util.promisify(stream.pipeline); |
@@ -58,22 +64,30 @@ export interface GithubRelease { | |||
58 | }>; | 64 | }>; |
59 | } | 65 | } |
60 | 66 | ||
67 | interface DownloadOpts { | ||
68 | progressTitle: string; | ||
69 | url: string; | ||
70 | dest: string; | ||
71 | mode?: number; | ||
72 | gunzip?: boolean; | ||
73 | } | ||
74 | |||
75 | export 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 | ||
62 | export 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 | */ | ||
95 | async function downloadFile( | 105 | async 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 | } |