/ src / retry-helper.ts
retry-helper.ts
 1  import * as core from '@actions/core'
 2  
 3  const defaultMaxAttempts = 3
 4  const defaultMinSeconds = 10
 5  const defaultMaxSeconds = 20
 6  
 7  export class RetryHelper {
 8    private maxAttempts: number
 9    private minSeconds: number
10    private maxSeconds: number
11  
12    constructor(
13      maxAttempts: number = defaultMaxAttempts,
14      minSeconds: number = defaultMinSeconds,
15      maxSeconds: number = defaultMaxSeconds
16    ) {
17      this.maxAttempts = maxAttempts
18      this.minSeconds = Math.floor(minSeconds)
19      this.maxSeconds = Math.floor(maxSeconds)
20      if (this.minSeconds > this.maxSeconds) {
21        throw new Error('min seconds should be less than or equal to max seconds')
22      }
23    }
24  
25    async execute<T>(action: () => Promise<T>): Promise<T> {
26      let attempt = 1
27      while (attempt < this.maxAttempts) {
28        // Try
29        try {
30          return await action()
31        } catch (err) {
32          core.info(err.message)
33        }
34  
35        // Sleep
36        const seconds = this.getSleepAmount()
37        core.info(`Waiting ${seconds} seconds before trying again`)
38        await this.sleep(seconds)
39        attempt++
40      }
41  
42      // Last attempt
43      return await action()
44    }
45  
46    private getSleepAmount(): number {
47      return (
48        Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) +
49        this.minSeconds
50      )
51    }
52  
53    private async sleep(seconds: number): Promise<void> {
54      return new Promise(resolve => setTimeout(resolve, seconds * 1000))
55    }
56  }
57  
58  export async function execute<T>(action: () => Promise<T>): Promise<T> {
59    const retryHelper = new RetryHelper()
60    return await retryHelper.execute(action)
61  }