From 7378abee4672f2dee47620cd113b7e7408ee37df Mon Sep 17 00:00:00 2001 From: Topper Bowers Date: Thu, 5 Feb 2026 08:36:23 +0100 Subject: [PATCH] linux-arm64 support (#63) docker on osx runs as linux-arm64 and there's no build for that. TBH, this is completely vibe coded but I did manually but I did look through this and seems right to me. --- .github/workflows/release.yaml | 5 + docker/release/build.sh | 6 ++ docker/release/linux-aarch64.Dockerfile | 108 ++++++++++++++++++++ justfile | 1 + scripts/release/sdk.ts | 2 + sdks/cli/bin/sandbox-agent | 3 +- sdks/cli/package.json | 1 + sdks/cli/platforms/linux-arm64/package.json | 22 ++++ sdks/cli/tests/launcher.test.ts | 2 + sdks/typescript/src/spawn.ts | 3 +- 10 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 docker/release/linux-aarch64.Dockerfile create mode 100644 sdks/cli/platforms/linux-arm64/package.json diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 76c5b31..78ab8e2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -96,6 +96,11 @@ jobs: target: x86_64-unknown-linux-musl binary_ext: "" arch: x86_64 + - platform: linux + runner: depot-ubuntu-24.04-arm-8 + target: aarch64-unknown-linux-musl + binary_ext: "" + arch: aarch64 - platform: windows runner: depot-ubuntu-24.04-8 target: x86_64-pc-windows-gnu diff --git a/docker/release/build.sh b/docker/release/build.sh index a9e42e4..6e7d66f 100755 --- a/docker/release/build.sh +++ b/docker/release/build.sh @@ -18,6 +18,12 @@ case $TARGET in TARGET_STAGE="x86_64-builder" BINARY="sandbox-agent-$TARGET" ;; + aarch64-unknown-linux-musl) + echo "Building for Linux aarch64 musl" + DOCKERFILE="linux-aarch64.Dockerfile" + TARGET_STAGE="aarch64-builder" + BINARY="sandbox-agent-$TARGET" + ;; x86_64-pc-windows-gnu) echo "Building for Windows x86_64" DOCKERFILE="windows.Dockerfile" diff --git a/docker/release/linux-aarch64.Dockerfile b/docker/release/linux-aarch64.Dockerfile new file mode 100644 index 0000000..d384f29 --- /dev/null +++ b/docker/release/linux-aarch64.Dockerfile @@ -0,0 +1,108 @@ +# syntax=docker/dockerfile:1.10.0 + +# Build inspector frontend +FROM node:22-alpine AS inspector-build +WORKDIR /app +RUN npm install -g pnpm + +# Copy package files for workspaces +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY frontend/packages/inspector/package.json ./frontend/packages/inspector/ +COPY sdks/cli-shared/package.json ./sdks/cli-shared/ +COPY sdks/typescript/package.json ./sdks/typescript/ + +# Install dependencies +RUN pnpm install --filter @sandbox-agent/inspector... + +# Copy SDK source (with pre-generated types from docs/openapi.json) +COPY docs/openapi.json ./docs/ +COPY sdks/cli-shared ./sdks/cli-shared +COPY sdks/typescript ./sdks/typescript + +# Build cli-shared and SDK (just tsup, skip generate since types are pre-generated) +RUN cd sdks/cli-shared && pnpm exec tsup +RUN cd sdks/typescript && SKIP_OPENAPI_GEN=1 pnpm exec tsup + +# Copy inspector source and build +COPY frontend/packages/inspector ./frontend/packages/inspector +RUN cd frontend/packages/inspector && pnpm exec vite build + +FROM rust:1.88.0 AS base + +# Install dependencies +RUN apt-get update && apt-get install -y \ + musl-tools \ + musl-dev \ + llvm-14-dev \ + libclang-14-dev \ + clang-14 \ + libssl-dev \ + pkg-config \ + ca-certificates \ + g++ \ + g++-multilib \ + git \ + curl && \ + rm -rf /var/lib/apt/lists/* && \ + wget -q https://github.com/cross-tools/musl-cross/releases/latest/download/aarch64-unknown-linux-musl.tar.xz && \ + tar -xf aarch64-unknown-linux-musl.tar.xz -C /opt/ && \ + rm aarch64-unknown-linux-musl.tar.xz + +# Install musl targets +RUN rustup target add aarch64-unknown-linux-musl + +# Set environment variables +ENV PATH="/opt/aarch64-unknown-linux-musl/bin:$PATH" \ + LIBCLANG_PATH=/usr/lib/llvm-14/lib \ + CLANG_PATH=/usr/bin/clang-14 \ + CC_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-gcc \ + CXX_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-g++ \ + AR_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-ar \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-unknown-linux-musl-gcc \ + CARGO_INCREMENTAL=0 \ + RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-static-libgcc" \ + CARGO_NET_GIT_FETCH_WITH_CLI=true + +# Set working directory +WORKDIR /build + +# Build for aarch64 +FROM base AS aarch64-builder + +# Accept version as build arg +ARG SANDBOX_AGENT_VERSION +ENV SANDBOX_AGENT_VERSION=${SANDBOX_AGENT_VERSION} + +# Set up OpenSSL for aarch64 musl target +ENV SSL_VER=1.1.1w +RUN wget https://www.openssl.org/source/openssl-$SSL_VER.tar.gz \ + && tar -xzf openssl-$SSL_VER.tar.gz \ + && cd openssl-$SSL_VER \ + && ./Configure no-shared no-async --prefix=/musl --openssldir=/musl/ssl linux-aarch64 \ + && make -j$(nproc) \ + && make install_sw \ + && cd .. \ + && rm -rf openssl-$SSL_VER* + +# Configure OpenSSL env vars for the build +ENV OPENSSL_DIR=/musl \ + OPENSSL_INCLUDE_DIR=/musl/include \ + OPENSSL_LIB_DIR=/musl/lib \ + PKG_CONFIG_ALLOW_CROSS=1 + +# Copy the source code +COPY . . + +# Copy pre-built inspector frontend +COPY --from=inspector-build /app/frontend/packages/inspector/dist ./frontend/packages/inspector/dist + +# Build for Linux with musl (static binary) - aarch64 +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=/build/target \ + cargo build -p sandbox-agent --release --target aarch64-unknown-linux-musl && \ + mkdir -p /artifacts && \ + cp target/aarch64-unknown-linux-musl/release/sandbox-agent /artifacts/sandbox-agent-aarch64-unknown-linux-musl + +# Default command to show help +CMD ["ls", "-la", "/artifacts"] diff --git a/justfile b/justfile index 2b3dd6b..f9d4103 100644 --- a/justfile +++ b/justfile @@ -17,6 +17,7 @@ release-build target="x86_64-unknown-linux-musl": [group('release')] release-build-all: ./docker/release/build.sh x86_64-unknown-linux-musl + ./docker/release/build.sh aarch64-unknown-linux-musl ./docker/release/build.sh x86_64-pc-windows-gnu ./docker/release/build.sh x86_64-apple-darwin ./docker/release/build.sh aarch64-apple-darwin diff --git a/scripts/release/sdk.ts b/scripts/release/sdk.ts index 42b181d..78f3e6a 100644 --- a/scripts/release/sdk.ts +++ b/scripts/release/sdk.ts @@ -18,6 +18,7 @@ const CRATES = [ const CLI_PACKAGES = [ "@sandbox-agent/cli", "@sandbox-agent/cli-linux-x64", + "@sandbox-agent/cli-linux-arm64", "@sandbox-agent/cli-win32-x64", "@sandbox-agent/cli-darwin-x64", "@sandbox-agent/cli-darwin-arm64", @@ -26,6 +27,7 @@ const CLI_PACKAGES = [ // Mapping from npm package name to Rust target and binary extension const CLI_PLATFORM_MAP: Record = { "@sandbox-agent/cli-linux-x64": { target: "x86_64-unknown-linux-musl", binaryExt: "" }, + "@sandbox-agent/cli-linux-arm64": { target: "aarch64-unknown-linux-musl", binaryExt: "" }, "@sandbox-agent/cli-win32-x64": { target: "x86_64-pc-windows-gnu", binaryExt: ".exe" }, "@sandbox-agent/cli-darwin-x64": { target: "x86_64-apple-darwin", binaryExt: "" }, "@sandbox-agent/cli-darwin-arm64": { target: "aarch64-apple-darwin", binaryExt: "" }, diff --git a/sdks/cli/bin/sandbox-agent b/sdks/cli/bin/sandbox-agent index 66c1aac..3f1d3ad 100755 --- a/sdks/cli/bin/sandbox-agent +++ b/sdks/cli/bin/sandbox-agent @@ -8,7 +8,7 @@ const fs = require("fs"); const path = require("path"); const TRUST_PACKAGES = - "@sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64"; + "@sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64"; function formatHint(binPath) { return formatNonExecutableBinaryMessage({ @@ -37,6 +37,7 @@ const PLATFORMS = { "darwin-arm64": "@sandbox-agent/cli-darwin-arm64", "darwin-x64": "@sandbox-agent/cli-darwin-x64", "linux-x64": "@sandbox-agent/cli-linux-x64", + "linux-arm64": "@sandbox-agent/cli-linux-arm64", "win32-x64": "@sandbox-agent/cli-win32-x64", }; diff --git a/sdks/cli/package.json b/sdks/cli/package.json index 968157b..0f85a6e 100644 --- a/sdks/cli/package.json +++ b/sdks/cli/package.json @@ -23,6 +23,7 @@ "@sandbox-agent/cli-darwin-arm64": "workspace:*", "@sandbox-agent/cli-darwin-x64": "workspace:*", "@sandbox-agent/cli-linux-x64": "workspace:*", + "@sandbox-agent/cli-linux-arm64": "workspace:*", "@sandbox-agent/cli-win32-x64": "workspace:*" }, "files": [ diff --git a/sdks/cli/platforms/linux-arm64/package.json b/sdks/cli/platforms/linux-arm64/package.json new file mode 100644 index 0000000..f4b12f7 --- /dev/null +++ b/sdks/cli/platforms/linux-arm64/package.json @@ -0,0 +1,22 @@ +{ + "name": "@sandbox-agent/cli-linux-arm64", + "version": "0.1.6-rc.1", + "description": "sandbox-agent CLI binary for Linux arm64", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/rivet-dev/sandbox-agent" + }, + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "scripts": { + "postinstall": "chmod +x bin/sandbox-agent || true" + }, + "files": [ + "bin" + ] +} diff --git a/sdks/cli/tests/launcher.test.ts b/sdks/cli/tests/launcher.test.ts index 1019bdf..0353284 100644 --- a/sdks/cli/tests/launcher.test.ts +++ b/sdks/cli/tests/launcher.test.ts @@ -38,6 +38,7 @@ describe("CLI Launcher", () => { "darwin-arm64": "@sandbox-agent/cli-darwin-arm64", "darwin-x64": "@sandbox-agent/cli-darwin-x64", "linux-x64": "@sandbox-agent/cli-linux-x64", + "linux-arm64": "@sandbox-agent/cli-linux-arm64", "win32-x64": "@sandbox-agent/cli-win32-x64", }; @@ -45,6 +46,7 @@ describe("CLI Launcher", () => { expect(PLATFORMS["darwin-arm64"]).toBe("@sandbox-agent/cli-darwin-arm64"); expect(PLATFORMS["darwin-x64"]).toBe("@sandbox-agent/cli-darwin-x64"); expect(PLATFORMS["linux-x64"]).toBe("@sandbox-agent/cli-linux-x64"); + expect(PLATFORMS["linux-arm64"]).toBe("@sandbox-agent/cli-linux-arm64"); expect(PLATFORMS["win32-x64"]).toBe("@sandbox-agent/cli-win32-x64"); }); diff --git a/sdks/typescript/src/spawn.ts b/sdks/typescript/src/spawn.ts index 6323e08..380afbc 100644 --- a/sdks/typescript/src/spawn.ts +++ b/sdks/typescript/src/spawn.ts @@ -29,11 +29,12 @@ const PLATFORM_PACKAGES: Record = { "darwin-arm64": "@sandbox-agent/cli-darwin-arm64", "darwin-x64": "@sandbox-agent/cli-darwin-x64", "linux-x64": "@sandbox-agent/cli-linux-x64", + "linux-arm64": "@sandbox-agent/cli-linux-arm64", "win32-x64": "@sandbox-agent/cli-win32-x64", }; const TRUST_PACKAGES = - "@sandbox-agent/cli-linux-x64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64"; + "@sandbox-agent/cli-linux-x64 @sandbox-agent/cli-linux-arm64 @sandbox-agent/cli-darwin-arm64 @sandbox-agent/cli-darwin-x64 @sandbox-agent/cli-win32-x64"; export function isNodeRuntime(): boolean { return typeof process !== "undefined" && !!process.versions?.node;