fix: bake version into binary at build time

Instead of hacking around the version in the Node.js wrapper script,
properly pass the version at build time via SANDBOX_AGENT_VERSION env var.

Changes:
- build.rs: Generate version.rs with VERSION constant from env var
- main.rs: Use generated version constant for clap --version
- Dockerfiles: Accept SANDBOX_AGENT_VERSION as build arg
- build.sh: Pass version as second argument to Docker builds
- release.yaml: Pass version to build script during CI
- Remove version hack from sdks/cli/bin/sandbox-agent wrapper

The version is now baked into the binary during the release build,
ensuring --version reports the correct npm package version.
This commit is contained in:
Nathan Flurry 2026-01-30 15:33:33 -08:00
parent 93445b3d4f
commit de139a7601
9 changed files with 56 additions and 22 deletions

View file

@ -125,8 +125,8 @@ jobs:
# Use Docker BuildKit
export DOCKER_BUILDKIT=1
# Build the binary using our Dockerfile
docker/release/build.sh ${{ matrix.target }}
# Build the binary using our Dockerfile with version
docker/release/build.sh ${{ matrix.target }} ${{ github.event.inputs.version }}
# Make sure dist directory exists and binary is there
ls -la dist/

View file

@ -2,6 +2,14 @@
set -euo pipefail
TARGET=${1:-x86_64-unknown-linux-musl}
VERSION=${2:-}
# Build arguments for Docker
BUILD_ARGS=""
if [ -n "$VERSION" ]; then
BUILD_ARGS="--build-arg SANDBOX_AGENT_VERSION=$VERSION"
echo "Building with version: $VERSION"
fi
case $TARGET in
x86_64-unknown-linux-musl)
@ -36,9 +44,9 @@ case $TARGET in
DOCKER_BUILDKIT=1
if [ -n "$TARGET_STAGE" ]; then
docker build --target "$TARGET_STAGE" -f "docker/release/$DOCKERFILE" -t "sandbox-agent-builder-$TARGET" .
docker build --target "$TARGET_STAGE" $BUILD_ARGS -f "docker/release/$DOCKERFILE" -t "sandbox-agent-builder-$TARGET" .
else
docker build -f "docker/release/$DOCKERFILE" -t "sandbox-agent-builder-$TARGET" .
docker build $BUILD_ARGS -f "docker/release/$DOCKERFILE" -t "sandbox-agent-builder-$TARGET" .
fi
CONTAINER_ID=$(docker create "sandbox-agent-builder-$TARGET")

View file

@ -66,6 +66,10 @@ WORKDIR /build
# Build for x86_64
FROM base AS x86_64-builder
# Accept version as build arg
ARG SANDBOX_AGENT_VERSION
ENV SANDBOX_AGENT_VERSION=${SANDBOX_AGENT_VERSION}
# Set up OpenSSL for x86_64 musl target
ENV SSL_VER=1.1.1w
RUN wget https://www.openssl.org/source/openssl-$SSL_VER.tar.gz \

View file

@ -70,6 +70,10 @@ WORKDIR /build
# Build for ARM64 macOS
FROM base AS aarch64-builder
# Accept version as build arg
ARG SANDBOX_AGENT_VERSION
ENV SANDBOX_AGENT_VERSION=${SANDBOX_AGENT_VERSION}
# Install macOS ARM64 target
RUN rustup target add aarch64-apple-darwin

View file

@ -70,6 +70,10 @@ WORKDIR /build
# Build for x86_64 macOS
FROM base AS x86_64-builder
# Accept version as build arg
ARG SANDBOX_AGENT_VERSION
ENV SANDBOX_AGENT_VERSION=${SANDBOX_AGENT_VERSION}
# Install macOS x86_64 target
RUN rustup target add x86_64-apple-darwin

View file

