Merge ccd52e107768d27a2dd400974a0e2e4cdc7ec278 into 76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e

This commit is contained in:
MOZGIII 2022-09-20 14:49:43 +02:00 committed by GitHub
commit 9244268a9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1184 additions and 1003 deletions

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
"name": "rust-cache",
"version": "2.0.0",
"license": "LGPL-3.0",
"dependencies": {

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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}`);
}

View File

@ -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>;
}