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.ts77
1 files changed, 41 insertions, 36 deletions
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index 0e7dd29c2..e02fd6d4f 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -60,32 +60,40 @@ export interface GithubRelease {
60 }>; 60 }>;
61} 61}
62 62
63interface DownloadOpts {
64 progressTitle: string;
65 url: string;
66 dest: string;
67 mode?: number;
68}
63 69
64export async function download( 70export async function download(opts: DownloadOpts) {
65 downloadUrl: string, 71 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode
66 destinationPath: string, 72 await withTempDir(async tempDir => {
67 progressTitle: string, 73 const tempFile = path.join(tempDir, path.basename(opts.dest));
68 { mode }: { mode?: number } = {}, 74
69) { 75 await vscode.window.withProgress(
70 await vscode.window.withProgress( 76 {
71 { 77 location: vscode.ProgressLocation.Notification,
72 location: vscode.ProgressLocation.Notification, 78 cancellable: false,
73 cancellable: false, 79 title: opts.progressTitle
74 title: progressTitle 80 },
75 }, 81 async (progress, _cancellationToken) => {
76 async (progress, _cancellationToken) => { 82 let lastPercentage = 0;
77 let lastPercentage = 0; 83 await downloadFile(opts.url, tempFile, opts.mode, (readBytes, totalBytes) => {
78 await downloadFile(downloadUrl, destinationPath, mode, (readBytes, totalBytes) => { 84 const newPercentage = (readBytes / totalBytes) * 100;
79 const newPercentage = (readBytes / totalBytes) * 100; 85 progress.report({
80 progress.report({ 86 message: newPercentage.toFixed(0) + "%",
81 message: newPercentage.toFixed(0) + "%", 87 increment: newPercentage - lastPercentage
82 increment: newPercentage - lastPercentage 88 });
89
90 lastPercentage = newPercentage;
83 }); 91 });
92 }
93 );
84 94
85 lastPercentage = newPercentage; 95 await moveFile(tempFile, opts.dest);
86 }); 96 });
87 }
88 );
89} 97}
90 98
91/** 99/**
@@ -120,21 +128,17 @@ async function downloadFile(
120 onProgress(readBytes, totalBytes); 128 onProgress(readBytes, totalBytes);
121 }); 129 });
122 130
123 // Put the artifact into a temporary folder to prevent partially downloaded files when user kills vscode 131 const destFileStream = fs.createWriteStream(destFilePath, { mode });
124 await withTempFile(async tempFilePath => { 132 await pipeline(res.body, destFileStream);
125 const destFileStream = fs.createWriteStream(tempFilePath, { mode }); 133 await new Promise<void>(resolve => {
126 await pipeline(res.body, destFileStream); 134 destFileStream.on("close", resolve);
127 await new Promise<void>(resolve => { 135 destFileStream.destroy();
128 destFileStream.on("close", resolve); 136 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
129 destFileStream.destroy(); 137 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
130 // This workaround is awaiting to be removed when vscode moves to newer nodejs version:
131 // https://github.com/rust-analyzer/rust-analyzer/issues/3167
132 });
133 await moveFile(tempFilePath, destFilePath);
134 }); 138 });
135} 139}
136 140
137async function withTempFile(scope: (tempFilePath: string) => Promise<void>) { 141async function withTempDir(scope: (tempDirPath: string) => Promise<void>) {
138 // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/ 142 // Based on the great article: https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies/
139 143
140 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks 144 // `.realpath()` should handle the cases where os.tmpdir() contains symlinks
@@ -143,7 +147,7 @@ async function withTempFile(scope: (tempFilePath: string) => Promise<void>) {
143 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer")); 147 const tempDir = await fs.promises.mkdtemp(path.join(osTempDir, "rust-analyzer"));
144 148
145 try { 149 try {
146 return await scope(path.join(tempDir, "file")); 150 return await scope(tempDir);
147 } finally { 151 } finally {
148 // We are good citizens :D 152 // We are good citizens :D
149 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error); 153 void fs.promises.rmdir(tempDir, { recursive: true }).catch(log.error);
@@ -160,6 +164,7 @@ async function moveFile(src: fs.PathLike, dest: fs.PathLike) {
160 await fs.promises.unlink(src); 164 await fs.promises.unlink(src);
161 } else { 165 } else {
162 log.error(`Failed to rename the file ${src} -> ${dest}`, err); 166 log.error(`Failed to rename the file ${src} -> ${dest}`, err);
167 throw err;
163 } 168 }
164 } 169 }
165} 170}