src: fix the movement of cleanup to the main step
parent
eb6486e7e1
commit
1dee25cffd
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
118
src/main.ts
118
src/main.ts
|
@ -16,7 +16,6 @@ import {Util} from '@docker/actions-toolkit/lib/util';
|
||||||
|
|
||||||
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
|
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
|
||||||
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
|
import {ConfigFile} from '@docker/actions-toolkit/lib/types/docker/docker';
|
||||||
import {UploadArtifactResponse} from '@docker/actions-toolkit/lib/types/github';
|
|
||||||
import axios, {AxiosError, AxiosInstance, AxiosResponse} from 'axios';
|
import axios, {AxiosError, AxiosInstance, AxiosResponse} from 'axios';
|
||||||
|
|
||||||
import * as context from './context';
|
import * as context from './context';
|
||||||
|
@ -38,8 +37,8 @@ async function getBlacksmithAgentClient(): Promise<AxiosInstance> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reports a successful build to the local sticky disk manager
|
// Reports a successful build to the local sticky disk manager
|
||||||
async function reportBuildCompleted(exportRes?: ExportRecordResponse) {
|
async function reportBuildCompleted(exportRes?: ExportRecordResponse, blacksmithDockerBuildId?: string | null, buildRef?: string, dockerBuildDurationSeconds?: string) {
|
||||||
if (!stateHelper.blacksmithDockerBuildId) {
|
if (!blacksmithDockerBuildId) {
|
||||||
core.warning('No docker build ID found, skipping build completion report');
|
core.warning('No docker build ID found, skipping build completion report');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -57,15 +56,15 @@ async function reportBuildCompleted(exportRes?: ExportRecordResponse) {
|
||||||
|
|
||||||
// Report success to Blacksmith API
|
// Report success to Blacksmith API
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
docker_build_id: stateHelper.blacksmithDockerBuildId,
|
docker_build_id: blacksmithDockerBuildId,
|
||||||
conclusion: 'successful',
|
conclusion: 'successful',
|
||||||
runtime_seconds: stateHelper.dockerBuildDurationSeconds
|
runtime_seconds: dockerBuildDurationSeconds
|
||||||
};
|
};
|
||||||
|
|
||||||
if (exportRes) {
|
if (exportRes) {
|
||||||
let buildRefSummary;
|
let buildRefSummary;
|
||||||
// Extract just the ref ID from the full buildRef path
|
// Extract just the ref ID from the full buildRef path
|
||||||
const refId = stateHelper.buildRef?.split('/').pop();
|
const refId = buildRef?.split('/').pop();
|
||||||
core.info(`Using buildRef ID: ${refId}`);
|
core.info(`Using buildRef ID: ${refId}`);
|
||||||
if (refId && exportRes.summaries[refId]) {
|
if (refId && exportRes.summaries[refId]) {
|
||||||
buildRefSummary = exportRes.summaries[refId];
|
buildRefSummary = exportRes.summaries[refId];
|
||||||
|
@ -83,7 +82,7 @@ async function reportBuildCompleted(exportRes?: ExportRecordResponse) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${stateHelper.blacksmithDockerBuildId}`, requestOptions, retryCondition);
|
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${blacksmithDockerBuildId}`, requestOptions, retryCondition);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning('Error reporting build completed:', error);
|
core.warning('Error reporting build completed:', error);
|
||||||
|
@ -92,8 +91,8 @@ async function reportBuildCompleted(exportRes?: ExportRecordResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reports a failed build to both the local sticky disk manager and the Blacksmith API
|
// Reports a failed build to both the local sticky disk manager and the Blacksmith API
|
||||||
async function reportBuildFailed() {
|
async function reportBuildFailed(dockerBuildId: string | null, dockerBuildDurationSeconds?: string) {
|
||||||
if (!stateHelper.blacksmithDockerBuildId) {
|
if (!dockerBuildId) {
|
||||||
core.warning('No docker build ID found, skipping build completion report');
|
core.warning('No docker build ID found, skipping build completion report');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -111,12 +110,12 @@ async function reportBuildFailed() {
|
||||||
|
|
||||||
// Report failure to Blacksmith API
|
// Report failure to Blacksmith API
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
docker_build_id: stateHelper.blacksmithDockerBuildId,
|
docker_build_id: dockerBuildId,
|
||||||
conclusion: 'failed',
|
conclusion: 'failed',
|
||||||
runtime_seconds: stateHelper.dockerBuildDurationSeconds
|
runtime_seconds: dockerBuildDurationSeconds
|
||||||
};
|
};
|
||||||
|
|
||||||
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${stateHelper.blacksmithDockerBuildId}`, requestOptions, retryCondition);
|
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${dockerBuildId}`, requestOptions, retryCondition);
|
||||||
return;
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning('Error reporting build failed:', error);
|
core.warning('Error reporting build failed:', error);
|
||||||
|
@ -332,9 +331,13 @@ async function maybeFormatBlockDevice(device: string): Promise<string> {
|
||||||
const {stdout} = await execAsync(`sudo blkid -o value -s TYPE ${device}`);
|
const {stdout} = await execAsync(`sudo blkid -o value -s TYPE ${device}`);
|
||||||
if (stdout.trim() === 'ext4') {
|
if (stdout.trim() === 'ext4') {
|
||||||
core.debug(`Device ${device} is already formatted with ext4`);
|
core.debug(`Device ${device} is already formatted with ext4`);
|
||||||
// Run resize2fs to ensure filesystem uses full block device
|
try {
|
||||||
await execAsync(`sudo resize2fs ${device}`);
|
// Run resize2fs to ensure filesystem uses full block device
|
||||||
core.debug(`Resized ext4 filesystem on ${device}`);
|
await execAsync(`sudo resize2fs -f ${device}`);
|
||||||
|
core.debug(`Resized ext4 filesystem on ${device}`);
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(`Error resizing ext4 filesystem on ${device}: ${error}`);
|
||||||
|
}
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -399,23 +402,24 @@ async function reportBuilderCreationFailed(stickydiskKey: string) {
|
||||||
// getBuilderAddr mounts a sticky disk for the entity, sets up buildkitd on top of it
|
// getBuilderAddr mounts a sticky disk for the entity, sets up buildkitd on top of it
|
||||||
// and returns the address to the builder.
|
// and returns the address to the builder.
|
||||||
// If it is unable to do so because of a timeout or an error it returns null.
|
// If it is unable to do so because of a timeout or an error it returns null.
|
||||||
async function getBuilderAddr(inputs: context.Inputs, dockerfilePath: string): Promise<string | null> {
|
async function getBuilderAddr(inputs: context.Inputs, dockerfilePath: string): Promise<{addr: string | null; buildId?: string | null}> {
|
||||||
try {
|
try {
|
||||||
const retryCondition = (error: AxiosError) => (error.response?.status ? error.response.status >= 500 : error.code === 'ECONNRESET');
|
const retryCondition = (error: AxiosError) => (error.response?.status ? error.response.status >= 500 : error.code === 'ECONNRESET');
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||||
|
|
||||||
|
let buildResponse: {docker_build_id: string} | null = null;
|
||||||
try {
|
try {
|
||||||
await getStickyDisk(dockerfilePath, retryCondition, {signal: controller.signal});
|
await getStickyDisk(dockerfilePath, retryCondition, {signal: controller.signal});
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
await maybeFormatBlockDevice(device);
|
await maybeFormatBlockDevice(device);
|
||||||
await reportBuild(dockerfilePath);
|
buildResponse = await reportBuild(dockerfilePath);
|
||||||
await execAsync(`sudo mkdir -p ${mountPoint}`);
|
await execAsync(`sudo mkdir -p ${mountPoint}`);
|
||||||
await execAsync(`sudo mount ${device} ${mountPoint}`);
|
await execAsync(`sudo mount ${device} ${mountPoint}`);
|
||||||
core.debug(`${device} has been mounted to ${mountPoint}`);
|
core.debug(`${device} has been mounted to ${mountPoint}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'AbortError') {
|
if (error.name === 'AbortError') {
|
||||||
return null;
|
return {addr: null};
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -442,7 +446,7 @@ async function getBuilderAddr(inputs: context.Inputs, dockerfilePath: string): P
|
||||||
if (!fs.existsSync('/run/buildkit/buildkitd.sock')) {
|
if (!fs.existsSync('/run/buildkit/buildkitd.sock')) {
|
||||||
throw new Error('buildkitd socket not found after 3s timeout');
|
throw new Error('buildkitd socket not found after 3s timeout');
|
||||||
}
|
}
|
||||||
return buildkitdAddr;
|
return {addr: buildkitdAddr, buildId: buildResponse?.docker_build_id};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as AxiosError).response && (error as AxiosError).response!.status === 404) {
|
if ((error as AxiosError).response && (error as AxiosError).response!.status === 404) {
|
||||||
if (!inputs.nofallback) {
|
if (!inputs.nofallback) {
|
||||||
|
@ -451,7 +455,7 @@ async function getBuilderAddr(inputs: context.Inputs, dockerfilePath: string): P
|
||||||
} else {
|
} else {
|
||||||
core.warning(`Error in getBuildkitdAddr: ${(error as Error).message}`);
|
core.warning(`Error in getBuildkitdAddr: ${(error as Error).message}`);
|
||||||
}
|
}
|
||||||
return null;
|
return {addr: null};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +519,10 @@ actionsToolkit.run(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let localBuilderAddr: string | null = null;
|
let builderInfo = {
|
||||||
|
addr: null as string | null,
|
||||||
|
buildId: null as string | null
|
||||||
|
};
|
||||||
await core.group(`Starting Blacksmith builder`, async () => {
|
await core.group(`Starting Blacksmith builder`, async () => {
|
||||||
const dockerfilePath = context.getDockerfilePath(inputs);
|
const dockerfilePath = context.getDockerfilePath(inputs);
|
||||||
if (!dockerfilePath) {
|
if (!dockerfilePath) {
|
||||||
|
@ -530,8 +537,12 @@ actionsToolkit.run(
|
||||||
if (dockerfilePath && dockerfilePath.length > 0) {
|
if (dockerfilePath && dockerfilePath.length > 0) {
|
||||||
core.debug(`Using dockerfile path: ${dockerfilePath}`);
|
core.debug(`Using dockerfile path: ${dockerfilePath}`);
|
||||||
}
|
}
|
||||||
localBuilderAddr = await getBuilderAddr(inputs, dockerfilePath);
|
const {addr, buildId} = await getBuilderAddr(inputs, dockerfilePath);
|
||||||
if (!localBuilderAddr) {
|
builderInfo = {
|
||||||
|
addr: addr || null,
|
||||||
|
buildId: buildId || null
|
||||||
|
};
|
||||||
|
if (!builderInfo.addr) {
|
||||||
await reportBuilderCreationFailed(dockerfilePath);
|
await reportBuilderCreationFailed(dockerfilePath);
|
||||||
if (inputs.nofallback) {
|
if (inputs.nofallback) {
|
||||||
throw Error('Failed to obtain Blacksmith builder. Failing the build');
|
throw Error('Failed to obtain Blacksmith builder. Failing the build');
|
||||||
|
@ -541,10 +552,10 @@ actionsToolkit.run(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (localBuilderAddr) {
|
if (builderInfo.addr) {
|
||||||
await core.group(`Creating a builder instance`, async () => {
|
await core.group(`Creating a builder instance`, async () => {
|
||||||
const name = `blacksmith`;
|
const name = `blacksmith`;
|
||||||
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, localBuilderAddr!));
|
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, builderInfo.addr!));
|
||||||
core.info(`Creating builder with command: ${createCmd.command}`);
|
core.info(`Creating builder with command: ${createCmd.command}`);
|
||||||
await Exec.getExecOutput(createCmd.command, createCmd.args, {
|
await Exec.getExecOutput(createCmd.command, createCmd.args, {
|
||||||
ignoreReturnCode: true
|
ignoreReturnCode: true
|
||||||
|
@ -621,6 +632,7 @@ actionsToolkit.run(
|
||||||
|
|
||||||
let err: Error | undefined;
|
let err: Error | undefined;
|
||||||
const buildStartTime = Date.now();
|
const buildStartTime = Date.now();
|
||||||
|
let buildDurationSeconds: string | undefined;
|
||||||
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
||||||
ignoreReturnCode: true,
|
ignoreReturnCode: true,
|
||||||
env: Object.assign({}, process.env, {
|
env: Object.assign({}, process.env, {
|
||||||
|
@ -632,7 +644,7 @@ actionsToolkit.run(
|
||||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
||||||
}
|
}
|
||||||
const buildDurationSeconds = Math.round((Date.now() - buildStartTime) / 1000).toString();
|
buildDurationSeconds = Math.round((Date.now() - buildStartTime) / 1000).toString();
|
||||||
stateHelper.setDockerBuildDurationSeconds(buildDurationSeconds);
|
stateHelper.setDockerBuildDurationSeconds(buildDurationSeconds);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -702,15 +714,22 @@ actionsToolkit.run(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (localBuilderAddr) {
|
if (builderInfo.addr) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
core.info(`Build failed: ${err.message}`);
|
||||||
stateHelper.setDockerBuildStatus('failure');
|
stateHelper.setDockerBuildStatus('failure');
|
||||||
} else {
|
} else {
|
||||||
|
core.info('Build completed successfully');
|
||||||
stateHelper.setDockerBuildStatus('success');
|
stateHelper.setDockerBuildStatus('success');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const buildxHistory = new BuildxHistory();
|
||||||
|
const exportRes = await buildxHistory.export({
|
||||||
|
refs: ref ? [ref] : []
|
||||||
|
});
|
||||||
await shutdownBuildkitd();
|
await shutdownBuildkitd();
|
||||||
|
core.info('Shutdown buildkitd');
|
||||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||||
try {
|
try {
|
||||||
await execAsync(`sudo umount ${mountPoint}`);
|
await execAsync(`sudo umount ${mountPoint}`);
|
||||||
|
@ -724,12 +743,9 @@ actionsToolkit.run(
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stateHelper.dockerBuildStatus == 'success') {
|
core.info('Unmounted device');
|
||||||
const buildxHistory = new BuildxHistory();
|
if (!err) {
|
||||||
const exportRes = await buildxHistory.export({
|
await reportBuildCompleted(exportRes, builderInfo.buildId, ref, buildDurationSeconds);
|
||||||
refs: stateHelper.buildRef ? [stateHelper.buildRef] : []
|
|
||||||
});
|
|
||||||
await reportBuildCompleted(exportRes);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const buildkitdLog = fs.readFileSync('buildkitd.log', 'utf8');
|
const buildkitdLog = fs.readFileSync('buildkitd.log', 'utf8');
|
||||||
|
@ -738,7 +754,7 @@ actionsToolkit.run(
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning(`Failed to read buildkitd.log: ${error.message}`);
|
core.warning(`Failed to read buildkitd.log: ${error.message}`);
|
||||||
}
|
}
|
||||||
await reportBuildFailed();
|
await reportBuildFailed(builderInfo.buildId, buildDurationSeconds);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning(`Error during Blacksmith builder shutdown: ${error.message}`);
|
core.warning(`Error during Blacksmith builder shutdown: ${error.message}`);
|
||||||
|
@ -751,40 +767,6 @@ actionsToolkit.run(
|
||||||
},
|
},
|
||||||
// post
|
// post
|
||||||
async () => {
|
async () => {
|
||||||
if (stateHelper.isSummarySupported) {
|
|
||||||
await core.group(`Generating build summary`, async () => {
|
|
||||||
try {
|
|
||||||
const recordUploadEnabled = buildRecordUploadEnabled();
|
|
||||||
let recordRetentionDays: number | undefined;
|
|
||||||
if (recordUploadEnabled) {
|
|
||||||
recordRetentionDays = buildRecordRetentionDays();
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildxHistory = new BuildxHistory();
|
|
||||||
const exportRes = await buildxHistory.export({
|
|
||||||
refs: stateHelper.buildRef ? [stateHelper.buildRef] : []
|
|
||||||
});
|
|
||||||
core.info(`Build record written to ${exportRes.dockerbuildFilename} (${Util.formatFileSize(exportRes.dockerbuildSize)})`);
|
|
||||||
|
|
||||||
let uploadRes: UploadArtifactResponse | undefined;
|
|
||||||
if (recordUploadEnabled) {
|
|
||||||
uploadRes = await GitHub.uploadArtifact({
|
|
||||||
filename: exportRes.dockerbuildFilename,
|
|
||||||
mimeType: 'application/gzip',
|
|
||||||
retentionDays: recordRetentionDays
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await GitHub.writeBuildSummary({
|
|
||||||
exportRes: exportRes,
|
|
||||||
uploadRes: uploadRes,
|
|
||||||
inputs: stateHelper.inputs
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
core.warning(e.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (stateHelper.tmpDir.length > 0) {
|
if (stateHelper.tmpDir.length > 0) {
|
||||||
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
|
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
|
||||||
fs.rmSync(stateHelper.tmpDir, {recursive: true});
|
fs.rmSync(stateHelper.tmpDir, {recursive: true});
|
||||||
|
|
Loading…
Reference in New Issue