mirror of
				https://github.com/Swatinem/rust-cache
				synced 2025-11-04 06:29:10 +00:00 
			
		
		
		
	Merge ccd52e1077 into 76686c56f2
				
					
				
			This commit is contained in:
		
						commit
						9244268a9e
					
				
					 8 changed files with 1184 additions and 1003 deletions
				
			
		| 
						 | 
					@ -14,6 +14,12 @@ inputs:
 | 
				
			||||||
  workspaces:
 | 
					  workspaces:
 | 
				
			||||||
    description: "Paths to multiple Cargo workspaces and their target directories, separated by newlines"
 | 
					    description: "Paths to multiple Cargo workspaces and their target directories, separated by newlines"
 | 
				
			||||||
    required: false
 | 
					    required: false
 | 
				
			||||||
 | 
					  maxRetryAttempts:
 | 
				
			||||||
 | 
					    description: "The amount of attempts to retry the network operations after retriable errors"
 | 
				
			||||||
 | 
					    required: false
 | 
				
			||||||
 | 
					  timeout:
 | 
				
			||||||
 | 
					    description: "The timeout for the networking operations"
 | 
				
			||||||
 | 
					    required: false
 | 
				
			||||||
  cache-on-failure:
 | 
					  cache-on-failure:
 | 
				
			||||||
    description: "Cache even if the build fails. Defaults to false"
 | 
					    description: "Cache even if the build fails. Defaults to false"
 | 
				
			||||||
    required: false
 | 
					    required: false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										49
									
								
								dist/restore/index.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								dist/restore/index.js
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -64390,6 +64390,40 @@ async function getCmdOutput(cmd, args = [], options = {}) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return stdout;
 | 
					    return stdout;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					async function withRetries(operation, maxRetryAttempts, isRetriable) {
 | 
				
			||||||
 | 
					    let attemptsLeft = maxRetryAttempts;
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return await operation();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (e) {
 | 
				
			||||||
 | 
					            attemptsLeft -= 1;
 | 
				
			||||||
 | 
					            if (attemptsLeft <= 0) {
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!isRetriable(e)) {
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            lib_core.info(`[warning] Retrying after an error, ${attemptsLeft} attempts left, error: ${e}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					class TimeoutError extends Error {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					async function withTimeout(operation, timeoutMs) {
 | 
				
			||||||
 | 
					    const timeout = timeoutMs
 | 
				
			||||||
 | 
					        ? new Promise((resolve) => {
 | 
				
			||||||
 | 
					            setTimeout(resolve, timeoutMs);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        : new Promise(() => { });
 | 
				
			||||||
 | 
					    const timeoutSym = Symbol("timeout");
 | 
				
			||||||
 | 
					    const racingTimeout = timeout.then(() => timeoutSym);
 | 
				
			||||||
 | 
					    const result = await Promise.race([racingTimeout, operation(timeout)]);
 | 
				
			||||||
 | 
					    if (result === timeoutSym) {
 | 
				
			||||||
 | 
					        throw new TimeoutError("operation timeout");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;// CONCATENATED MODULE: ./src/workspace.ts
 | 
					;// CONCATENATED MODULE: ./src/workspace.ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64444,6 +64478,10 @@ class CacheConfig {
 | 
				
			||||||
        this.restoreKey = "";
 | 
					        this.restoreKey = "";
 | 
				
			||||||
        /** The workspace configurations */
 | 
					        /** The workspace configurations */
 | 
				
			||||||
        this.workspaces = [];
 | 
					        this.workspaces = [];
 | 
				
			||||||
 | 
					        /** The max timeout for the networking operations */
 | 
				
			||||||
 | 
					        this.timeout = null;
 | 
				
			||||||
 | 
					        /** The max retry attemtps for the networking operations */
 | 
				
			||||||
 | 
					        this.maxRetryAttempts = 0;
 | 
				
			||||||
        /** The prefix portion of the cache key */
 | 
					        /** The prefix portion of the cache key */
 | 
				
			||||||
        this.keyPrefix = "";
 | 
					        this.keyPrefix = "";
 | 
				
			||||||
        /** The rust version considered for the cache key */
 | 
					        /** The rust version considered for the cache key */
 | 
				
			||||||
| 
						 | 
					@ -64549,6 +64587,10 @@ class CacheConfig {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.workspaces = workspaces;
 | 
					        self.workspaces = workspaces;
 | 
				
			||||||
        self.cachePaths = [config_CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
					        self.cachePaths = [config_CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
				
			||||||
 | 
					        const timeoutInput = lib_core.getInput("timeout");
 | 
				
			||||||
 | 
					        self.timeout = timeoutInput ? parseFloat(timeoutInput) : null;
 | 
				
			||||||
 | 
					        const maxRetryAttemptsInput = lib_core.getInput("maxRetryAttempts");
 | 
				
			||||||
 | 
					        self.maxRetryAttempts = maxRetryAttemptsInput ? parseFloat(maxRetryAttemptsInput) : 0;
 | 
				
			||||||
        return self;
 | 
					        return self;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    printInfo() {
 | 
					    printInfo() {
 | 
				
			||||||
| 
						 | 
					@ -64576,6 +64618,10 @@ class CacheConfig {
 | 
				
			||||||
        for (const file of this.keyFiles) {
 | 
					        for (const file of this.keyFiles) {
 | 
				
			||||||
            lib_core.info(`  - ${file}`);
 | 
					            lib_core.info(`  - ${file}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        lib_core.info(`Network operations timeout:`);
 | 
				
			||||||
 | 
					        lib_core.info(`    ${this.timeout}`);
 | 
				
			||||||
 | 
					        lib_core.info(`Max retry attempts for the network operations:`);
 | 
				
			||||||
 | 
					        lib_core.info(`    ${this.maxRetryAttempts}`);
 | 
				
			||||||
        lib_core.endGroup();
 | 
					        lib_core.endGroup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -64810,6 +64856,7 @@ async function exists(path) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on("uncaughtException", (e) => {
 | 
					process.on("uncaughtException", (e) => {
 | 
				
			||||||
    lib_core.info(`[warning] ${e.message}`);
 | 
					    lib_core.info(`[warning] ${e.message}`);
 | 
				
			||||||
    if (e.stack) {
 | 
					    if (e.stack) {
 | 
				
			||||||
| 
						 | 
					@ -64835,7 +64882,7 @@ async function run() {
 | 
				
			||||||
        lib_core.saveState(config_STATE_BINS, JSON.stringify([...bins]));
 | 
					        lib_core.saveState(config_STATE_BINS, JSON.stringify([...bins]));
 | 
				
			||||||
        lib_core.info(`... Restoring cache ...`);
 | 
					        lib_core.info(`... Restoring cache ...`);
 | 
				
			||||||
        const key = config.cacheKey;
 | 
					        const key = config.cacheKey;
 | 
				
			||||||
        const restoreKey = await cache.restoreCache(config.cachePaths, key, [config.restoreKey]);
 | 
					        const restoreKey = await withRetries(() => withTimeout(() => cache.restoreCache(config.cachePaths, key, [config.restoreKey]), config.timeout), config.maxRetryAttempts, () => true);
 | 
				
			||||||
        if (restoreKey) {
 | 
					        if (restoreKey) {
 | 
				
			||||||
            lib_core.info(`Restored from cache key "${restoreKey}".`);
 | 
					            lib_core.info(`Restored from cache key "${restoreKey}".`);
 | 
				
			||||||
            lib_core.saveState(STATE_KEY, restoreKey);
 | 
					            lib_core.saveState(STATE_KEY, restoreKey);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										49
									
								
								dist/save/index.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								dist/save/index.js
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -64390,6 +64390,40 @@ async function getCmdOutput(cmd, args = [], options = {}) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return stdout;
 | 
					    return stdout;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					async function withRetries(operation, maxRetryAttempts, isRetriable) {
 | 
				
			||||||
 | 
					    let attemptsLeft = maxRetryAttempts;
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return await operation();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (e) {
 | 
				
			||||||
 | 
					            attemptsLeft -= 1;
 | 
				
			||||||
 | 
					            if (attemptsLeft <= 0) {
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!isRetriable(e)) {
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            core.info(`[warning] Retrying after an error, ${attemptsLeft} attempts left, error: ${e}`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					class TimeoutError extends Error {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					async function withTimeout(operation, timeoutMs) {
 | 
				
			||||||
 | 
					    const timeout = timeoutMs
 | 
				
			||||||
 | 
					        ? new Promise((resolve) => {
 | 
				
			||||||
 | 
					            setTimeout(resolve, timeoutMs);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        : new Promise(() => { });
 | 
				
			||||||
 | 
					    const timeoutSym = Symbol("timeout");
 | 
				
			||||||
 | 
					    const racingTimeout = timeout.then(() => timeoutSym);
 | 
				
			||||||
 | 
					    const result = await Promise.race([racingTimeout, operation(timeout)]);
 | 
				
			||||||
 | 
					    if (result === timeoutSym) {
 | 
				
			||||||
 | 
					        throw new TimeoutError("operation timeout");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;// CONCATENATED MODULE: ./src/workspace.ts
 | 
					;// CONCATENATED MODULE: ./src/workspace.ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64444,6 +64478,10 @@ class CacheConfig {
 | 
				
			||||||
        this.restoreKey = "";
 | 
					        this.restoreKey = "";
 | 
				
			||||||
        /** The workspace configurations */
 | 
					        /** The workspace configurations */
 | 
				
			||||||
        this.workspaces = [];
 | 
					        this.workspaces = [];
 | 
				
			||||||
 | 
					        /** The max timeout for the networking operations */
 | 
				
			||||||
 | 
					        this.timeout = null;
 | 
				
			||||||
 | 
					        /** The max retry attemtps for the networking operations */
 | 
				
			||||||
 | 
					        this.maxRetryAttempts = 0;
 | 
				
			||||||
        /** The prefix portion of the cache key */
 | 
					        /** The prefix portion of the cache key */
 | 
				
			||||||
        this.keyPrefix = "";
 | 
					        this.keyPrefix = "";
 | 
				
			||||||
        /** The rust version considered for the cache key */
 | 
					        /** The rust version considered for the cache key */
 | 
				
			||||||
| 
						 | 
					@ -64549,6 +64587,10 @@ class CacheConfig {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.workspaces = workspaces;
 | 
					        self.workspaces = workspaces;
 | 
				
			||||||
        self.cachePaths = [CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
					        self.cachePaths = [CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
				
			||||||
 | 
					        const timeoutInput = core.getInput("timeout");
 | 
				
			||||||
 | 
					        self.timeout = timeoutInput ? parseFloat(timeoutInput) : null;
 | 
				
			||||||
 | 
					        const maxRetryAttemptsInput = core.getInput("maxRetryAttempts");
 | 
				
			||||||
 | 
					        self.maxRetryAttempts = maxRetryAttemptsInput ? parseFloat(maxRetryAttemptsInput) : 0;
 | 
				
			||||||
        return self;
 | 
					        return self;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    printInfo() {
 | 
					    printInfo() {
 | 
				
			||||||
| 
						 | 
					@ -64576,6 +64618,10 @@ class CacheConfig {
 | 
				
			||||||
        for (const file of this.keyFiles) {
 | 
					        for (const file of this.keyFiles) {
 | 
				
			||||||
            core.info(`  - ${file}`);
 | 
					            core.info(`  - ${file}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        core.info(`Network operations timeout:`);
 | 
				
			||||||
 | 
					        core.info(`    ${this.timeout}`);
 | 
				
			||||||
 | 
					        core.info(`Max retry attempts for the network operations:`);
 | 
				
			||||||
 | 
					        core.info(`    ${this.maxRetryAttempts}`);
 | 
				
			||||||
        core.endGroup();
 | 
					        core.endGroup();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -64811,6 +64857,7 @@ async function exists(path) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on("uncaughtException", (e) => {
 | 
					process.on("uncaughtException", (e) => {
 | 
				
			||||||
    core.info(`[warning] ${e.message}`);
 | 
					    core.info(`[warning] ${e.message}`);
 | 
				
			||||||
    if (e.stack) {
 | 
					    if (e.stack) {
 | 
				
			||||||
| 
						 | 
					@ -64865,7 +64912,7 @@ async function run() {
 | 
				
			||||||
            core.info(`[warning] ${e.stack}`);
 | 
					            core.info(`[warning] ${e.stack}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        core.info(`... Saving cache ...`);
 | 
					        core.info(`... Saving cache ...`);
 | 
				
			||||||
        await cache.saveCache(config.cachePaths, config.cacheKey);
 | 
					        await withRetries(() => withTimeout(() => cache.saveCache(config.cachePaths, config.cacheKey), config.timeout), config.maxRetryAttempts, () => true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    catch (e) {
 | 
					    catch (e) {
 | 
				
			||||||
        core.info(`[warning] ${e.stack}`);
 | 
					        core.info(`[warning] ${e.stack}`);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
  "requires": true,
 | 
					  "requires": true,
 | 
				
			||||||
  "packages": {
 | 
					  "packages": {
 | 
				
			||||||
    "": {
 | 
					    "": {
 | 
				
			||||||
 | 
					      "name": "rust-cache",
 | 
				
			||||||
      "version": "2.0.0",
 | 
					      "version": "2.0.0",
 | 
				
			||||||
      "license": "LGPL-3.0",
 | 
					      "license": "LGPL-3.0",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,11 @@ export class CacheConfig {
 | 
				
			||||||
  /** The workspace configurations */
 | 
					  /** The workspace configurations */
 | 
				
			||||||
  public workspaces: Array<Workspace> = [];
 | 
					  public workspaces: Array<Workspace> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** The max timeout for the networking operations */
 | 
				
			||||||
 | 
					  public timeout: null | number = null;
 | 
				
			||||||
 | 
					  /** The max retry attemtps for the networking operations */
 | 
				
			||||||
 | 
					  public maxRetryAttempts: number = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** The prefix portion of the cache key */
 | 
					  /** The prefix portion of the cache key */
 | 
				
			||||||
  private keyPrefix = "";
 | 
					  private keyPrefix = "";
 | 
				
			||||||
  /** The rust version considered for the cache key */
 | 
					  /** The rust version considered for the cache key */
 | 
				
			||||||
| 
						 | 
					@ -156,6 +161,12 @@ export class CacheConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self.cachePaths = [CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
					    self.cachePaths = [CARGO_HOME, ...workspaces.map((ws) => ws.target)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const timeoutInput = core.getInput("timeout")
 | 
				
			||||||
 | 
					    self.timeout = timeoutInput ? parseFloat(timeoutInput) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const maxRetryAttemptsInput = core.getInput("maxRetryAttempts")
 | 
				
			||||||
 | 
					    self.maxRetryAttempts =  maxRetryAttemptsInput ? parseFloat(maxRetryAttemptsInput) : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return self;
 | 
					    return self;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -184,6 +195,10 @@ export class CacheConfig {
 | 
				
			||||||
    for (const file of this.keyFiles) {
 | 
					    for (const file of this.keyFiles) {
 | 
				
			||||||
      core.info(`  - ${file}`);
 | 
					      core.info(`  - ${file}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    core.info(`Network operations timeout:`);
 | 
				
			||||||
 | 
					    core.info(`    ${this.timeout}`);
 | 
				
			||||||
 | 
					    core.info(`Max retry attempts for the network operations:`);
 | 
				
			||||||
 | 
					    core.info(`    ${this.maxRetryAttempts}`);
 | 
				
			||||||
    core.endGroup();
 | 
					    core.endGroup();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import * as core from "@actions/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { cleanTargetDir, getCargoBins } from "./cleanup";
 | 
					import { cleanTargetDir, getCargoBins } from "./cleanup";
 | 
				
			||||||
import { CacheConfig, STATE_BINS, STATE_KEY } from "./config";
 | 
					import { CacheConfig, STATE_BINS, STATE_KEY } from "./config";
 | 
				
			||||||
 | 
					import { withRetries, withTimeout } from "./utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on("uncaughtException", (e) => {
 | 
					process.on("uncaughtException", (e) => {
 | 
				
			||||||
  core.info(`[warning] ${e.message}`);
 | 
					  core.info(`[warning] ${e.message}`);
 | 
				
			||||||
| 
						 | 
					@ -34,7 +35,16 @@ async function run() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    core.info(`... Restoring cache ...`);
 | 
					    core.info(`... Restoring cache ...`);
 | 
				
			||||||
    const key = config.cacheKey;
 | 
					    const key = config.cacheKey;
 | 
				
			||||||
    const restoreKey = await cache.restoreCache(config.cachePaths, key, [config.restoreKey]);
 | 
					    const restoreKey = await withRetries(
 | 
				
			||||||
 | 
					      () =>
 | 
				
			||||||
 | 
					        withTimeout(
 | 
				
			||||||
 | 
					          () => cache.restoreCache(config.cachePaths, key, [config.restoreKey]),
 | 
				
			||||||
 | 
					          config.timeout
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      config.maxRetryAttempts,
 | 
				
			||||||
 | 
					      () => true
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (restoreKey) {
 | 
					    if (restoreKey) {
 | 
				
			||||||
      core.info(`Restored from cache key "${restoreKey}".`);
 | 
					      core.info(`Restored from cache key "${restoreKey}".`);
 | 
				
			||||||
      core.saveState(STATE_KEY, restoreKey);
 | 
					      core.saveState(STATE_KEY, restoreKey);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								src/save.ts
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								src/save.ts
									
										
									
									
									
								
							| 
						 | 
					@ -4,6 +4,7 @@ import * as exec from "@actions/exec";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { cleanBin, cleanGit, cleanRegistry, cleanTargetDir } from "./cleanup";
 | 
					import { cleanBin, cleanGit, cleanRegistry, cleanTargetDir } from "./cleanup";
 | 
				
			||||||
import { CacheConfig, STATE_KEY } from "./config";
 | 
					import { CacheConfig, STATE_KEY } from "./config";
 | 
				
			||||||
 | 
					import { withRetries, withTimeout } from "./utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on("uncaughtException", (e) => {
 | 
					process.on("uncaughtException", (e) => {
 | 
				
			||||||
  core.info(`[warning] ${e.message}`);
 | 
					  core.info(`[warning] ${e.message}`);
 | 
				
			||||||
| 
						 | 
					@ -64,7 +65,15 @@ async function run() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    core.info(`... Saving cache ...`);
 | 
					    core.info(`... Saving cache ...`);
 | 
				
			||||||
    await cache.saveCache(config.cachePaths, config.cacheKey);
 | 
					    await withRetries(
 | 
				
			||||||
 | 
					      () =>
 | 
				
			||||||
 | 
					        withTimeout(
 | 
				
			||||||
 | 
					          () => cache.saveCache(config.cachePaths, config.cacheKey),
 | 
				
			||||||
 | 
					          config.timeout
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      config.maxRetryAttempts,
 | 
				
			||||||
 | 
					      () => true
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  } catch (e) {
 | 
					  } catch (e) {
 | 
				
			||||||
    core.info(`[warning] ${(e as any).stack}`);
 | 
					    core.info(`[warning] ${(e as any).stack}`);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/utils.ts
									
										
									
									
									
								
							
							
						
						
									
										46
									
								
								src/utils.ts
									
										
									
									
									
								
							| 
						 | 
					@ -28,3 +28,49 @@ export async function getCmdOutput(
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return stdout;
 | 
					  return stdout;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function withRetries<T>(
 | 
				
			||||||
 | 
					  operation: () => Promise<T>,
 | 
				
			||||||
 | 
					  maxRetryAttempts: number,
 | 
				
			||||||
 | 
					  isRetriable: (error: unknown) => boolean
 | 
				
			||||||
 | 
					): Promise<T> {
 | 
				
			||||||
 | 
					  let attemptsLeft = maxRetryAttempts;
 | 
				
			||||||
 | 
					  while (true) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      return await operation();
 | 
				
			||||||
 | 
					    } catch (e: unknown) {
 | 
				
			||||||
 | 
					      attemptsLeft -= 1;
 | 
				
			||||||
 | 
					      if (attemptsLeft <= 0) {
 | 
				
			||||||
 | 
					        throw e;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (!isRetriable(e)) {
 | 
				
			||||||
 | 
					        throw e;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      core.info(
 | 
				
			||||||
 | 
					        `[warning] Retrying after an error, ${attemptsLeft} attempts left, error: ${e}`
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TimeoutError extends Error {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function withTimeout<T>(
 | 
				
			||||||
 | 
					  operation: (onTimeout: Promise<void>) => Promise<T>,
 | 
				
			||||||
 | 
					  timeoutMs: null | number
 | 
				
			||||||
 | 
					): Promise<T> {
 | 
				
			||||||
 | 
					  const timeout = timeoutMs
 | 
				
			||||||
 | 
					    ? new Promise<void>((resolve) => {
 | 
				
			||||||
 | 
					        setTimeout(resolve, timeoutMs);
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    : new Promise<never>(() => {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const timeoutSym = Symbol("timeout" as const);
 | 
				
			||||||
 | 
					  const racingTimeout = timeout.then(() => timeoutSym);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const result = await Promise.race([racingTimeout, operation(timeout)]);
 | 
				
			||||||
 | 
					  if (result === timeoutSym) {
 | 
				
			||||||
 | 
					    throw new TimeoutError("operation timeout");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return result as Awaited<T>;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue