Files
codebase-memory-mcp/scripts/build.sh
T
Martin Vogel 365841304c fix(install,smoke): carry cbm-integrations.json wherever the binary goes
pr-smoke failed on all three platforms: the binary's install/uninstall render
templates from the integration asset, and every place that stages the binary
without the asset made those operations fail closed with "integration assets
missing". The asset must travel with the binary in EVERY layout, not just the
release archive. Five staging paths were missing it:

- scripts/build.sh — stage cbm-integrations.json next to the freshly built
  binary, so `build/c/codebase-memory-mcp install` works straight out of a
  build tree (dev, and the base for the smoke fixture).
- scripts/smoke-local.sh — include the asset in the fixture tarball and its
  required-sidecar check. Member set and ORDER now mirror package-release.sh
  exactly (binary, cbm-integrations.json, LICENSE, install.sh, notices); the
  fixture was smoking an archive layout we never actually ship.
- install.sh / install.ps1 — after installing the binary, copy the asset beside
  it in the install dir. `install` already publishes a verified copy to
  ~/.cbm/assets/<version>/, but a later install/uninstall run from the install
  dir resolves the asset NEXT TO THE BINARY first, so without the adjacent copy
  that lookup misses and a re-install or uninstall fails on a machine that just
  installed successfully. Best-effort atomic rename, same shape as the existing
  install.sh/ps1 sidecar copy. install.ps1 stays pure ASCII.
- scripts/smoke-test.sh Phase 14 — this phase hand-stages the binary into a
  fresh HOME without going through install, so nothing populates ~/.cbm/assets;
  stage the asset next to each staged copy so the uninstall it drives resolves.

Verified: scripts/smoke-local.sh on the standard binary now runs clean through
all 16 phases — zero "integration assets missing" / hook_script_uninstall /
agent-cleanup-failed lines (was 5). windows-bundle, smoke-fixture, exec-bit and
no-embedded-scripts contracts pass; install.ps1 has zero non-ASCII bytes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-08 05:08:25 +02:00

