2020-09-02 08:07:11 +00:00
|
|
|
import * as fs from 'fs';
|
2023-09-08 13:28:08 +00:00
|
|
|
import * as path from 'path';
|
2020-09-02 08:07:11 +00:00
|
|
|
import * as stateHelper from './state-helper';
|
2020-08-15 22:36:41 +00:00
|
|
|
import * as core from '@actions/core';
|
2023-02-20 10:11:15 +00:00
|
|
|
import * as actionsToolkit from '@docker/actions-toolkit';
|
2024-04-26 09:20:49 +00:00
|
|
|
|
2024-11-18 15:59:50 +00:00
|
|
|
import {Buildx} from '@docker/actions-toolkit/lib/buildx/buildx';
|
|
|
|
import {History as BuildxHistory} from '@docker/actions-toolkit/lib/buildx/history';
|
|
|
|
import {Context} from '@docker/actions-toolkit/lib/context';
|
|
|
|
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
|
|
|
|
import {Exec} from '@docker/actions-toolkit/lib/exec';
|
|
|
|
import {GitHub} from '@docker/actions-toolkit/lib/github';
|
|
|
|
import {Toolkit} from '@docker/actions-toolkit/lib/toolkit';
|
|
|
|
import {Util} from '@docker/actions-toolkit/lib/util';
|
|
|
|
|
|
|
|
import {BuilderInfo} from '@docker/actions-toolkit/lib/types/buildx/builder';
|
|
|
|
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';
|
2020-08-15 22:36:41 +00:00
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
import * as context from './context';
|
2024-11-18 15:59:50 +00:00
|
|
|
import {promisify} from 'util';
|
|
|
|
import {exec} from 'child_process';
|
2024-10-31 10:18:44 +00:00
|
|
|
import * as TOML from '@iarna/toml';
|
2022-04-28 07:31:47 +00:00
|
|
|
|
2024-09-13 00:11:11 +00:00
|
|
|
const buildxVersion = 'v0.17.0';
|
2024-10-31 10:18:44 +00:00
|
|
|
const mountPoint = '/var/lib/buildkit';
|
|
|
|
const device = '/dev/vdb';
|
|
|
|
const execAsync = promisify(exec);
|
2024-09-12 00:08:08 +00:00
|
|
|
|
2024-11-19 06:18:53 +00:00
|
|
|
// Returns a client for the sticky disk manager on the agent on this host
|
|
|
|
async function getBlacksmithAgentClient(): Promise<AxiosInstance> {
|
2024-11-18 15:59:50 +00:00
|
|
|
const stickyDiskMgrUrl = 'http://192.168.127.1:5556';
|
2024-09-12 00:08:08 +00:00
|
|
|
return axios.create({
|
2024-11-20 04:19:59 +00:00
|
|
|
baseURL: stickyDiskMgrUrl
|
2024-09-12 00:08:08 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-11-19 05:27:39 +00:00
|
|
|
// Reports a successful build to the local sticky disk manager
|
2024-09-12 00:54:27 +00:00
|
|
|
async function reportBuildCompleted() {
|
2024-11-19 05:38:24 +00:00
|
|
|
if (!stateHelper.blacksmithDockerBuildId) {
|
|
|
|
core.warning('No docker build ID found, skipping build completion report');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
try {
|
2024-11-19 06:18:53 +00:00
|
|
|
const client = await getBlacksmithAgentClient();
|
2024-10-31 10:18:44 +00:00
|
|
|
const formData = new FormData();
|
|
|
|
formData.append('shouldCommit', 'true');
|
2024-11-04 11:14:01 +00:00
|
|
|
formData.append('vmID', process.env.VM_ID || '');
|
2024-10-31 10:18:44 +00:00
|
|
|
const retryCondition = (error: AxiosError) => {
|
2024-11-04 11:14:01 +00:00
|
|
|
return error.response?.status ? error.response.status > 500 : false;
|
2024-10-31 10:18:44 +00:00
|
|
|
};
|
2024-09-12 00:54:27 +00:00
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
await postWithRetry(client, '/stickydisks', formData, retryCondition);
|
2024-11-19 05:27:39 +00:00
|
|
|
|
|
|
|
// Report success to Blacksmith API
|
|
|
|
const requestOptions = {
|
|
|
|
docker_build_id: stateHelper.blacksmithDockerBuildId,
|
2024-11-19 07:28:40 +00:00
|
|
|
conclusion: 'successful',
|
|
|
|
runtime_seconds: stateHelper.dockerBuildDurationSeconds
|
2024-11-19 05:27:39 +00:00
|
|
|
};
|
|
|
|
|
2024-11-19 06:35:40 +00:00
|
|
|
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${stateHelper.blacksmithDockerBuildId}`, requestOptions, retryCondition);
|
2024-10-31 10:18:44 +00:00
|
|
|
return;
|
|
|
|
} catch (error) {
|
2024-11-04 21:24:12 +00:00
|
|
|
core.warning('Error reporting build completed:', error);
|
2024-11-19 05:27:39 +00:00
|
|
|
// We don't want to fail the build if this fails so we swallow the error
|
2024-09-25 03:35:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 05:27:39 +00:00
|
|
|
// Reports a failed build to both the local sticky disk manager and the Blacksmith API
|
2024-09-12 00:54:27 +00:00
|
|
|
async function reportBuildFailed() {
|
2024-11-19 05:38:24 +00:00
|
|
|
if (!stateHelper.blacksmithDockerBuildId) {
|
|
|
|
core.warning('No docker build ID found, skipping build completion report');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-12 00:54:27 +00:00
|
|
|
try {
|
2024-11-19 06:18:53 +00:00
|
|
|
const client = await getBlacksmithAgentClient();
|
2024-10-31 10:18:44 +00:00
|
|
|
const formData = new FormData();
|
|
|
|
formData.append('shouldCommit', 'false');
|
2024-11-04 11:14:01 +00:00
|
|
|
formData.append('vmID', process.env.VM_ID || '');
|
2024-10-31 10:18:44 +00:00
|
|
|
const retryCondition = (error: AxiosError) => {
|
2024-11-04 11:14:01 +00:00
|
|
|
return error.response?.status ? error.response.status > 500 : false;
|
2024-10-31 10:18:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
await postWithRetry(client, '/stickydisks', formData, retryCondition);
|
2024-11-19 05:27:39 +00:00
|
|
|
|
|
|
|
// Report failure to Blacksmith API
|
|
|
|
const requestOptions = {
|
|
|
|
docker_build_id: stateHelper.blacksmithDockerBuildId,
|
2024-11-19 07:28:40 +00:00
|
|
|
conclusion: 'failed',
|
|
|
|
runtime_seconds: stateHelper.dockerBuildDurationSeconds
|
2024-11-19 05:27:39 +00:00
|
|
|
};
|
|
|
|
|
2024-11-19 06:35:40 +00:00
|
|
|
await postWithRetryToBlacksmithAPI(`/stickydisks/dockerbuilds/${stateHelper.blacksmithDockerBuildId}`, requestOptions, retryCondition);
|
2024-10-31 10:18:44 +00:00
|
|
|
return;
|
2024-09-12 00:54:27 +00:00
|
|
|
} catch (error) {
|
2024-11-04 21:24:12 +00:00
|
|
|
core.warning('Error reporting build failed:', error);
|
2024-11-19 05:27:39 +00:00
|
|
|
// We don't want to fail the build if this fails so we swallow the error
|
2024-09-12 00:54:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 06:35:40 +00:00
|
|
|
async function postWithRetryToBlacksmithAPI(url: string, requestBody: unknown, retryCondition: (error: AxiosError) => boolean): Promise<AxiosResponse> {
|
2024-11-16 16:46:14 +00:00
|
|
|
const maxRetries = 5;
|
|
|
|
const retryDelay = 100;
|
2024-11-19 06:35:40 +00:00
|
|
|
const apiUrl = process.env.PETNAME?.includes('staging') ? 'https://stagingapi.blacksmith.sh' : 'https://api.blacksmith.sh';
|
2024-11-16 16:46:14 +00:00
|
|
|
|
|
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
|
|
try {
|
2024-11-19 06:35:40 +00:00
|
|
|
core.debug(`Request headers: Authorization: Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}, X-Github-Repo-Name: ${process.env.GITHUB_REPO_NAME || ''}`);
|
2024-11-19 06:18:53 +00:00
|
|
|
|
2024-11-19 06:55:23 +00:00
|
|
|
const fullUrl = `${apiUrl}${url}`;
|
|
|
|
core.debug(`Making request to full URL: ${fullUrl}`);
|
|
|
|
|
|
|
|
return await axios.post(fullUrl, requestBody, {
|
2024-11-19 06:35:40 +00:00
|
|
|
headers: {
|
|
|
|
Authorization: `Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}`,
|
2024-11-19 06:55:23 +00:00
|
|
|
'X-Github-Repo-Name': process.env.GITHUB_REPO_NAME || '',
|
|
|
|
'Content-Type': 'application/json'
|
2024-11-19 06:35:40 +00:00
|
|
|
}
|
|
|
|
});
|
2024-11-16 16:46:14 +00:00
|
|
|
} catch (error) {
|
|
|
|
if (attempt === maxRetries || !retryCondition(error as AxiosError)) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
core.warning(`Request failed, retrying (${attempt}/${maxRetries})...`);
|
|
|
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error('Max retries reached');
|
|
|
|
}
|
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
async function postWithRetry(client: AxiosInstance, url: string, formData: FormData, retryCondition: (error: AxiosError) => boolean): Promise<AxiosResponse> {
|
2024-10-02 20:57:18 +00:00
|
|
|
const maxRetries = 5;
|
|
|
|
const retryDelay = 100;
|
|
|
|
|
|
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
|
|
try {
|
2024-10-31 10:18:44 +00:00
|
|
|
return await client.post(url, formData, {
|
|
|
|
headers: {
|
2024-11-20 04:19:59 +00:00
|
|
|
Authorization: `Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}`,
|
|
|
|
'X-Github-Repo-Name': process.env.GITHUB_REPO_NAME || '',
|
2024-10-31 10:18:44 +00:00
|
|
|
'Content-Type': 'multipart/form-data'
|
|
|
|
}
|
|
|
|
});
|
2024-10-02 20:57:18 +00:00
|
|
|
} catch (error) {
|
|
|
|
if (attempt === maxRetries || !retryCondition(error as AxiosError)) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
core.warning(`Request failed, retrying (${attempt}/${maxRetries})...`);
|
|
|
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error('Max retries reached');
|
|
|
|
}
|
|
|
|
|
2024-11-18 15:59:50 +00:00
|
|
|
async function getWithRetry(client: AxiosInstance, url: string, formData: FormData | null, retryCondition: (error: AxiosError) => boolean, options?: {signal?: AbortSignal}): Promise<AxiosResponse> {
|
2024-10-02 20:57:18 +00:00
|
|
|
const maxRetries = 5;
|
|
|
|
const retryDelay = 100;
|
|
|
|
|
|
|
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
|
|
try {
|
2024-10-31 10:18:44 +00:00
|
|
|
if (formData) {
|
|
|
|
return await client.get(url, {
|
|
|
|
data: formData,
|
|
|
|
headers: {
|
2024-11-20 04:19:59 +00:00
|
|
|
Authorization: `Bearer ${process.env.BLACKSMITH_STICKYDISK_TOKEN}`,
|
|
|
|
'X-Github-Repo-Name': process.env.GITHUB_REPO_NAME || '',
|
2024-10-31 10:18:44 +00:00
|
|
|
'Content-Type': 'multipart/form-data'
|
|
|
|
},
|
|
|
|
signal: options?.signal
|
|
|
|
});
|
|
|
|
}
|
2024-11-18 15:59:50 +00:00
|
|
|
return await client.get(url, {signal: options?.signal});
|
2024-10-02 20:57:18 +00:00
|
|
|
} catch (error) {
|
|
|
|
if (attempt === maxRetries || !retryCondition(error as AxiosError)) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
core.warning(`Request failed, retrying (${attempt}/${maxRetries})...`);
|
|
|
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error('Max retries reached');
|
|
|
|
}
|
|
|
|
|
2024-11-18 15:59:50 +00:00
|
|
|
async function getStickyDisk(dockerfilePath: string, retryCondition: (error: AxiosError) => boolean, options?: {signal?: AbortSignal}): Promise<unknown> {
|
2024-11-19 06:18:53 +00:00
|
|
|
const client = await getBlacksmithAgentClient();
|
2024-10-31 10:18:44 +00:00
|
|
|
const formData = new FormData();
|
|
|
|
formData.append('stickyDiskKey', dockerfilePath);
|
|
|
|
formData.append('region', process.env.BLACKSMITH_REGION || 'eu-central');
|
|
|
|
formData.append('installationModelID', process.env.BLACKSMITH_INSTALLATION_MODEL_ID || '');
|
2024-11-04 14:56:41 +00:00
|
|
|
formData.append('vmID', process.env.VM_ID || '');
|
2024-11-08 03:05:54 +00:00
|
|
|
core.debug(`Getting sticky disk for ${dockerfilePath}`);
|
|
|
|
core.debug('FormData contents:');
|
2024-11-07 20:30:58 +00:00
|
|
|
for (const pair of formData.entries()) {
|
2024-11-08 03:05:54 +00:00
|
|
|
core.debug(`${pair[0]}: ${pair[1]}`);
|
2024-11-07 20:30:58 +00:00
|
|
|
}
|
2024-10-31 10:18:44 +00:00
|
|
|
const response = await getWithRetry(client, '/stickydisks', formData, retryCondition, options);
|
|
|
|
return response.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getDiskSize(device: string): Promise<number> {
|
|
|
|
try {
|
2024-11-18 15:59:50 +00:00
|
|
|
const {stdout} = await execAsync(`sudo lsblk -b -n -o SIZE ${device}`);
|
2024-10-31 10:18:44 +00:00
|
|
|
const sizeInBytes = parseInt(stdout.trim(), 10);
|
|
|
|
if (isNaN(sizeInBytes)) {
|
|
|
|
throw new Error('Failed to parse disk size');
|
|
|
|
}
|
|
|
|
return sizeInBytes;
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`Error getting disk size: ${error.message}`);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeBuildkitdTomlFile(parallelism: number): Promise<void> {
|
|
|
|
const diskSize = await getDiskSize(device);
|
|
|
|
const jsonConfig: TOML.JsonMap = {
|
2024-11-18 15:59:50 +00:00
|
|
|
root: '/var/lib/buildkit',
|
|
|
|
grpc: {
|
|
|
|
address: ['unix:///run/buildkit/buildkitd.sock']
|
2024-10-31 10:18:44 +00:00
|
|
|
},
|
2024-11-18 15:59:50 +00:00
|
|
|
worker: {
|
|
|
|
oci: {
|
|
|
|
enabled: true,
|
|
|
|
gc: true,
|
|
|
|
gckeepstorage: diskSize.toString(),
|
|
|
|
'max-parallelism': parallelism,
|
|
|
|
snapshotter: 'overlayfs',
|
|
|
|
gcpolicy: [
|
2024-10-31 10:18:44 +00:00
|
|
|
{
|
2024-11-18 15:59:50 +00:00
|
|
|
all: true,
|
|
|
|
keepDuration: 1209600
|
2024-10-31 10:18:44 +00:00
|
|
|
},
|
|
|
|
{
|
2024-11-18 15:59:50 +00:00
|
|
|
all: true,
|
|
|
|
keepBytes: diskSize.toString()
|
2024-10-31 10:18:44 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
2024-11-18 15:59:50 +00:00
|
|
|
containerd: {
|
|
|
|
enabled: false
|
2024-10-31 10:18:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const tomlString = TOML.stringify(jsonConfig);
|
|
|
|
|
|
|
|
try {
|
|
|
|
await fs.promises.writeFile('buildkitd.toml', tomlString);
|
|
|
|
core.debug(`TOML configuration is ${tomlString}`);
|
|
|
|
} catch (err) {
|
|
|
|
core.warning('error writing TOML configuration:', err);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function startBuildkitd(parallelism: number): Promise<string> {
|
|
|
|
try {
|
|
|
|
await writeBuildkitdTomlFile(parallelism);
|
|
|
|
await execAsync('sudo mkdir -p /run/buildkit');
|
|
|
|
await execAsync('sudo chmod 755 /run/buildkit');
|
2024-11-18 15:59:50 +00:00
|
|
|
const addr = 'unix:///run/buildkit/buildkitd.sock';
|
|
|
|
const {stdout: startStdout, stderr: startStderr} = await execAsync(
|
|
|
|
`sudo nohup buildkitd --addr ${addr} --allow-insecure-entitlement security.insecure --config=buildkitd.toml --allow-insecure-entitlement network.host > buildkitd.log 2>&1 &`
|
2024-10-31 10:18:44 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (startStderr) {
|
|
|
|
throw new Error(`error starting buildkitd service: ${startStderr}`);
|
|
|
|
}
|
|
|
|
core.debug(`buildkitd daemon started successfully ${startStdout}`);
|
|
|
|
|
2024-11-18 15:59:50 +00:00
|
|
|
const {stderr} = await execAsync(`pgrep -f buildkitd`);
|
2024-10-31 10:18:44 +00:00
|
|
|
if (stderr) {
|
|
|
|
throw new Error(`error finding buildkitd PID: ${stderr}`);
|
|
|
|
}
|
|
|
|
return addr;
|
|
|
|
} catch (error) {
|
|
|
|
core.error('failed to start buildkitd daemon:', error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to gracefully shut down the buildkitd process
|
|
|
|
async function shutdownBuildkitd(): Promise<void> {
|
|
|
|
try {
|
|
|
|
await execAsync(`sudo pkill -TERM buildkitd`);
|
|
|
|
} catch (error) {
|
|
|
|
core.error('error shutting down buildkitd process:', error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function to get the number of available CPUs
|
|
|
|
async function getNumCPUs(): Promise<number> {
|
|
|
|
try {
|
2024-11-18 15:59:50 +00:00
|
|
|
const {stdout} = await execAsync('sudo nproc');
|
2024-10-31 10:18:44 +00:00
|
|
|
return parseInt(stdout.trim());
|
|
|
|
} catch (error) {
|
|
|
|
core.warning('Failed to get CPU count, defaulting to 1:', error);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-22 18:12:02 +00:00
|
|
|
async function maybeFormatBlockDevice(device: string): Promise<string> {
|
|
|
|
try {
|
|
|
|
// Check if device is formatted with ext4
|
|
|
|
const {stdout} = await execAsync(`sudo blkid -o value -s TYPE ${device}`);
|
|
|
|
if (stdout.trim() === 'ext4') {
|
|
|
|
core.debug(`Device ${device} is already formatted with ext4`);
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format device with ext4
|
|
|
|
core.debug(`Formatting device ${device} with ext4`);
|
|
|
|
await execAsync(`sudo mkfs.ext4 -m0 -Enodiscard,lazy_itable_init=1,lazy_journal_init=1 -F ${device}`);
|
|
|
|
core.debug(`Successfully formatted ${device} with ext4`);
|
|
|
|
return device;
|
|
|
|
} catch (error) {
|
|
|
|
core.error(`Failed to format device ${device}:`, error);
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-19 05:27:39 +00:00
|
|
|
// reportBuild reports the build to the Blacksmith API and returns the build ID
|
|
|
|
async function reportBuild(dockerfilePath: string) {
|
2024-11-19 05:38:24 +00:00
|
|
|
try {
|
2024-11-19 06:35:40 +00:00
|
|
|
const requestBody = {
|
2024-11-19 05:38:24 +00:00
|
|
|
dockerfile_path: dockerfilePath,
|
|
|
|
repo_name: process.env.GITHUB_REPO_NAME || '',
|
|
|
|
region: process.env.BLACKSMITH_REGION || 'eu-central',
|
|
|
|
arch: process.env.PETNAME?.includes('arm') ? 'arm64' : 'amd64',
|
|
|
|
git_sha: process.env.GITHUB_SHA || '',
|
|
|
|
vm_id: process.env.VM_ID || '',
|
|
|
|
git_branch: process.env.GITHUB_REF_NAME || ''
|
|
|
|
};
|
2024-11-19 06:35:40 +00:00
|
|
|
core.debug(`Reporting build with options: ${JSON.stringify(requestBody, null, 2)}`);
|
2024-11-19 05:38:24 +00:00
|
|
|
const retryCondition = (error: AxiosError) => {
|
|
|
|
return error.response?.status ? error.response.status > 500 : false;
|
|
|
|
};
|
2024-11-19 06:35:40 +00:00
|
|
|
const response = await postWithRetryToBlacksmithAPI('/stickydisks/dockerbuilds', requestBody, retryCondition);
|
2024-11-19 05:38:24 +00:00
|
|
|
stateHelper.setBlacksmithDockerBuildId(response.data.docker_build_id);
|
|
|
|
return response.data;
|
|
|
|
} catch (error) {
|
2024-11-19 06:37:29 +00:00
|
|
|
const statusCode = (error as AxiosError)?.response?.status;
|
|
|
|
core.warning(`Error reporting build to Blacksmith API (status: ${statusCode || 'unknown'}):`);
|
|
|
|
core.warning(error);
|
2024-11-19 05:38:24 +00:00
|
|
|
return null;
|
|
|
|
}
|
2024-11-19 05:27:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async function reportBuilderCreationFailed(stickydiskKey: string) {
|
2024-11-16 16:46:14 +00:00
|
|
|
const requestOptions = {
|
2024-11-16 18:02:17 +00:00
|
|
|
stickydisk_key: stickydiskKey,
|
|
|
|
repo_name: process.env.GITHUB_REPO_NAME || '',
|
2024-11-16 16:46:14 +00:00
|
|
|
region: process.env.BLACKSMITH_REGION || 'eu-central',
|
|
|
|
arch: process.env.PETNAME?.includes('arm') ? 'arm64' : 'amd64'
|
|
|
|
};
|
2024-11-13 02:23:57 +00:00
|
|
|
const retryCondition = (error: AxiosError) => {
|
|
|
|
return error.response?.status ? error.response.status > 500 : false;
|
|
|
|
};
|
2024-11-19 06:35:40 +00:00
|
|
|
const response = await postWithRetryToBlacksmithAPI('/stickydisks/report-failed', requestOptions, retryCondition);
|
2024-11-13 02:23:57 +00:00
|
|
|
return response.data;
|
|
|
|
}
|
|
|
|
|
2024-11-18 16:19:23 +00:00
|
|
|
// getBuilderAddr mounts a sticky disk for the entity, sets up buildkitd on top of it
|
|
|
|
// and returns the address to the builder.
|
2024-09-12 00:08:08 +00:00
|
|
|
// If it is unable to do so because of a timeout or an error it returns null.
|
2024-11-18 16:19:23 +00:00
|
|
|
async function getBuilderAddr(inputs: context.Inputs, dockerfilePath: string): Promise<string | null> {
|
2024-09-12 00:08:08 +00:00
|
|
|
try {
|
2024-10-02 20:57:18 +00:00
|
|
|
const retryCondition = (error: AxiosError) => (error.response?.status ? error.response.status >= 500 : error.code === 'ECONNRESET');
|
2024-10-31 10:18:44 +00:00
|
|
|
const controller = new AbortController();
|
|
|
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
2024-10-02 20:57:18 +00:00
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
try {
|
2024-11-18 15:59:50 +00:00
|
|
|
await getStickyDisk(dockerfilePath, retryCondition, {signal: controller.signal});
|
2024-10-31 10:18:44 +00:00
|
|
|
clearTimeout(timeoutId);
|
2024-11-22 18:12:02 +00:00
|
|
|
await maybeFormatBlockDevice(device);
|
2024-11-19 05:27:39 +00:00
|
|
|
await reportBuild(dockerfilePath);
|
2024-10-31 10:18:44 +00:00
|
|
|
await execAsync(`sudo mkdir -p ${mountPoint}`);
|
|
|
|
await execAsync(`sudo mount ${device} ${mountPoint}`);
|
|
|
|
core.debug(`${device} has been mounted to ${mountPoint}`);
|
|
|
|
} catch (error) {
|
|
|
|
if (error.name === 'AbortError') {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
throw error;
|
|
|
|
}
|
2024-09-12 00:08:08 +00:00
|
|
|
|
2024-11-08 03:05:54 +00:00
|
|
|
core.debug('Successfully obtained sticky disk, proceeding to start buildkitd');
|
2024-11-07 20:27:48 +00:00
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
// Start buildkitd.
|
|
|
|
const parallelism = await getNumCPUs();
|
2024-11-18 15:59:50 +00:00
|
|
|
const buildkitdAddr = await startBuildkitd(parallelism);
|
2024-10-31 10:18:44 +00:00
|
|
|
core.debug(`buildkitd daemon started at addr ${buildkitdAddr}`);
|
|
|
|
// Change permissions on the buildkitd socket to allow non-root access
|
2024-09-12 00:08:08 +00:00
|
|
|
const startTime = Date.now();
|
2024-10-31 10:18:44 +00:00
|
|
|
const timeout = 3000; // 3 seconds in milliseconds
|
2024-10-02 20:57:18 +00:00
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
while (Date.now() - startTime < timeout) {
|
|
|
|
if (fs.existsSync('/run/buildkit/buildkitd.sock')) {
|
|
|
|
// Change permissions on the buildkitd socket to allow non-root access
|
|
|
|
await execAsync(`sudo chmod 666 /run/buildkit/buildkitd.sock`);
|
|
|
|
break;
|
2024-09-12 00:08:08 +00:00
|
|
|
}
|
2024-10-31 10:18:44 +00:00
|
|
|
await new Promise(resolve => setTimeout(resolve, 100)); // Poll every 100ms
|
2024-09-12 00:08:08 +00:00
|
|
|
}
|
|
|
|
|
2024-10-31 10:18:44 +00:00
|
|
|
if (!fs.existsSync('/run/buildkit/buildkitd.sock')) {
|
|
|
|
throw new Error('buildkitd socket not found after 3s timeout');
|
|
|
|
}
|
|
|
|
return buildkitdAddr;
|
2024-09-12 00:08:08 +00:00
|
|
|
} catch (error) {
|
2024-10-02 20:57:18 +00:00
|
|
|
if ((error as AxiosError).response && (error as AxiosError).response!.status === 404) {
|
2024-09-21 00:21:04 +00:00
|
|
|
if (!inputs.nofallback) {
|
|
|
|
core.warning('No builder instances were available, falling back to a local build');
|
|
|
|
}
|
2024-09-17 02:26:04 +00:00
|
|
|
} else {
|
2024-10-02 20:57:18 +00:00
|
|
|
core.warning(`Error in getBuildkitdAddr: ${(error as Error).message}`);
|
2024-09-17 02:26:04 +00:00
|
|
|
}
|
2024-09-12 00:08:08 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function setupBuildx(version: string, toolkit: Toolkit): Promise<void> {
|
|
|
|
let toolPath;
|
|
|
|
const standalone = await toolkit.buildx.isStandalone();
|
|
|
|
|
|
|
|
if (!(await toolkit.buildx.isAvailable()) || version) {
|
|
|
|
await core.group(`Download buildx from GitHub Releases`, async () => {
|
|
|
|
toolPath = await toolkit.buildxInstall.download(version || 'latest', true);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toolPath) {
|
|
|
|
await core.group(`Install buildx`, async () => {
|
|
|
|
if (standalone) {
|
|
|
|
await toolkit.buildxInstall.installStandalone(toolPath);
|
|
|
|
} else {
|
|
|
|
await toolkit.buildxInstall.installPlugin(toolPath);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
await core.group(`Buildx version`, async () => {
|
|
|
|
await toolkit.buildx.printVersion();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
actionsToolkit.run(
|
|
|
|
// main
|
|
|
|
async () => {
|
2024-05-02 11:49:01 +00:00
|
|
|
const startedTime = new Date();
|
2023-04-16 23:32:21 +00:00
|
|
|
const inputs: context.Inputs = await context.getInputs();
|
2024-05-02 11:49:01 +00:00
|
|
|
stateHelper.setInputs(inputs);
|
2024-03-06 13:20:33 +00:00
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
const toolkit = new Toolkit();
|
2022-04-28 07:31:47 +00:00
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
await core.group(`GitHub Actions runtime token ACs`, async () => {
|
|
|
|
try {
|
|
|
|
await GitHub.printActionsRuntimeTokenACs();
|
|
|
|
} catch (e) {
|
|
|
|
core.warning(e.message);
|
2023-01-12 18:44:15 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
await core.group(`Docker info`, async () => {
|
|
|
|
try {
|
|
|
|
await Docker.printVersion();
|
|
|
|
await Docker.printInfo();
|
|
|
|
} catch (e) {
|
|
|
|
core.info(e.message);
|
|
|
|
}
|
|
|
|
});
|
2020-08-15 22:36:41 +00:00
|
|
|
|
2024-09-12 00:08:08 +00:00
|
|
|
await core.group(`Setup buildx`, async () => {
|
|
|
|
await setupBuildx(buildxVersion, toolkit);
|
|
|
|
|
|
|
|
if (!(await toolkit.buildx.isAvailable())) {
|
|
|
|
core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-11-19 05:27:39 +00:00
|
|
|
let localBuilderAddr: string | null = null;
|
|
|
|
await core.group(`Starting Blacksmith builder`, async () => {
|
2024-11-13 02:23:57 +00:00
|
|
|
const dockerfilePath = context.getDockerfilePath(inputs);
|
|
|
|
if (!dockerfilePath) {
|
|
|
|
if (inputs.nofallback) {
|
2024-11-19 05:27:39 +00:00
|
|
|
await reportBuilderCreationFailed('');
|
2024-11-13 02:23:57 +00:00
|
|
|
throw Error('Failed to resolve dockerfile path, and fallback is disabled');
|
|
|
|
} else {
|
|
|
|
core.warning('Failed to resolve dockerfile path, and fallback is enabled. Falling back to a local build.');
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (dockerfilePath && dockerfilePath.length > 0) {
|
|
|
|
core.debug(`Using dockerfile path: ${dockerfilePath}`);
|
|
|
|
}
|
2024-11-19 05:27:39 +00:00
|
|
|
localBuilderAddr = await getBuilderAddr(inputs, dockerfilePath);
|
|
|
|
if (!localBuilderAddr) {
|
|
|
|
await reportBuilderCreationFailed(dockerfilePath);
|
2024-09-21 00:21:04 +00:00
|
|
|
if (inputs.nofallback) {
|
2024-09-21 00:47:13 +00:00
|
|
|
throw Error('Failed to obtain Blacksmith builder. Failing the build');
|
2024-09-21 00:21:04 +00:00
|
|
|
} else {
|
2024-11-19 05:27:39 +00:00
|
|
|
core.warning('Failed to obtain Blacksmith builder address. Falling back to a local build.');
|
2024-09-21 00:21:04 +00:00
|
|
|
}
|
2024-09-12 00:08:08 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-11-19 05:27:39 +00:00
|
|
|
if (localBuilderAddr) {
|
|
|
|
await core.group(`Creating a builder instance`, async () => {
|
2024-09-13 00:11:11 +00:00
|
|
|
const name = `blacksmith`;
|
2024-11-19 05:27:39 +00:00
|
|
|
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, localBuilderAddr!));
|
2024-09-12 00:08:08 +00:00
|
|
|
core.info(`Creating builder with command: ${createCmd.command}`);
|
|
|
|
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 {
|
|
|
|
// If we failed to obtain the address, let's check if we have an already configured builder.
|
|
|
|
await core.group(`Checking for configured builder`, async () => {
|
|
|
|
try {
|
|
|
|
const builder = await toolkit.builder.inspect();
|
|
|
|
if (builder) {
|
2024-09-13 19:26:02 +00:00
|
|
|
core.info(`Found configured builder: ${builder.name}`);
|
2024-09-12 00:08:08 +00:00
|
|
|
} else {
|
2024-09-13 19:26:02 +00:00
|
|
|
// 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}`);
|
|
|
|
}
|
2024-09-12 00:08:08 +00:00
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
core.setFailed(`Error configuring builder: ${error.message}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-08 13:28:08 +00:00
|
|
|
await core.group(`Proxy configuration`, async () => {
|
|
|
|
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) {
|
2023-06-13 09:46:07 +00:00
|
|
|
for (const host in dockerConfig.proxies) {
|
|
|
|
let prefix = '';
|
2023-09-07 10:21:01 +00:00
|
|
|
if (Object.keys(dockerConfig.proxies).length > 1) {
|
2023-06-13 09:46:07 +00:00
|
|
|
prefix = ' ';
|
|
|
|
core.info(host);
|
|
|
|
}
|
|
|
|
for (const key in dockerConfig.proxies[host]) {
|
|
|
|
core.info(`${prefix}${key}: ${dockerConfig.proxies[host][key]}`);
|
|
|
|
}
|
|
|
|
}
|
2023-09-08 13:28:08 +00:00
|
|
|
} else if (!dockerConfigMalformed) {
|
|
|
|
core.info('No proxy configuration found');
|
|
|
|
}
|
|
|
|
});
|
2023-06-13 09:46:07 +00:00
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
stateHelper.setTmpDir(Context.tmpDir());
|
|
|
|
|
2024-06-27 09:26:35 +00:00
|
|
|
let builder: BuilderInfo;
|
2024-05-29 10:52:50 +00:00
|
|
|
await core.group(`Builder info`, async () => {
|
2024-11-12 00:16:54 +00:00
|
|
|
builder = await toolkit.builder.inspect();
|
2024-05-29 10:52:50 +00:00
|
|
|
core.info(JSON.stringify(builder, null, 2));
|
|
|
|
});
|
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
const args: string[] = await context.getArgs(inputs, toolkit);
|
2024-10-02 20:57:18 +00:00
|
|
|
args.push('--debug');
|
2024-03-06 13:20:33 +00:00
|
|
|
core.debug(`context.getArgs: ${JSON.stringify(args)}`);
|
|
|
|
|
2023-02-20 10:11:15 +00:00
|
|
|
const buildCmd = await toolkit.buildx.getCommand(args);
|
2024-10-02 20:57:18 +00:00
|
|
|
|
2024-03-06 13:20:33 +00:00
|
|
|
core.debug(`buildCmd.command: ${buildCmd.command}`);
|
|
|
|
core.debug(`buildCmd.args: ${JSON.stringify(buildCmd.args)}`);
|
|
|
|
|
2024-05-02 11:49:01 +00:00
|
|
|
let err: Error | undefined;
|
2024-11-19 07:28:40 +00:00
|
|
|
const buildStartTime = Date.now();
|
2023-02-20 10:11:15 +00:00
|
|
|
await Exec.getExecOutput(buildCmd.command, buildCmd.args, {
|
2024-07-30 15:48:59 +00:00
|
|
|
ignoreReturnCode: true,
|
|
|
|
env: Object.assign({}, process.env, {
|
|
|
|
BUILDX_METADATA_WARNINGS: 'true'
|
|
|
|
}) as {
|
|
|
|
[key: string]: string;
|
|
|
|
}
|
2023-02-20 10:11:15 +00:00
|
|
|
}).then(res => {
|
|
|
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
2024-05-02 11:49:01 +00:00
|
|
|
err = Error(`buildx failed with: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
2023-02-20 10:11:15 +00:00
|
|
|
}
|
2024-11-19 07:28:40 +00:00
|
|
|
const buildDurationSeconds = Math.round((Date.now() - buildStartTime) / 1000).toString();
|
|
|
|
stateHelper.setDockerBuildDurationSeconds(buildDurationSeconds);
|
2023-02-20 10:11:15 +00:00
|
|
|
});
|
2020-08-17 20:18:15 +00:00
|
|
|
|
2024-05-14 12:11:32 +00:00
|
|
|
const imageID = toolkit.buildxBuild.resolveImageID();
|
|
|
|
const metadata = toolkit.buildxBuild.resolveMetadata();
|
2024-07-30 15:48:59 +00:00
|
|
|
const digest = toolkit.buildxBuild.resolveDigest(metadata);
|
2022-02-09 10:32:35 +00:00
|
|
|
if (imageID) {
|
2022-03-14 18:30:50 +00:00
|
|
|
await core.group(`ImageID`, async () => {
|
2022-02-09 10:32:35 +00:00
|
|
|
core.info(imageID);
|
2022-10-12 04:56:31 +00:00
|
|
|
core.setOutput('imageid', imageID);
|
2022-03-14 18:30:50 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (digest) {
|
|
|
|
await core.group(`Digest`, async () => {
|
|
|
|
core.info(digest);
|
2022-10-12 04:56:31 +00:00
|
|
|
core.setOutput('digest', digest);
|
2022-02-09 10:32:35 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
if (metadata) {
|
2022-03-14 18:30:50 +00:00
|
|
|
await core.group(`Metadata`, async () => {
|
2024-04-26 09:20:49 +00:00
|
|
|
const metadatadt = JSON.stringify(metadata, null, 2);
|
|
|
|
core.info(metadatadt);
|
|
|
|
core.setOutput('metadata', metadatadt);
|
2022-02-09 10:32:35 +00:00
|
|
|
});
|
|
|
|
}
|
2024-06-27 09:26:35 +00:00
|
|
|
|
2024-07-30 15:48:59 +00:00
|
|
|
let ref: string | undefined;
|
2024-05-02 11:49:01 +00:00
|
|
|
await core.group(`Reference`, async () => {
|
2024-11-12 00:16:54 +00:00
|
|
|
ref = await buildRef(toolkit, startedTime, builder.name);
|
2024-05-02 11:49:01 +00:00
|
|
|
if (ref) {
|
|
|
|
core.info(ref);
|
|
|
|
stateHelper.setBuildRef(ref);
|
|
|
|
} else {
|
2024-06-27 09:26:35 +00:00
|
|
|
core.info('No build reference found');
|
2024-05-02 11:49:01 +00:00
|
|
|
}
|
|
|
|
});
|
2024-06-27 09:26:35 +00:00
|
|
|
|
2024-07-31 10:39:27 +00:00
|
|
|
if (buildChecksAnnotationsEnabled()) {
|
|
|
|
const warnings = toolkit.buildxBuild.resolveWarnings(metadata);
|
|
|
|
if (ref && warnings && warnings.length > 0) {
|
|
|
|
const annotations = await Buildx.convertWarningsToGitHubAnnotations(warnings, [ref]);
|
|
|
|
core.debug(`annotations: ${JSON.stringify(annotations, null, 2)}`);
|
|
|
|
if (annotations && annotations.length > 0) {
|
|
|
|
await core.group(`Generating GitHub annotations (${annotations.length} build checks found)`, async () => {
|
|
|
|
for (const annotation of annotations) {
|
|
|
|
core.warning(annotation.message, annotation);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-07-30 15:48:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-27 09:26:35 +00:00
|
|
|
await core.group(`Check build summary support`, async () => {
|
2024-07-02 15:38:24 +00:00
|
|
|
if (!buildSummaryEnabled()) {
|
2024-06-27 09:26:35 +00:00
|
|
|
core.info('Build summary disabled');
|
|
|
|
} else if (GitHub.isGHES) {
|
2024-08-13 09:05:41 +00:00
|
|
|
core.info('Build summary is not yet supported on GHES');
|
2024-06-27 09:26:35 +00:00
|
|
|
} else if (!(await toolkit.buildx.versionSatisfies('>=0.13.0'))) {
|
2024-08-13 09:05:41 +00:00
|
|
|
core.info('Build summary requires Buildx >= 0.13.0');
|
2024-06-27 09:26:35 +00:00
|
|
|
} else if (builder && builder.driver === 'cloud') {
|
2024-08-13 09:05:41 +00:00
|
|
|
core.info('Build summary is not yet supported with Docker Build Cloud');
|
2024-06-27 09:26:35 +00:00
|
|
|
} else if (!ref) {
|
2024-08-13 09:05:41 +00:00
|
|
|
core.info('Build summary requires a build reference');
|
2024-06-27 09:26:35 +00:00
|
|
|
} else {
|
|
|
|
core.info('Build summary supported!');
|
|
|
|
stateHelper.setSummarySupported();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-05-02 11:49:01 +00:00
|
|
|
if (err) {
|
2024-11-19 05:27:39 +00:00
|
|
|
if (localBuilderAddr) {
|
|
|
|
stateHelper.setDockerBuildStatus('failure');
|
2024-09-12 00:54:27 +00:00
|
|
|
}
|
2024-05-02 11:49:01 +00:00
|
|
|
throw err;
|
|
|
|
}
|
2024-11-19 05:27:39 +00:00
|
|
|
if (localBuilderAddr) {
|
|
|
|
stateHelper.setDockerBuildStatus('success');
|
2024-09-12 00:54:27 +00:00
|
|
|
}
|
2023-02-20 10:11:15 +00:00
|
|
|
},
|
|
|
|
// post
|
|
|
|
async () => {
|
2024-06-27 09:26:35 +00:00
|
|
|
if (stateHelper.isSummarySupported) {
|
2024-05-02 11:49:01 +00:00
|
|
|
await core.group(`Generating build summary`, async () => {
|
2024-05-02 11:49:01 +00:00
|
|
|
try {
|
2024-07-02 16:07:54 +00:00
|
|
|
const recordUploadEnabled = buildRecordUploadEnabled();
|
2024-07-02 16:09:36 +00:00
|
|
|
let recordRetentionDays: number | undefined;
|
2024-07-02 16:07:54 +00:00
|
|
|
if (recordUploadEnabled) {
|
2024-07-02 16:09:36 +00:00
|
|
|
recordRetentionDays = buildRecordRetentionDays();
|
2024-07-02 16:07:54 +00:00
|
|
|
}
|
|
|
|
|
2024-05-02 11:49:01 +00:00
|
|
|
const buildxHistory = new BuildxHistory();
|
|
|
|
const exportRes = await buildxHistory.export({
|
2024-06-27 09:26:35 +00:00
|
|
|
refs: stateHelper.buildRef ? [stateHelper.buildRef] : []
|
2024-05-02 11:49:01 +00:00
|
|
|
});
|
2024-07-02 16:07:54 +00:00
|
|
|
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',
|
2024-07-02 16:09:36 +00:00
|
|
|
retentionDays: recordRetentionDays
|
2024-07-02 16:07:54 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-02 11:49:01 +00:00
|
|
|
await GitHub.writeBuildSummary({
|
|
|
|
exportRes: exportRes,
|
|
|
|
uploadRes: uploadRes,
|
|
|
|
inputs: stateHelper.inputs
|
|
|
|
});
|
2024-05-02 11:49:01 +00:00
|
|
|
} catch (e) {
|
|
|
|
core.warning(e.message);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-11-19 05:27:39 +00:00
|
|
|
if (stateHelper.dockerBuildStatus != '') {
|
2024-11-04 21:24:12 +00:00
|
|
|
try {
|
|
|
|
await shutdownBuildkitd();
|
2024-11-08 15:27:16 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2024-11-19 05:27:39 +00:00
|
|
|
if (stateHelper.dockerBuildStatus == 'success') {
|
2024-11-04 21:24:12 +00:00
|
|
|
await reportBuildCompleted();
|
|
|
|
} else {
|
|
|
|
await reportBuildFailed();
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
core.warning(`Error during Blacksmith builder shutdown: ${error.message}`);
|
2024-09-13 00:11:11 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-20 10:11:15 +00:00
|
|
|
if (stateHelper.tmpDir.length > 0) {
|
|
|
|
await core.group(`Removing temp folder ${stateHelper.tmpDir}`, async () => {
|
2024-11-18 15:59:50 +00:00
|
|
|
fs.rmSync(stateHelper.tmpDir, {recursive: true});
|
2023-02-20 10:11:15 +00:00
|
|
|
});
|
|
|
|
}
|
2020-09-02 08:07:11 +00:00
|
|
|
}
|
2023-02-20 10:11:15 +00:00
|
|
|
);
|
2024-05-02 11:49:01 +00:00
|
|
|
|
|
|
|
async function buildRef(toolkit: Toolkit, since: Date, builder?: string): Promise<string> {
|
|
|
|
// get ref from metadata file
|
|
|
|
const ref = toolkit.buildxBuild.resolveRef();
|
|
|
|
if (ref) {
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
// otherwise, look for the very first build ref since the build has started
|
|
|
|
if (!builder) {
|
|
|
|
const currentBuilder = await toolkit.builder.inspect();
|
|
|
|
builder = currentBuilder.name;
|
|
|
|
}
|
|
|
|
const refs = Buildx.refs({
|
|
|
|
dir: Buildx.refsDir,
|
|
|
|
builderName: builder,
|
|
|
|
since: since
|
|
|
|
});
|
|
|
|
return Object.keys(refs).length > 0 ? Object.keys(refs)[0] : '';
|
|
|
|
}
|
2024-06-24 08:14:14 +00:00
|
|
|
|
2024-07-31 10:39:27 +00:00
|
|
|
function buildChecksAnnotationsEnabled(): boolean {
|
|
|
|
if (process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS) {
|
|
|
|
return Util.parseBool(process.env.DOCKER_BUILD_CHECKS_ANNOTATIONS);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-07-02 15:38:24 +00:00
|
|
|
function buildSummaryEnabled(): boolean {
|
2024-07-02 12:49:31 +00:00
|
|
|
if (process.env.DOCKER_BUILD_NO_SUMMARY) {
|
2024-07-02 15:38:24 +00:00
|
|
|
core.warning('DOCKER_BUILD_NO_SUMMARY is deprecated. Set DOCKER_BUILD_SUMMARY to false instead.');
|
|
|
|
return !Util.parseBool(process.env.DOCKER_BUILD_NO_SUMMARY);
|
|
|
|
} else if (process.env.DOCKER_BUILD_SUMMARY) {
|
|
|
|
return Util.parseBool(process.env.DOCKER_BUILD_SUMMARY);
|
2024-07-02 12:49:31 +00:00
|
|
|
}
|
2024-07-02 15:38:24 +00:00
|
|
|
return true;
|
2024-07-02 12:49:31 +00:00
|
|
|
}
|
|
|
|
|
2024-07-02 16:07:54 +00:00
|
|
|
function buildRecordUploadEnabled(): boolean {
|
|
|
|
if (process.env.DOCKER_BUILD_RECORD_UPLOAD) {
|
|
|
|
return Util.parseBool(process.env.DOCKER_BUILD_RECORD_UPLOAD);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-07-02 16:09:36 +00:00
|
|
|
function buildRecordRetentionDays(): number | undefined {
|
|
|
|
let val: string | undefined;
|
2024-06-24 08:14:14 +00:00
|
|
|
if (process.env.DOCKER_BUILD_EXPORT_RETENTION_DAYS) {
|
2024-07-02 16:09:36 +00:00
|
|
|
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);
|
2024-06-24 08:14:14 +00:00
|
|
|
if (isNaN(res)) {
|
2024-07-02 16:09:36 +00:00
|
|
|
throw Error(`Invalid build record retention days: ${val}`);
|
2024-06-24 08:14:14 +00:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|