mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-11-11 12:37:33 +00:00
Building the image natively on an arm64 machine (a Raspberry Pi 4 or 5 on 64-bit OS, Apple silicon) fails at the first `RUN` of the build stage: ``` => ERROR [builder 2/40] RUN apk add --no-cache curl xz && ... 0.841 exec /bin/sh: exec format error ``` **Cause:** the Dockerfile declares `ARG BUILDPLATFORM=linux/amd64`. A default on that ARG overrides the value BuildKit sets for the machine doing the build, so an arm64 host pulls the amd64 `golang` image and can't run it. CI didn't catch it because its arm64 image is cross-built on an amd64 runner, where `linux/amd64` was correct anyway. **Fix:** declare `ARG BUILDPLATFORM` without a default. BuildKit, the default builder since Docker 23 and what buildx uses, fills it in on every host. amd64 builds and the CI cross-build are unchanged. **Workaround on current master:** `docker build --build-arg BUILDPLATFORM=linux/arm64 .` **Test:** `tests/unit/test-dockerfile-buildplatform.js` fails on the old Dockerfile and passes on this one. It checks three things: - `BUILDPLATFORM` has no default. - The Go builder stays pinned to `$BUILDPLATFORM`. - No stage hard-codes a platform. It's wired into `test-all.sh`. Found while building the image on a Raspberry Pi running Raspberry Pi OS Trixie (arm64) with Docker 29. --- Co-authored-by: Claude <noreply@anthropic.com>
45 lines
1.8 KiB
JavaScript
45 lines
1.8 KiB
JavaScript
/* test-dockerfile-buildplatform.js — the build stage runs on the building
|
|
* machine's own architecture.
|
|
*
|
|
* BuildKit sets BUILDPLATFORM to the host doing the build. Giving the global
|
|
* ARG a default (it used to say linux/amd64) overrides that, so a native build
|
|
* on an arm64 host — a Raspberry Pi, Apple silicon — pulled the amd64 Go image
|
|
* and failed at the first RUN with "exec format error". CI never saw it: its
|
|
* arm64 image is cross-built on an amd64 runner, where the default happened to
|
|
* be right.
|
|
*/
|
|
'use strict';
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const assert = require('assert');
|
|
|
|
const dockerfile = fs.readFileSync(path.join(__dirname, '..', '..', 'Dockerfile'), 'utf8');
|
|
const lines = dockerfile.split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#'));
|
|
|
|
let passed = 0, failed = 0;
|
|
function test(name, fn) {
|
|
try { fn(); passed++; console.log(` ✅ ${name}`); }
|
|
catch (e) { failed++; console.log(` ❌ ${name}: ${e.message}`); }
|
|
}
|
|
|
|
console.log('\n=== Dockerfile: build platform ===');
|
|
|
|
test('BUILDPLATFORM is never given a default', () => {
|
|
const withDefault = lines.filter((l) => /^ARG\s+BUILDPLATFORM\s*=/.test(l));
|
|
assert.deepStrictEqual(withDefault, [], 'found: ' + withDefault.join(' | '));
|
|
});
|
|
|
|
test('the Go builder stage is pinned to BUILDPLATFORM (native, no QEMU)', () => {
|
|
const from = lines.find((l) => /^FROM\b.*golang:.*\bAS\s+builder\b/i.test(l));
|
|
assert.ok(from, 'builder FROM line not found');
|
|
assert.ok(/--platform=\$BUILDPLATFORM\b/.test(from), from);
|
|
});
|
|
|
|
test('no stage hard-codes a platform', () => {
|
|
const pinned = lines.filter((l) => /^FROM\b/.test(l) && /--platform=(?!\$)/.test(l));
|
|
assert.deepStrictEqual(pinned, [], 'found: ' + pinned.join(' | '));
|
|
});
|
|
|
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
if (failed) process.exit(1);
|