@@ -6,6 +6,7 @@ import type {Dir} from 'fs';
6
6
import Module from 'module' ;
7
7
import path from 'path' ;
8
8
import semver from 'semver' ;
9
+ import { setTimeout as setTimeoutPromise } from 'timers/promises' ;
9
10
10
11
import * as engine from './Engine' ;
11
12
import * as debugUtils from './debugUtils' ;
@@ -213,7 +214,11 @@ export async function installVersion(installTarget: string, locator: Locator, {s
213
214
214
215
await fs . promises . mkdir ( path . dirname ( installFolder ) , { recursive : true } ) ;
215
216
try {
216
- await fs . promises . rename ( tmpFolder , installFolder ) ;
217
+ if ( process . platform === `win32` ) {
218
+ await renameUnderWindows ( tmpFolder , installFolder ) ;
219
+ } else {
220
+ await fs . promises . rename ( tmpFolder , installFolder ) ;
221
+ }
217
222
} catch ( err ) {
218
223
if (
219
224
( err as nodeUtils . NodeError ) . code === `ENOTEMPTY` ||
@@ -260,6 +265,30 @@ export async function installVersion(installTarget: string, locator: Locator, {s
260
265
} ;
261
266
}
262
267
268
+ async function renameUnderWindows ( oldPath : fs . PathLike , newPath : fs . PathLike ) {
269
+ // Windows malicious file analysis blocks files currently under analysis, so we need to wait for file release
270
+ const retries = 5 ;
271
+ for ( let i = 0 ; i < retries ; i ++ ) {
272
+ try {
273
+ await fs . promises . rename ( oldPath , newPath ) ;
274
+ break ;
275
+ } catch ( err ) {
276
+ if (
277
+ (
278
+ ( err as nodeUtils . NodeError ) . code === `ENOENT` ||
279
+ ( err as nodeUtils . NodeError ) . code === `EPERM`
280
+ ) &&
281
+ i < ( retries - 1 )
282
+ ) {
283
+ await setTimeoutPromise ( 100 * 2 ** i ) ;
284
+ continue ;
285
+ } else {
286
+ throw err ;
287
+ }
288
+ }
289
+ }
290
+ }
291
+
263
292
/**
264
293
* Loads the binary, taking control of the current process.
265
294
*/
0 commit comments