Skip to content

Commit

Permalink
Merge pull request #99 from useblacksmith/scaffold-multi-platform
Browse files Browse the repository at this point in the history
src: add scaffolding for support multi-platform builds
  • Loading branch information
adityamaru authored Feb 17, 2025
2 parents ca7f4dd + 4a3e86e commit 1def72d
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 103 deletions.
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,10 @@ const config: Config.InitialOptions = {
'^.+\\.ts$': 'ts-jest',
'^.+\\.js$': 'babel-jest'
},
transformIgnorePatterns: [
'/node_modules/(?!(@buf|@connectrpc)/)'
],
transformIgnorePatterns: ['/node_modules/(?!(@buf|@connectrpc)/)'],
verbose: true,
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/__tests__/**'
]
collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/**/__tests__/**']
};

export default config;
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"packageManager": "[email protected]",
"dependencies": {
"@actions/core": "^1.10.1",
"@buf/blacksmith_vm-agent.connectrpc_es": "^1.6.1-20250209182455-7d83cfb8ddb1.2",
"@buf/blacksmith_vm-agent.connectrpc_es": "^1.6.1-20250211212423-70f4f1344c53.2",
"@connectrpc/connect": "^1.6.1",
"@connectrpc/connect-node": "^1.6.1",
"@docker/actions-toolkit": "0.37.1",
Expand Down
10 changes: 6 additions & 4 deletions src/__tests__/blacksmith-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as main from '../main';
import * as reporter from '../reporter';
import {getDockerfilePath} from '../context';
import * as setupBuilder from '../setup_builder';
import { Metric_MetricType } from "@buf/blacksmith_vm-agent.bufbuild_es/stickydisk/v1/stickydisk_pb";
import {Metric_MetricType} from '@buf/blacksmith_vm-agent.bufbuild_es/stickydisk/v1/stickydisk_pb';

jest.mock('@actions/core', () => ({
debug: jest.fn(),
Expand Down Expand Up @@ -34,15 +34,17 @@ jest.mock('../setup_builder', () => ({
...jest.requireActual('../setup_builder'),
startAndConfigureBuildkitd: jest.fn(),
setupStickyDisk: jest.fn(),
getNumCPUs: jest.fn().mockResolvedValue(4)
getNumCPUs: jest.fn().mockResolvedValue(4),
leaveTailnet: jest.fn().mockResolvedValue(undefined),
getTailscaleIP: jest.fn()
}));

describe('startBlacksmithBuilder', () => {
let mockInputs;

beforeEach(() => {
jest.clearAllMocks();
mockInputs = {nofallback: false};
mockInputs = {nofallback: false, platforms: []};
});

test('should handle missing dockerfile path with nofallback=false', async () => {
Expand Down Expand Up @@ -110,7 +112,7 @@ describe('startBlacksmithBuilder', () => {
buildId: mockBuildId,
exposeId: mockExposeId
});
expect(setupBuilder.startAndConfigureBuildkitd).toHaveBeenCalledWith(mockParallelism);
expect(setupBuilder.startAndConfigureBuildkitd).toHaveBeenCalledWith(mockParallelism, []);
expect(reporter.reportBuildPushActionFailure).not.toHaveBeenCalled();
});

Expand Down
32 changes: 4 additions & 28 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,13 @@ import * as context from './context';
import {promisify} from 'util';
import {exec} from 'child_process';
import * as reporter from './reporter';
import {setupStickyDisk, startAndConfigureBuildkitd, getNumCPUs} from './setup_builder';
import {setupStickyDisk, startAndConfigureBuildkitd, getNumCPUs, leaveTailnet} from './setup_builder';
import {Metric_MetricType} from '@buf/blacksmith_vm-agent.bufbuild_es/stickydisk/v1/stickydisk_pb';

const buildxVersion = 'v0.17.0';
const mountPoint = '/var/lib/buildkit';
const execAsync = promisify(exec);

async function joinTailnet(): Promise<void> {
const token = process.env.BLACKSMITH_TAILSCALE_TOKEN;
if (!token || token === 'unset') {
core.debug('BLACKSMITH_TAILSCALE_TOKEN environment variable not set, skipping tailnet join');
return;
}

try {
await execAsync(`sudo tailscale up --authkey=${token} --hostname=${process.env.VM_ID}`);

core.info('Successfully joined tailnet');
} catch (error) {
throw new Error(`Failed to join tailnet: ${error.message}`);
}
}

async function leaveTailnet(): Promise<void> {
try {
await execAsync('sudo tailscale down');
} catch (error) {
core.warning(`Error leaving tailnet: ${error.message}`);
}
}

async function setupBuildx(version: string, toolkit: Toolkit): Promise<void> {
let toolPath;
const standalone = await toolkit.buildx.isStandalone();
Expand Down Expand Up @@ -94,8 +70,6 @@ async function setupBuildx(version: string, toolkit: Toolkit): Promise<void> {
*/
export async function startBlacksmithBuilder(inputs: context.Inputs): Promise<{addr: string | null; buildId: string | null; exposeId: string}> {
try {
await joinTailnet();

const dockerfilePath = context.getDockerfilePath(inputs);
if (!dockerfilePath) {
throw new Error('Failed to resolve dockerfile path');
Expand All @@ -107,7 +81,7 @@ export async function startBlacksmithBuilder(inputs: context.Inputs): Promise<{a
const parallelism = await getNumCPUs();

const buildkitdStartTime = Date.now();
const buildkitdAddr = await startAndConfigureBuildkitd(parallelism);
const buildkitdAddr = await startAndConfigureBuildkitd(parallelism, inputs.platforms);
const buildkitdDurationMs = Date.now() - buildkitdStartTime;
await reporter.reportMetric(Metric_MetricType.BPA_BUILDKITD_READY_DURATION_MS, buildkitdDurationMs);

Expand All @@ -128,6 +102,8 @@ export async function startBlacksmithBuilder(inputs: context.Inputs): Promise<{a

core.warning(`${errorMessage}. Falling back to a local build.`);
return {addr: null, buildId: null, exposeId: ''};
} finally {
await leaveTailnet();
}
}

Expand Down
40 changes: 16 additions & 24 deletions src/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import * as core from '@actions/core';
import axios, {AxiosError, AxiosInstance, AxiosResponse } from 'axios';
import axios, {AxiosError, AxiosInstance, AxiosResponse} from 'axios';
import axiosRetry from 'axios-retry';
import {ExportRecordResponse} from '@docker/actions-toolkit/lib/types/buildx/history';
import FormData from 'form-data';
import { createClient } from "@connectrpc/connect";
import { createGrpcTransport } from "@connectrpc/connect-node";
import { StickyDiskService } from "@buf/blacksmith_vm-agent.connectrpc_es/stickydisk/v1/stickydisk_connect";
import { Metric, Metric_MetricType } from "@buf/blacksmith_vm-agent.bufbuild_es/stickydisk/v1/stickydisk_pb";
import {createClient} from '@connectrpc/connect';
import {createGrpcTransport} from '@connectrpc/connect-node';
import {StickyDiskService} from '@buf/blacksmith_vm-agent.connectrpc_es/stickydisk/v1/stickydisk_connect';
import {Metric, Metric_MetricType} from '@buf/blacksmith_vm-agent.bufbuild_es/stickydisk/v1/stickydisk_pb';

// Configure base axios instance for Blacksmith API.
const createBlacksmithAPIClient = () => {
const apiUrl = process.env.BLACKSMITH_BACKEND_URL || (
process.env.BLACKSMITH_ENV?.includes('staging')
? 'https://stagingapi.blacksmith.sh'
: 'https://api.blacksmith.sh'
);
const apiUrl = process.env.BLACKSMITH_BACKEND_URL || (process.env.BLACKSMITH_ENV?.includes('staging') ? 'https://stagingapi.blacksmith.sh' : 'https://api.blacksmith.sh');
core.debug(`Using Blacksmith API URL: ${apiUrl}`);

const client = axios.create({
baseURL: apiUrl,
headers: {
Expand All @@ -30,8 +26,7 @@ const createBlacksmithAPIClient = () => {
retries: 5,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error: AxiosError) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error) ||
(error.response?.status ? error.response.status >= 500 : false);
return axiosRetry.isNetworkOrIdempotentRequestError(error) || (error.response?.status ? error.response.status >= 500 : false);
}
});

Expand All @@ -41,7 +36,7 @@ const createBlacksmithAPIClient = () => {
export function createBlacksmithAgentClient() {
const transport = createGrpcTransport({
baseUrl: 'http://192.168.127.1:5557',
httpVersion: '2',
httpVersion: '2'
});

return createClient(StickyDiskService, transport);
Expand All @@ -58,7 +53,7 @@ export async function reportBuildPushActionFailure(error?: Error, event?: string
message: event ? `${event}: ${error?.message || ''}` : error?.message || '',
warning: isWarning || false
};

const client = createBlacksmithAPIClient();
const response = await client.post('/stickydisks/report-failed', requestOptions);
return response.data;
Expand All @@ -72,7 +67,7 @@ export async function reportBuildCompleted(exportRes?: ExportRecordResponse, bla

try {
const agentClient = createBlacksmithAgentClient();

await agentClient.commitStickyDisk({
exposeId: exposeId || '',
stickyDiskKey: process.env.GITHUB_REPO_NAME || '',
Expand Down Expand Up @@ -175,22 +170,19 @@ export async function post(client: AxiosInstance, url: string, formData: FormDat
return await client.post(url, formData, {
headers: {
...client.defaults.headers.common,
...(formData && { 'Content-Type': 'multipart/form-data' }),
...(formData && {'Content-Type': 'multipart/form-data'})
},
signal: options?.signal
});
}

export async function reportMetric(
metricType: Metric_MetricType,
value: number
): Promise<void> {
export async function reportMetric(metricType: Metric_MetricType, value: number): Promise<void> {
try {
const agentClient = createBlacksmithAgentClient();

const metric = new Metric({
type: metricType,
value: { case: "intValue", value: BigInt(value) }
value: {case: 'intValue', value: BigInt(value)}
});

await agentClient.reportMetric({
Expand All @@ -202,4 +194,4 @@ export async function reportMetric(
// We can enable this once all agents are updated to support metrics.
// core.warning('Error reporting metric to BlacksmithAgent:', error);
}
}
}
Loading

0 comments on commit 1def72d

Please sign in to comment.