@ -26,6 +26,10 @@ RUN cd frontend/packages/inspector && pnpm exec vite build
FROM rust:1.88.0
# Accept version as build arg
ARG SANDBOX_AGENT_VERSION
ENV SANDBOX_AGENT_VERSION=${SANDBOX_AGENT_VERSION}
# Install dependencies
RUN apt-get update && apt-get install -y \
llvm-14-dev \

View file

@ -1,21 +1,6 @@
#!/usr/bin/env node
const { execFileSync } = require("child_process");
const path = require("path");
const fs = require("fs");
// Handle --version / -V at the launcher level to report npm package version
// This ensures version matches what's installed via npm, not what's compiled into the binary
const args = process.argv.slice(2);
if (args.length === 1 && (args[0] === "--version" || args[0] === "-V")) {
const pkgJsonPath = path.join(__dirname, "..", "package.json");
try {
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
console.log(`sandbox-agent ${pkgJson.version}`);
process.exit(0);
} catch (e) {
// Fall through to binary if we can't read package.json
}
}
const PLATFORMS = {
"darwin-arm64": "@sandbox-agent/cli-darwin-arm64",
@ -34,7 +19,7 @@ if (!pkg) {
try {
const pkgPath = require.resolve(`${pkg}/package.json`);
const bin = process.platform === "win32" ? "sandbox-agent.exe" : "sandbox-agent";
execFileSync(path.join(path.dirname(pkgPath), "bin", bin), args, { stdio: "inherit" });
execFileSync(path.join(path.dirname(pkgPath), "bin", bin), process.argv.slice(2), { stdio: "inherit" });
} catch (e) {
if (e.status !== undefined) process.exit(e.status);
throw e;

View file

@ -16,10 +16,14 @@ fn main() {
.join("dist");
println!("cargo:rerun-if-env-changed=SANDBOX_AGENT_SKIP_INSPECTOR");
println!("cargo:rerun-if-env-changed=SANDBOX_AGENT_VERSION");
println!("cargo:rerun-if-changed={}", dist_dir.display());
let skip = env::var("SANDBOX_AGENT_SKIP_INSPECTOR").is_ok();
// Generate version constant from environment variable or fallback to Cargo.toml version
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
generate_version(&out_dir);
let skip = env::var("SANDBOX_AGENT_SKIP_INSPECTOR").is_ok();
let out_file = out_dir.join("inspector_assets.rs");
if skip {
@ -61,3 +65,19 @@ fn quote_path(path: &Path) -> String {
.replace('\\', "\\\\")
.replace('"', "\\\"")
}
fn generate_version(out_dir: &Path) {
// Use SANDBOX_AGENT_VERSION env var if set, otherwise fall back to CARGO_PKG_VERSION
let version = env::var("SANDBOX_AGENT_VERSION")
.unwrap_or_else(|_| env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION"));
let out_file = out_dir.join("version.rs");
let contents = format!(
"/// Version string for this build.\n\
/// Set via SANDBOX_AGENT_VERSION env var at build time, or falls back to Cargo.toml version.\n\
pub const VERSION: &str = \"{}\";\n",
version
);
fs::write(&out_file, contents).expect("write version.rs");
}

View file

@ -4,6 +4,11 @@ use std::path::PathBuf;
use std::sync::Arc;
use clap::{Args, Parser, Subcommand};
// Include the generated version constant
mod build_version {
include!(concat!(env!("OUT_DIR"), "/version.rs"));
}
use reqwest::blocking::Client as HttpClient;
use reqwest::Method;
use sandbox_agent::router::{build_router_with_state, shutdown_servers};
@ -34,7 +39,7 @@ const DEFAULT_PORT: u16 = 2468;
#[derive(Parser, Debug)]
#[command(name = "sandbox-agent", bin_name = "sandbox-agent")]
#[command(about = "https://sandboxagent.dev", version)]
#[command(about = "https://sandboxagent.dev", version = build_version::VERSION)]
#[command(arg_required_else_help = true)]
struct Cli {
#[command(subcommand)]