src: wrap all steps after blacksmith builder in try catch

This ensures we always run cleanup if any step after creating the Blacksmith
builder errors out.
pull/1358/head
Aditya Maru 1 year ago
parent 02f42e1599
commit bdf7f0bb37

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

@ -569,217 +569,216 @@ actionsToolkit.run(
} }
}); });
if (builderInfo.addr) { let buildError: Error | undefined;
await core.group(`Creating a builder instance`, async () => { let buildDurationSeconds: string | undefined;
const name = `blacksmith-${Date.now().toString(36)}`; let ref: string | undefined;
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, builderInfo.addr!)); try {
core.info(`Creating builder with command: ${createCmd.command}`); if (builderInfo.addr) {
await Exec.getExecOutput(createCmd.command, createCmd.args, { await core.group(`Creating a builder instance`, async () => {
ignoreReturnCode: true const name = `blacksmith-${Date.now().toString(36)}`;
}).then(res => { const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, builderInfo.addr!));
if (res.stderr.length > 0 && res.exitCode != 0) { core.info(`Creating builder with command: ${createCmd.command}`);
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'); await Exec.getExecOutput(createCmd.command, createCmd.args, {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error');
}
});
});
} else {
await core.group(`Checking for configured builder`, async () => {
try {
const builder = await toolkit.builder.inspect();
if (builder) {
core.info(`Found configured builder: ${builder.name}`);
} else {
// Create a local builder using the docker-container driver (which is the default driver in setup-buildx)
const createLocalBuilderCmd = 'docker buildx create --name local --driver docker-container --use';
try {
await Exec.exec(createLocalBuilderCmd);
core.info('Created and set a local builder for use');
} catch (error) {
core.setFailed(`Failed to create local builder: ${error.message}`);
}
}
} catch (error) {
core.setFailed(`Error configuring builder: ${error.message}`);
} }
}); });
}); }
} else {
// If we failed to obtain the address, let's check if we have an already configured builder. await core.group(`Proxy configuration`, async () => {
await core.group(`Checking for configured builder`, async () => { let dockerConfig: ConfigFile | undefined;
let dockerConfigMalformed = false;
try { try {
const builder = await toolkit.builder.inspect(); dockerConfig = await Docker.configFile();
if (builder) { } catch (e) {
core.info(`Found configured builder: ${builder.name}`); dockerConfigMalformed = true;
} else { core.warning(`Unable to parse config file ${path.join(Docker.configDir, 'config.json')}: ${e}`);
// Create a local builder using the docker-container driver (which is the default driver in setup-buildx) }
const createLocalBuilderCmd = 'docker buildx create --name local --driver docker-container --use'; if (dockerConfig && dockerConfig.proxies) {
try { for (const host in dockerConfig.proxies) {
await Exec.exec(createLocalBuilderCmd); let prefix = '';
core.info('Created and set a local builder for use'); if (Object.keys(dockerConfig.proxies).length > 1) {
} catch (error) { prefix = ' ';
core.setFailed(`Failed to create local builder: ${error.message}`); core.info(host);
}
for (const key in dockerConfig.proxies[host]) {
core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`);
} }
} }
} catch (error) { } else if (!dockerConfigMalformed) {
core.setFailed(`Error configuring builder: ${error.message}`); core.info('No proxy configuration found');
} }
}); });
}
await core.group(`Proxy configuration`, async () => { stateHelper.setTmpDir(Context.tmpDir());
let dockerConfig: ConfigFile | undefined;
let dockerConfigMalformed = false;
try {
dockerConfig = await Docker.configFile();
} catch (e) {
dockerConfigMalformed = true;
core.warning(`Unable to parse config file ${path.join(Docker.configDir, 'config.json')}: ${e}`);
}
if (dockerConfig && dockerConfig.proxies) {
for (const host in dockerConfig.proxies) {
let prefix = '';
if (Object.keys(dockerConfig.proxies).length > 1) {
prefix = ' ';
core.info(host);
}
for (const key in dockerConfig.proxies[host]) {
core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`);
}
}
} else if (!dockerConfigMalformed) {
core.info('No proxy configuration found');
}
});
stateHelper.setTmpDir(Context.tmpDir()); let builder: BuilderInfo;
await core.group(`Builder info`, async () => {
builder = await toolkit.builder.inspect();
core.info(JSON.stringify(builder, null, 2));
});
let builder: BuilderInfo; const args: string[] = await context.getArgs(inputs, toolkit);
await core.group(`Builder info`, async () => { args.push('--debug');
builder = await toolkit.builder.inspect(); core.debug(`context.getArgs: ${JSON.stringify(args)}`);
core.info(JSON.stringify(builder, null, 2));
});
const args: string[] = await context.getArgs(inputs, toolkit); const buildCmd = await toolkit.buildx.getCommand(args);
args.push('--debug');
core.debug(`context.getArgs: ${JSON.stringify(args)}`);
const buildCmd = await toolkit.buildx.getCommand(args); core.debug(`buildCmd.command: ${buildCmd.command}`);
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
core.debug(`buildCmd.command: ${buildCmd.command}`); const buildStartTime = Date.now();
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`); await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
ignoreReturnCode: true,
env: Object.assign({}, process.env, {
BUILDX_METADATA_WARNINGS: 'true'
}) as {
[key: string]: string;
}
}).then(res => {
buildDurationSeconds = Math.round((Date.now() - buildStartTime) / 1000).toString();
stateHelper.setDockerBuildDurationSeconds(buildDurationSeconds);
if (res.stderr.length > 0 && res.exitCode != 0) {
throw Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
}
});
let err: Error | undefined; const imageID = toolkit.buildxBuild.resolveImageID();
const buildStartTime = Date.now(); const metadata = toolkit.buildxBuild.resolveMetadata();
let buildDurationSeconds: string | undefined; const digest = toolkit.buildxBuild.resolveDigest(metadata);
await Exec.getExecOutput(buildCmd.command, buildCmd.args, { if (imageID) {
ignoreReturnCode: true, await core.group(`ImageID`, async () => {
env: Object.assign({}, process.env, { core.info(imageID);
BUILDX_METADATA_WARNINGS: 'true' core.setOutput('imageid', imageID);
}) as { });
[key: string]: string;
} }
}).then(res => { if (digest) {
if (res.stderr.length > 0 && res.exitCode != 0) { await core.group(`Digest`, async () => {
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`); core.info(digest);
core.setOutput('digest', digest);
});
}
if (metadata) {
await core.group(`Metadata`, async () => {
const metadatadt = JSON.stringify(metadata, null, 2);
core.info(metadatadt);
core.setOutput('metadata', metadatadt);
});
} }
buildDurationSeconds = Math.round((Date.now() - buildStartTime) / 1000).toString();
stateHelper.setDockerBuildDurationSeconds(buildDurationSeconds);
});
const imageID = toolkit.buildxBuild.resolveImageID(); let ref: string | undefined;
const metadata = toolkit.buildxBuild.resolveMetadata(); await core.group(`Reference`, async () => {
const digest = toolkit.buildxBuild.resolveDigest(metadata); ref = await buildRef(toolkit, startedTime, builder.name);
if (imageID) { if (ref) {
await core.group(`ImageID`, async () => { core.info(ref);
core.info(imageID); stateHelper.setBuildRef(ref);
core.setOutput('imageid', imageID); } else {
}); core.info('No build reference found');
} }
if (digest) {
await core.group(`Digest`, async () => {
core.info(digest);
core.setOutput('digest', digest);
});
}
if (metadata) {
await core.group(`Metadata`, async () => {
const metadatadt = JSON.stringify(metadata, null, 2);
core.info(metadatadt);
core.setOutput('metadata', metadatadt);
}); });
}
let ref: string | undefined; if (buildChecksAnnotationsEnabled()) {
await core.group(`Reference`, async () => { const warnings = toolkit.buildxBuild.resolveWarnings(metadata);
ref = await buildRef(toolkit, startedTime, builder.name); if (ref && warnings && warnings.length > 0) {
if (ref) { const annotations = await Buildx.convertWarningsToGitHubAnnotations(warnings, [ref]);
core.info(ref); core.debug(`annotations: ${JSON.stringify(annotations, null, 2)}`);
stateHelper.setBuildRef(ref); if (annotations && annotations.length > 0) {
} else { await core.group(`Generating GitHub annotations (${annotations.length} build checks found)`, async () => {
core.info('No build reference found'); for (const annotation of annotations) {
core.warning(annotation.message, annotation);
}
});
}
}
} }
});
if (buildChecksAnnotationsEnabled()) { await core.group(`Check build summary support`, async () => {
const warnings = toolkit.buildxBuild.resolveWarnings(metadata); if (!buildSummaryEnabled()) {
if (ref && warnings && warnings.length > 0) { core.info('Build summary disabled');
const annotations = await Buildx.convertWarningsToGitHubAnnotations(warnings, [ref]); } else if (GitHub.isGHES) {
core.debug(`annotations: ${JSON.stringify(annotations, null, 2)}`); core.info('Build summary is not yet supported on GHES');
if (annotations && annotations.length > 0) { } else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) {
await core.group(`Generating GitHub annotations (${annotations.length} build checks found)`, async () => { core.info('Build summary requires Buildx >= 0.13.0');
for (const annotation of annotations) { } else if (builder && builder.driver === 'cloud') {
core.warning(annotation.message, annotation); core.info('Build summary is not yet supported with Docker Build Cloud');
} } else if (!ref) {
}); core.info('Build summary requires a build reference');
} else {
core.info('Build summary supported!');
stateHelper.setSummarySupported();
} }
} });
} catch (error) {
buildError = error as Error;
} }
await core.group(`Check build summary support`, async () => { await core.group('Cleaning up Blacksmith builder', async () => {
if (!buildSummaryEnabled()) { if (builderInfo.addr) {
core.info('Build summary disabled'); try {
} else if (GitHub.isGHES) { const buildxHistory = new BuildxHistory();
core.info('Build summary is not yet supported on GHES'); const exportRes = await buildxHistory.export({
} else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) { refs: ref ? [ref] : []
core.info('Build summary requires Buildx >= 0.13.0'); });
} else if (builder && builder.driver === 'cloud') { await shutdownBuildkitd();
core.info('Build summary is not yet supported with Docker Build Cloud'); core.info('Shutdown buildkitd');
} else if (!ref) { for (let attempt = 1; attempt <= 3; attempt++) {
core.info('Build summary requires a build reference'); try {
} else { await execAsync(`sudo umount ${mountPoint}`);
core.info('Build summary supported!'); core.debug(`${device} has been unmounted`);
stateHelper.setSummarySupported(); break;
} } catch (error) {
}); if (attempt === 3) {
throw error;
if (builderInfo.addr) { }
if (err) { core.warning(`Unmount failed, retrying (${attempt}/3)...`);
core.info(`Build failed: ${err.message}`); await new Promise(resolve => setTimeout(resolve, 100));
stateHelper.setDockerBuildStatus('failure');
} else {
core.info('Build completed successfully');
stateHelper.setDockerBuildStatus('success');
}
try {
const buildxHistory = new BuildxHistory();
const exportRes = await buildxHistory.export({
refs: ref ? [ref] : []
});
await shutdownBuildkitd();
core.info('Shutdown buildkitd');
for (let attempt = 1; attempt <= 3; attempt++) {
try {
await execAsync(`sudo umount ${mountPoint}`);
core.debug(`${device} has been unmounted`);
break;
} catch (error) {
if (attempt === 3) {
throw error;
} }
core.warning(`Unmount failed, retrying (${attempt}/3)...`);
await new Promise(resolve => setTimeout(resolve, 100));
} }
} core.info('Unmounted device');
core.info('Unmounted device'); if (!buildError) {
if (!err) { await reportBuildCompleted(exportRes, builderInfo.buildId, ref, buildDurationSeconds);
await reportBuildCompleted(exportRes, builderInfo.buildId, ref, buildDurationSeconds); } else {
} else { try {
try { const buildkitdLog = fs.readFileSync('buildkitd.log', 'utf8');
const buildkitdLog = fs.readFileSync('buildkitd.log', 'utf8'); core.info('buildkitd.log contents:');
core.info('buildkitd.log contents:'); core.info(buildkitdLog);
core.info(buildkitdLog); } catch (error) {
} catch (error) { core.warning(`Failed to read buildkitd.log: ${error.message}`);
core.warning(`Failed to read buildkitd.log: ${error.message}`); }
await reportBuildFailed(builderInfo.buildId, buildDurationSeconds);
} }
await reportBuildFailed(builderInfo.buildId, buildDurationSeconds); } catch (error) {
core.warning(`Error during Blacksmith builder shutdown: ${error.message}`);
} }
} catch (error) {
core.warning(`Error during Blacksmith builder shutdown: ${error.message}`);
} }
} });
if (err) { // Re-throw the error after cleanup
throw err; if (buildError) {
throw buildError;
} }
}, },
// post // post
@ -826,28 +825,4 @@ function buildSummaryEnabled(): boolean {
return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY); return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY);
} }
return true; return true;
} }
function buildRecordUploadEnabled(): boolean {
if (process.env.DOCKER_BUILD_RECORD_UPLOAD) {
return Util.parseBool(process.env.DOCKER_BUILD_RECORD_UPLOAD);
}
return true;
}
function buildRecordRetentionDays(): number | undefined {
let val: string | undefined;
if (process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS) {
core.warning('DOCKER_BUILD_EXPORT_RETENTION_DAYS is deprecated. Use DOCKER_BUILD_RECORD_RETENTION_DAYS instead.');
val = process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS;
} else if (process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS) {
val = process.env.DOCKER_BUILD_RECORD_RETENTION_DAYS;
}
if (val) {
const res = parseInt(val);
if (isNaN(res)) {
throw Error(`Invalid build record retention days: ${val}`);
}
return res;
}
}
Loading…
Cancel
Save