1- import { exec as execCallback } from 'node:child_process' ;
1+ import { execFile as execFileCallback } from 'node:child_process' ;
22import fs from 'node:fs/promises' ;
3+ import { setTimeout } from 'node:timers/promises' ;
34import { promisify } from 'node:util' ;
45import { getBin , getPlatform } from '../include.ts' ;
56import { exists , rm , rmdir } from './files.ts' ;
67import { getLogger } from './logger.ts' ;
78
8- const exec = promisify ( execCallback ) ;
9+ const execFile = promisify ( execFileCallback ) ;
910const logger = getLogger ( 'addon-tools' ) ;
11+ const ONE_SECOND_MS = 1000 ;
12+ const RETRY_DELAYS_MS = [ ONE_SECOND_MS * 2 , ONE_SECOND_MS * 5 ] as const ;
1013
11- const download = async ( url : string ) => {
12- const { stderr } = await exec (
13- [ 'curl -fsSL -o' , `${ getBin ( ) } /${ getPlatform ( ) } .gz` , url ] . join ( ' ' ) ,
14- ) ;
14+ const download = async ( url : string , gzPath : string ) => {
15+ const { stderr } = await execFile ( 'curl' , [ '-fsSL' , '-o' , gzPath , url ] ) ;
1516 if ( stderr ) {
1617 logger . warn ( stderr ) ;
1718 }
1819} ;
1920
2021const unpack = async ( gzPath : string , binPath : string ) => {
21- const { stderr } = await exec ( ` tar -xzf ${ gzPath } --directory ${ binPath } ` ) ;
22+ const { stderr } = await execFile ( ' tar' , [ ' -xzf' , gzPath , ' --directory' , binPath ] ) ;
2223 if ( stderr ) {
2324 logger . warn ( stderr ) ;
2425 }
2526} ;
2627
28+ const waitBeforeRetry = async ( attempt : number ) : Promise < void > => {
29+ const delayMs = RETRY_DELAYS_MS [ attempt - 1 ] ?? RETRY_DELAYS_MS . at ( - 1 ) ;
30+ if ( delayMs ) {
31+ await setTimeout ( delayMs ) ;
32+ }
33+ } ;
34+
35+ const tryInstall = async ( urlPath : string , gzPath : string , binPath : string ) : Promise < boolean > => {
36+ await download ( urlPath , gzPath ) ;
37+
38+ if ( ! ( await exists ( gzPath ) ) ) {
39+ throw new Error ( `Could not download "${ urlPath } " to "${ gzPath } "` ) ;
40+ }
41+
42+ await unpack ( gzPath , binPath ) ;
43+ return true ;
44+ } ;
45+
2746export const install = async ( folderUrl : string ) : Promise < boolean > => {
2847 const binPath = getBin ( ) ;
2948 const urlPath = `${ folderUrl } /${ getPlatform ( ) } .gz` ;
@@ -32,20 +51,27 @@ export const install = async (folderUrl: string): Promise<boolean> => {
3251 await rmdir ( binPath ) ;
3352 await fs . mkdir ( binPath , { recursive : true } ) ;
3453
35- try {
36- await download ( urlPath ) ;
54+ // oxlint-disable no-await-in-loop
55+ for ( let attempt = 1 ; attempt <= RETRY_DELAYS_MS . length + 1 ; attempt ++ ) {
56+ try {
57+ const isInstalled = await tryInstall ( urlPath , gzPath , binPath ) ;
58+ await rm ( gzPath ) . catch ( ( rmError : unknown ) => logger . warn ( rmError ) ) ;
59+ return isInstalled ;
60+ } catch ( error ) {
61+ logger . warn ( error ) ;
62+ await rm ( gzPath ) . catch ( ( rmError : unknown ) => logger . warn ( rmError ) ) ;
3763
38- if ( ! ( await exists ( gzPath ) ) ) {
39- logger . warn ( `Could not download "${ urlPath } " to "${ gzPath } "` ) ;
40- return false ;
41- }
64+ if ( attempt > RETRY_DELAYS_MS . length ) {
65+ return false ;
66+ }
4267
43- await unpack ( gzPath , binPath ) ;
44- } catch ( error ) {
45- logger . warn ( error ) ;
46- return false ;
47- } finally {
48- await rm ( gzPath ) . catch ( ( error : unknown ) => logger . warn ( error ) ) ;
68+ logger . warn (
69+ `Retrying install from "${ urlPath } " (${ attempt } /${ RETRY_DELAYS_MS . length } )` ,
70+ ) ;
71+ await waitBeforeRetry ( attempt ) ;
72+ }
4973 }
50- return true ;
74+ // oxlint-enable no-await-in-loop
75+
76+ return false ;
5177} ;
0 commit comments