mirror of
https://github.com/docker/build-push-action.git
synced 2025-08-09 18:12:12 +00:00
fix: use correct platform when creating remote buildx builder
The remote builder was hardcoded to use --platform linux/amd64 regardless of user input or runner architecture. This caused performance issues on ARM runners and cache inefficiencies. Now properly uses the platforms input or detects host architecture to avoid unnecessary QEMU emulation and improve build performance. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6fe3b1c366
commit
a7fa33c366
984
package-lock.json
generated
984
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.10",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/preset-typescript": "^7.27.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^20.12.12",
|
||||
|
@ -9,4 +9,4 @@ export class Metric {
|
||||
type: Metric_MetricType = Metric_MetricType.UNSPECIFIED;
|
||||
value: number = 0;
|
||||
labels: Record<string, string> = {};
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
export class StickyDiskService {
|
||||
constructor() {}
|
||||
|
||||
|
||||
async commitStickyDisk() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
async getStickyDisk() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
async queueDockerJob() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
async reportMetric() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
async up() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,4 @@ export const execa = jest.fn().mockImplementation(() => {
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
};
|
||||
});
|
||||
});
|
||||
|
41
src/__tests__/remote-builder-args.test.ts
Normal file
41
src/__tests__/remote-builder-args.test.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import * as os from 'os';
|
||||
import {getRemoteBuilderArgs, resolveRemoteBuilderPlatforms} from '../context';
|
||||
|
||||
jest.mock('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
warning: jest.fn(),
|
||||
error: jest.fn()
|
||||
}));
|
||||
|
||||
describe('Remote builder platform argument resolution', () => {
|
||||
const builderName = 'test-builder';
|
||||
const builderUrl = 'tcp://127.0.0.1:1234';
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
test('returns comma-separated list when platforms are supplied', async () => {
|
||||
const platforms = ['linux/arm64', 'linux/amd64'];
|
||||
const platformStr = resolveRemoteBuilderPlatforms(platforms);
|
||||
expect(platformStr).toBe('linux/arm64,linux/amd64');
|
||||
|
||||
const args = await getRemoteBuilderArgs(builderName, builderUrl, platforms);
|
||||
const idx = args.indexOf('--platform');
|
||||
expect(idx).toBeGreaterThan(-1);
|
||||
expect(args[idx + 1]).toBe('linux/arm64,linux/amd64');
|
||||
});
|
||||
|
||||
test('falls back to host architecture when no platforms supplied', async () => {
|
||||
jest.spyOn(os, 'arch').mockReturnValue('arm64' as any);
|
||||
|
||||
const platformStr = resolveRemoteBuilderPlatforms([]);
|
||||
expect(platformStr).toBe('linux/arm64');
|
||||
|
||||
const args = await getRemoteBuilderArgs(builderName, builderUrl, []);
|
||||
const idx = args.indexOf('--platform');
|
||||
expect(idx).toBeGreaterThan(-1);
|
||||
expect(args[idx + 1]).toBe('linux/arm64');
|
||||
});
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as handlebars from 'handlebars';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
|
||||
import {Build} from '@docker/actions-toolkit/lib/buildx/build';
|
||||
import {Context} from '@docker/actions-toolkit/lib/context';
|
||||
@ -336,12 +336,39 @@ export const tlsClientKeyPath = '/tmp/blacksmith_client_key.pem';
|
||||
export const tlsClientCaCertificatePath = '/tmp/blacksmith_client_ca_certificate.pem';
|
||||
export const tlsRootCaCertificatePath = '/tmp/blacksmith_root_ca_certificate.pem';
|
||||
|
||||
export async function getRemoteBuilderArgs(name: string, builderUrl: string): Promise<Array<string>> {
|
||||
/**
|
||||
* Resolve the platform list that should be passed to `docker buildx create`.
|
||||
*
|
||||
* Priority:
|
||||
* 1. Use the user-supplied platforms list (comma-joined) if provided.
|
||||
* 2. Fallback to the architecture of the host runner.
|
||||
*
|
||||
* The function is exported to allow isolated unit testing.
|
||||
*/
|
||||
export function resolveRemoteBuilderPlatforms(platforms?: string[]): string {
|
||||
// If user explicitly provided platforms, honour them verbatim.
|
||||
if (platforms && platforms.length > 0) {
|
||||
return platforms.join(',');
|
||||
}
|
||||
|
||||
// Otherwise derive from host architecture.
|
||||
const nodeArch = os.arch(); // e.g. 'x64', 'arm64', 'arm'
|
||||
const archMap: {[key: string]: string} = {
|
||||
x64: 'amd64',
|
||||
arm64: 'arm64',
|
||||
arm: 'arm'
|
||||
};
|
||||
const mappedArch = archMap[nodeArch] || nodeArch;
|
||||
return `linux/${mappedArch}`;
|
||||
}
|
||||
|
||||
export async function getRemoteBuilderArgs(name: string, builderUrl: string, platforms?: string[]): Promise<Array<string>> {
|
||||
const args: Array<string> = ['create', '--name', name, '--driver', 'remote'];
|
||||
|
||||
// TODO(aayush): Instead of hardcoding the platform, we should fail the build if the platform is
|
||||
// unsupported.
|
||||
args.push('--platform', 'linux/amd64');
|
||||
const platformFlag = resolveRemoteBuilderPlatforms(platforms);
|
||||
core.info(`Determined remote builder platform(s): ${platformFlag}`);
|
||||
args.push('--platform', platformFlag);
|
||||
|
||||
// Always use the remote builder, overriding whatever has been configured so far.
|
||||
args.push('--use');
|
||||
// Use the provided builder URL
|
||||
|
@ -202,7 +202,7 @@ actionsToolkit.run(
|
||||
if (builderInfo.addr) {
|
||||
await core.group(`Creating a builder instance`, async () => {
|
||||
const name = `blacksmith-${Date.now().toString(36)}`;
|
||||
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, builderInfo.addr!));
|
||||
const createCmd = await toolkit.buildx.getCommand(await context.getRemoteBuilderArgs(name, builderInfo.addr!, inputs.platforms));
|
||||
core.info(`Creating builder with command: ${createCmd.command}`);
|
||||
await Exec.getExecOutput(createCmd.command, createCmd.args, {
|
||||
ignoreReturnCode: true
|
||||
|
Loading…
x
Reference in New Issue
Block a user