170 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# build.sh — Clean build of production binary (standard or with UI).
#
# Usage:
# scripts/build.sh # Standard binary
# scripts/build.sh --with-ui # Binary with embedded UI
# scripts/build.sh --help # Full usage
# scripts/build.sh --version v0.8.0 # With version stamp
# scripts/build.sh --arch x86_64 # Force x86_64 build
# scripts/build.sh CC=gcc-14 CXX=g++-14 # Override compiler
#
# This script is the SINGLE source of truth for building release binaries.
# Used identically in local development and CI workflows.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
usage() {
cat <<'EOF'
Usage: scripts/build.sh [--with-ui] [--version V] [--arch ARCH] [VAR=VAL ...]
The canonical production-build entry: identical in local CI, PR CI, dry run
and release. Always a CLEAN build of BUILD_DIR (build/c by default) — the
content-verified compiler cache (ccache via scripts/env.sh) makes repeat
builds fast without ever reusing a stale object: every object is re-derived
from current sources; a cache hit is byte-identical to a cold compile by
construction (CCACHE_COMPILERCHECK=content).
Options:
--with-ui Embed the web UI (builds the frontend first; needs node).
--version V Stamp the version string (release venue passes the tag).
--arch ARCH Force target arch (arm64 | x86_64), e.g. under Rosetta.
-h, --help This text.
Make passthrough (VAR=VAL, forwarded verbatim):
CC= CXX= Compiler override (venues pass their matrix compiler).
BUILD_DIR= Build in an isolated directory — REQUIRED when a clean
product build must not wipe build/c's test-runner (e.g.
build/smoke for the local ladder smoke).
STATIC=1 Fully static portable build (Alpine/musl leg).
EXTRA_CFLAGS= EXTRA_LDFLAGS= Sanitizer soak builds (see _soak.yml).
Environment:
CBM_NO_CCACHE=1 Disable the compiler cache (build correctness is identical;
only speed changes).
Callers: _build.yml (all release artifacts) · pr.yml pr-smoke · every
docker-compose build/smoke service · win.sh build (Windows VM ladder).
EOF
}
for arg in "$@"; do
case "$arg" in
-h|--help) usage; exit 0 ;;
esac
done
# Pre-parse --arch flag before sourcing env.sh
for arg in "$@"; do
case "$arg" in
--arch=*) export CBM_ARCH="${arg#--arch=}" ;;
esac
done
prev_arg=""
for arg in "$@"; do
if [[ "${prev_arg:-}" == "--arch" ]]; then
export CBM_ARCH="$arg"
fi
prev_arg="$arg"
done
# shellcheck source=env.sh
source "$ROOT/scripts/env.sh"
# shellcheck source=path-safety.sh
source "$ROOT/scripts/path-safety.sh"
# Parse remaining arguments. BUILD_DIR is tracked for the clean step below so
# containerized legs can build in their own directory instead of deleting and
# clobbering the host's native build/c artifacts.
WITH_UI=false
VERSION=""
BUILD_DIR="build/c"
EXTRA_MAKE_ARGS=()
prev_arg=""
for arg in "$@"; do
# Skip --arch and its value (already handled)
if [[ "${prev_arg:-}" == "--arch" ]]; then
prev_arg="$arg"
continue
fi
case "$arg" in
--with-ui)
WITH_UI=true
;;
--version)
prev_arg="$arg"
continue
;;
--arch|--arch=*)
;; # already handled
-*)
# STRICT: an unknown flag never falls through into make where it
# would fail cryptically (or worse, be absorbed).
echo "build.sh: unknown option '$arg'. Please consult --help." >&2
exit 2
;;
BUILD_DIR=*)
BUILD_DIR="${arg#BUILD_DIR=}"
EXTRA_MAKE_ARGS+=("$arg")
;;
CC=*|CXX=*)
export "${arg}"
EXTRA_MAKE_ARGS+=("$arg")
;;
*=*)
EXTRA_MAKE_ARGS+=("$arg") # VAR=VAL make passthrough
;;
*)
# Bare words are only ever the value of --version; anything else is
# a usage error, not a silent make argument.
if [[ "${prev_arg:-}" == "--version" ]]; then
VERSION="$arg"
else
echo "build.sh: unexpected argument '$arg'. Please consult --help." >&2
exit 2
fi
;;
esac
prev_arg="$arg"
done
# Version flag
CFLAGS_EXTRA=""
if [[ -n "$VERSION" ]]; then
CLEAN_VERSION="${VERSION#v}"
CFLAGS_EXTRA="-DCBM_VERSION=\"\\\"$CLEAN_VERSION\\\"\""
fi
print_env "build.sh"
echo " ui=$WITH_UI version=${VERSION:-dev}"
# Verify compiler supports target arch
verify_compiler "$CC"
# Step 1: Clean C build artifacts only (not node_modules — npm ci handles that)
cbm_remove_build_dir "$ROOT" "$BUILD_DIR"
# Step 2: Build (Makefile applies $ARCHFLAGS for the target arch on macOS)
if $WITH_UI; then
make -j"$NPROC" -f Makefile.cbm cbm-with-ui \
CFLAGS_EXTRA="$CFLAGS_EXTRA" "${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"}"
else
make -j"$NPROC" -f Makefile.cbm cbm \
CFLAGS_EXTRA="$CFLAGS_EXTRA" "${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"}"
fi
# Stage the integration-template asset next to the binary so the dev/CI build
# mirrors the release archive layout. The binary resolves cbm-integrations.json
# next to itself, in $CBM_ASSETS_DIR, or under ~/.cbm/assets/<version>/; a plain
# build/c binary has none of those, so install/uninstall (which render the
# templates to verify hash and ownership) would fail closed exactly as they do
# for a user who deleted the file. Copying it here keeps `install` working
# straight out of a build tree — smoke, local dev, and the release packaging all
# then see the same adjacency.
cp "$ROOT/assets/cbm-integrations.json" "$BUILD_DIR/cbm-integrations.json"
echo "=== Build complete: ${BUILD_DIR}/codebase-memory-mcp ==="