mirror of
https://github.com/Swatinem/rust-cache.git
synced 2025-08-14 20:55:13 +00:00
Merge ccd52e107768d27a2dd400974a0e2e4cdc7ec278 into 76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e
This commit is contained in:
commit
9244268a9e
@ -14,6 +14,12 @@ inputs:
|
||||
workspaces:
|
||||
description: "Paths to multiple Cargo workspaces and their target directories, separated by newlines"
|
||||
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:
|
||||
description: "Cache even if the build fails. Defaults to 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;
|
||||
}
|
||||
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
|
||||
|
||||
@ -64444,6 +64478,10 @@ class CacheConfig {
|
||||
this.restoreKey = "";
|
||||
/** The workspace configurations */
|
||||
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 */
|
||||
this.keyPrefix = "";
|
||||
/** The rust version considered for the cache key */
|
||||
@ -64549,6 +64587,10 @@ class CacheConfig {
|
||||
}
|
||||
self.workspaces = workspaces;
|
||||
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;
|
||||
}
|
||||
printInfo() {
|
||||
@ -64576,6 +64618,10 @@ class CacheConfig {
|
||||
for (const file of this.keyFiles) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -64810,6 +64856,7 @@ async function exists(path) {
|
||||
|
||||
|
||||
|
||||
|
||||
process.on("uncaughtException", (e) => {
|
||||
lib_core.info(`[warning] ${e.message}`);
|
||||
if (e.stack) {
|
||||
@ -64835,7 +64882,7 @@ async function run() {
|
||||
lib_core.saveState(config_STATE_BINS, JSON.stringify([...bins]));
|
||||
lib_core.info(`... Restoring cache ...`);
|
||||
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) {
|
||||
lib_core.info(`Restored from cache 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;
|
||||
}
|
||||
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
|
||||
|
||||
@ -64444,6 +64478,10 @@ class CacheConfig {
|
||||
this.restoreKey = "";
|
||||
/** The workspace configurations */
|
||||
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 */
|
||||
this.keyPrefix = "";
|
||||
/** The rust version considered for the cache key */
|
||||
@ -64549,6 +64587,10 @@ class CacheConfig {
|
||||
}
|
||||
self.workspaces = workspaces;
|
||||
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;
|
||||
}
|
||||
printInfo() {
|
||||
@ -64576,6 +64618,10 @@ class CacheConfig {
|
||||
for (const file of this.keyFiles) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -64811,6 +64857,7 @@ async function exists(path) {
|
||||
|
||||
|
||||
|
||||
|
||||
process.on("uncaughtException", (e) => {
|
||||
core.info(`[warning] ${e.message}`);
|
||||
if (e.stack) {
|
||||
@ -64865,7 +64912,7 @@ async function run() {
|
||||
core.info(`[warning] ${e.stack}`);
|
||||
}
|
||||
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) {
|
||||
core.info(`[warning] ${e.stack}`);
|
||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "rust-cache",
|
||||
"version": "2.0.0",
|
||||
"license": "LGPL-3.0",
|
||||
"dependencies": {
|
||||
|
@ -27,6 +27,11 @@ export class CacheConfig {
|
||||
/** The workspace configurations */
|
||||
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 */
|
||||
private keyPrefix = "";
|
||||
/** The rust version considered for the cache key */
|
||||
@ -156,6 +161,12 @@ export class CacheConfig {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -184,6 +195,10 @@ export class CacheConfig {
|
||||
for (const file of this.keyFiles) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import * as core from "@actions/core";
|
||||
|
||||
import { cleanTargetDir, getCargoBins } from "./cleanup";
|
||||
import { CacheConfig, STATE_BINS, STATE_KEY } from "./config";
|
||||
import { withRetries, withTimeout } from "./utils";
|
||||
|
||||
process.on("uncaughtException", (e) => {
|
||||
core.info(`[warning] ${e.message}`);
|
||||
@ -34,7 +35,16 @@ async function run() {
|
||||
|
||||
core.info(`... Restoring cache ...`);
|
||||
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) {
|
||||
core.info(`Restored from cache 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 { CacheConfig, STATE_KEY } from "./config";
|
||||
import { withRetries, withTimeout } from "./utils";
|
||||
|
||||
process.on("uncaughtException", (e) => {
|
||||
core.info(`[warning] ${e.message}`);
|
||||
@ -64,7 +65,15 @@ async function run() {
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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…
x
Reference in New Issue
Block a user