Compare commits

...

7 commits

Author SHA1 Message Date
Daniel Amar
9282018910
Merge 80d22c042f into e468171a9d 2025-06-18 15:24:54 +02:00
CrazyMax
e468171a9d
Merge pull request #429 from crazy-max/fix-keep-state
fix keep-state not being respected
2025-06-18 10:37:30 +02:00
CrazyMax
a3e7502fd0
chore: update generated content
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-06-17 17:34:10 +02:00
CrazyMax
b145473295
fix keep-state not being respected
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-06-17 17:34:10 +02:00
Daniel Amar
80d22c042f
remove retry only on certain errors. maxRetries set to 3 instead of 5. 2025-05-27 13:53:26 -04:00
Daniel Amar
66d7daabf8
Add timeout control to prevent long waits during buildkit initialization
Signed-off-by: Daniel Amar <daniel.amar@onepeloton.com>
2025-05-05 17:09:29 -04:00
Daniel Amar
bc3c5abd64
Add retry mechanism with exponential backoff for buildkit bootstrap
Signed-off-by: Daniel Amar <daniel.amar@onepeloton.com>
2025-05-05 17:09:29 -04:00
5 changed files with 71 additions and 12 deletions

View file

@ -621,6 +621,7 @@ jobs:
uses: ./
with:
name: foo
keep-state: true
keep-state-error:
runs-on: ubuntu-latest

2
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View file

@ -17,6 +17,37 @@ import {ContextInfo} from '@docker/actions-toolkit/lib/types/docker/docker';
import * as context from './context';
import * as stateHelper from './state-helper';
/**
* Retry a function with exponential backoff
*/
async function retryWithBackoff<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
initialDelay: number = 1000,
maxDelay: number = 10000,
shouldRetry: (error: Error) => boolean = () => true
): Promise<T> {
let retries = 0;
let delay = initialDelay;
while (true) {
try {
return await operation();
} catch (error) {
if (retries >= maxRetries || !shouldRetry(error)) {
throw error;
}
retries++;
core.info(`Retry ${retries}/${maxRetries} after ${delay}ms due to: ${error.message}`);
await new Promise(resolve => setTimeout(resolve, delay));
// Exponential backoff with jitter
delay = Math.min(delay * 2, maxDelay) * (0.8 + Math.random() * 0.4);
}
}
}
actionsToolkit.run(
// main
async () => {
@ -175,13 +206,40 @@ actionsToolkit.run(
await core.group(`Booting builder`, async () => {
const inspectCmd = await toolkit.buildx.getCommand(await context.getInspectArgs(inputs, toolkit));
await Exec.getExecOutput(inspectCmd.command, inspectCmd.args, {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error');
}
});
try {
await retryWithBackoff(
async () => {
// Create a promise that will timeout after 15 seconds
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('Timeout exceeded while waiting for buildkit to initialize'));
}, 15000); // 15 second timeout
});
// Create the actual command execution promise
const execPromise = Exec.getExecOutput(inspectCmd.command, inspectCmd.args, {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error');
}
return res;
});
// Race the timeout against the actual command
// If the command takes too long, we'll get the timeout error instead
return Promise.race([execPromise, timeoutPromise]);
},
3, // maxRetries - retry up to 3 times for buildkit initialization
1000, // initialDelay - start with 1 second
15000 // maxDelay - cap at 15 seconds
);
} catch (error) {
// Log the warning but continue - this matches current behavior where builds still succeed
core.warning(`Failed to bootstrap builder after multiple retries: ${error.message}`);
core.warning('Continuing execution as buildkit daemon may initialize later');
}
});
if (inputs.install) {

View file

@ -9,7 +9,7 @@ export const certsDir = process.env['STATE_certsDir'] || '';
export const tmpDockerContext = process.env['STATE_tmpDockerContext'] || '';
export const cleanup = /true/i.test(process.env['STATE_cleanup'] || '');
export const buildxIsDefaultBuilder = /true/i.test(process.env['STATE_buildxIsDefaultBuilder'] || '');
export const keepState = !!process.env['STATE_keepState'];
export const keepState = /true/i.test(process.env['STATE_keepState'] || '');
export function setDebug(debug: string) {
core.saveState('isDebug', debug);
@ -47,6 +47,6 @@ export function setBuildxIsDefaultBuilder(buildxIsDefaultBuilder: boolean) {
core.saveState('buildxIsDefaultBuilder', buildxIsDefaultBuilder);
}
export function setKeepState(retain: boolean) {
core.saveState('keepState', retain);
export function setKeepState(keepState: boolean) {
core.saveState('keepState', keepState);
}