11import * as assert from 'assert' ;
2+ import * as crypto from 'crypto' ;
23import * as fs from 'fs' ;
34import fetch from 'node-fetch' ;
5+ import * as path from 'path' ;
46import * as stream from 'stream' ;
57import * as util from 'util' ;
68import * as vscode from 'vscode' ;
9+ import * as zlib from 'zlib' ;
710
811const pipeline = util . promisify ( stream . pipeline ) ;
912
@@ -62,24 +65,34 @@ export interface GithubRelease {
6265 } > ;
6366}
6467
65- export async function download (
66- downloadUrl : string ,
67- destinationPath : string ,
68- progressTitle : string ,
69- { mode } : { mode ?: number } = { } ,
70- ) {
68+ interface DownloadOpts {
69+ progressTitle : string ;
70+ url : string ;
71+ dest : string ;
72+ mode ?: number ;
73+ gunzip ?: boolean ;
74+ }
75+
76+ export async function download ( opts : DownloadOpts ) {
77+ // Put artifact into a temporary file (in the same dir for simplicity)
78+ // to prevent partially downloaded files when user kills vscode
79+ const dest = path . parse ( opts . dest ) ;
80+ const randomHex = crypto . randomBytes ( 5 ) . toString ( 'hex' ) ;
81+ const tempFile = path . join ( dest . dir , `${ dest . name } ${ randomHex } ` ) ;
82+
7183 await vscode . window . withProgress (
7284 {
7385 location : vscode . ProgressLocation . Notification ,
7486 cancellable : false ,
75- title : progressTitle ,
87+ title : opts . progressTitle ,
7688 } ,
7789 async ( progress , _cancellationToken ) => {
7890 let lastPercentage = 0 ;
7991 await downloadFile (
80- downloadUrl ,
81- destinationPath ,
82- mode ,
92+ opts . url ,
93+ tempFile ,
94+ opts . mode ,
95+ Boolean ( opts . gunzip ) ,
8396 ( readBytes , totalBytes ) => {
8497 const newPercentage = ( readBytes / totalBytes ) * 100 ;
8598 progress . report ( {
@@ -92,6 +105,8 @@ export async function download(
92105 ) ;
93106 } ,
94107 ) ;
108+
109+ return fs . promises . rename ( tempFile , opts . dest ) ;
95110}
96111
97112/**
@@ -104,6 +119,7 @@ async function downloadFile(
104119 url : string ,
105120 destFilePath : fs . PathLike ,
106121 mode : number | undefined ,
122+ gunzip : boolean ,
107123 onProgress : ( readBytes : number , totalBytes : number ) => void ,
108124) : Promise < void > {
109125 const res = await fetch ( url ) ;
@@ -136,13 +152,13 @@ async function downloadFile(
136152 } ) ;
137153
138154 const destFileStream = fs . createWriteStream ( destFilePath , { mode } ) ;
155+ const srcStream = gunzip ? res . body . pipe ( zlib . createGunzip ( ) ) : res . body ;
139156
140- await pipeline ( res . body , destFileStream ) ;
157+ await pipeline ( srcStream , destFileStream ) ;
141158 return new Promise < void > ( resolve => {
142159 destFileStream . on ( 'close' , resolve ) ;
143160 destFileStream . destroy ( ) ;
144-
145- // Details on workaround: https://github.com/rust-analyzer/rust-analyzer/pull/3092#discussion_r378191131
146- // Issue at nodejs repo: https://github.com/nodejs/node/issues/31776
161+ // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
162+ // https://github.com/rust-analyzer/rust-analyzer/issues/3167
147163 } ) ;
148164}
0 commit comments