|
|
|
@ -97,25 +97,7 @@ actionsToolkit.run(
|
|
|
|
core.debug(`buildCmd.command: ${buildCmd.command}`);
|
|
|
|
core.debug(`buildCmd.command: ${buildCmd.command}`);
|
|
|
|
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
|
|
|
|
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
|
|
|
|
|
|
|
|
|
|
|
|
let err: Error | undefined;
|
|
|
|
await executeBuildWithRetry(buildCmd, inputs);
|
|
|
|
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
|
|
|
|
|
|
|
ignoreReturnCode: true,
|
|
|
|
|
|
|
|
env: Object.assign({}, process.env, {
|
|
|
|
|
|
|
|
BUILDX_METADATA_WARNINGS: 'true'
|
|
|
|
|
|
|
|
}) as {
|
|
|
|
|
|
|
|
[key: string]: string;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}).then(res => {
|
|
|
|
|
|
|
|
if (res.exitCode != 0) {
|
|
|
|
|
|
|
|
if (inputs.call && inputs.call === 'check' && res.stdout.length > 0) {
|
|
|
|
|
|
|
|
// checks warnings are printed to stdout: https://github.com/docker/buildx/pull/2647
|
|
|
|
|
|
|
|
// take the first line with the message summaryzing the warnings
|
|
|
|
|
|
|
|
err = new Error(res.stdout.split('\n')[0]?.trim());
|
|
|
|
|
|
|
|
} else if (res.stderr.length > 0) {
|
|
|
|
|
|
|
|
err = new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const imageID = toolkit.buildxBuild.resolveImageID();
|
|
|
|
const imageID = toolkit.buildxBuild.resolveImageID();
|
|
|
|
const metadata = toolkit.buildxBuild.resolveMetadata();
|
|
|
|
const metadata = toolkit.buildxBuild.resolveMetadata();
|
|
|
|
@ -182,10 +164,6 @@ actionsToolkit.run(
|
|
|
|
stateHelper.setSummarySupported();
|
|
|
|
stateHelper.setSummarySupported();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
|
|
throw err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// post
|
|
|
|
// post
|
|
|
|
async () => {
|
|
|
|
async () => {
|
|
|
|
@ -238,6 +216,89 @@ actionsToolkit.run(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function executeBuildWithRetry(buildCmd: {command: string; args: string[]}, inputs: context.Inputs): Promise<void> {
|
|
|
|
|
|
|
|
// Validate and sanitize retry inputs
|
|
|
|
|
|
|
|
let maxAttempts = inputs['max-attempts'];
|
|
|
|
|
|
|
|
if (isNaN(maxAttempts) || maxAttempts < 1) {
|
|
|
|
|
|
|
|
core.warning(`Invalid max-attempts value '${inputs['max-attempts']}'. Using default: 1`);
|
|
|
|
|
|
|
|
maxAttempts = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let retryWaitSeconds = inputs['retry-wait-seconds'];
|
|
|
|
|
|
|
|
if (isNaN(retryWaitSeconds) || retryWaitSeconds < 0) {
|
|
|
|
|
|
|
|
core.warning(`Invalid retry-wait-seconds value '${inputs['retry-wait-seconds']}'. Using default: 5`);
|
|
|
|
|
|
|
|
retryWaitSeconds = 5;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let timeoutMinutes = inputs['timeout-minutes'];
|
|
|
|
|
|
|
|
if (isNaN(timeoutMinutes) || timeoutMinutes < 0) {
|
|
|
|
|
|
|
|
core.warning(`Invalid timeout-minutes value '${inputs['timeout-minutes']}'. Using default: 0`);
|
|
|
|
|
|
|
|
timeoutMinutes = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let lastError: Error | undefined;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (maxAttempts > 1) {
|
|
|
|
|
|
|
|
core.info(`Build attempt ${attempt} of ${maxAttempts}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await executeBuildWithTimeout(buildCmd, inputs, timeoutMinutes);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
lastError = error as Error;
|
|
|
|
|
|
|
|
core.warning(`Build failed on attempt ${attempt}: ${lastError.message}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (attempt < maxAttempts) {
|
|
|
|
|
|
|
|
if (retryWaitSeconds > 0) {
|
|
|
|
|
|
|
|
core.info(`Retrying in ${retryWaitSeconds} seconds...`);
|
|
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, retryWaitSeconds * 1000));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
core.info('Retrying immediately...');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (lastError) {
|
|
|
|
|
|
|
|
core.error(`All ${maxAttempts} attempts failed`);
|
|
|
|
|
|
|
|
throw lastError;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function executeBuildWithTimeout(buildCmd: {command: string; args: string[]}, inputs: context.Inputs, timeoutMinutes: number): Promise<void> {
|
|
|
|
|
|
|
|
const buildPromise = Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
|
|
|
|
|
|
|
ignoreReturnCode: true,
|
|
|
|
|
|
|
|
env: Object.assign({}, process.env, {
|
|
|
|
|
|
|
|
BUILDX_METADATA_WARNINGS: 'true'
|
|
|
|
|
|
|
|
}) as {
|
|
|
|
|
|
|
|
[key: string]: string;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}).then(res => {
|
|
|
|
|
|
|
|
if (res.exitCode != 0) {
|
|
|
|
|
|
|
|
if (inputs.call && inputs.call === 'check' && res.stdout.length > 0) {
|
|
|
|
|
|
|
|
throw new Error(res.stdout.split('\n')[0]?.trim());
|
|
|
|
|
|
|
|
} else if (res.stderr.length > 0) {
|
|
|
|
|
|
|
|
throw new Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new Error('buildx failed with unknown error');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (timeoutMinutes <= 0) {
|
|
|
|
|
|
|
|
return buildPromise;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let timeoutHandle: NodeJS.Timeout;
|
|
|
|
|
|
|
|
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
|
|
|
|
|
|
timeoutHandle = setTimeout(() => reject(new Error(`Build attempt timed out after ${timeoutMinutes} minutes`)), timeoutMinutes * 60 * 1000);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Promise.race([buildPromise, timeoutPromise]).finally(() => clearTimeout(timeoutHandle));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise<string> {
|
|
|
|
async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise<string> {
|
|
|
|
// get ref from metadata file
|
|
|
|
// get ref from metadata file
|
|
|
|
const ref = toolkit.buildxBuild.resolveRef();
|
|
|
|
const ref = toolkit.buildxBuild.resolveRef();
|
|
|
|
|