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>
This commit is contained in:
Martin Vogel
2026-08-08 05:08:25 +02:00
parent 83df500364
commit 365841304c
5 changed files with 64 additions and 5 deletions
+18
View File
@@ -322,6 +322,24 @@ if (Test-Path -LiteralPath $DownloadedInstaller -PathType Leaf) {
}
}
# Place the integration-template asset beside the installed binary, mirroring
# install.sh. `install` above published a verified copy to ~/.cbm/assets, but a
# later install/uninstall run from $InstallDir resolves the asset next to the
# binary first; without this copy that lookup misses and the run fails closed
# with "integration assets missing".
$DownloadedAsset = Join-Path $TmpDir "cbm-integrations.json"
if (Test-Path -LiteralPath $DownloadedAsset -PathType Leaf) {
$AssetDest = Join-Path $InstallDir "cbm-integrations.json"
$AssetTmp = "$AssetDest.new"
try {
Copy-Item -LiteralPath $DownloadedAsset -Destination $AssetTmp -Force -ErrorAction Stop
Move-Item -LiteralPath $AssetTmp -Destination $AssetDest -Force -ErrorAction Stop
} catch {
Remove-Item -LiteralPath $AssetTmp -Force -ErrorAction SilentlyContinue
Write-Host "note: could not place cbm-integrations.json in $InstallDir (asset resolves from ~/.cbm/assets)"
}
}
# Verify
try {
$ver = & $Dest --version 2>&1
+19
View File
@@ -294,6 +294,25 @@ if [ -f "$DL_INSTALLER" ]; then
fi
fi
# Place the integration-template asset beside the installed binary. `install`
# above already published a verified copy to ~/.cbm/assets/<version>/, but a
# later `install`/`uninstall` run from INSTALL_DIR resolves the asset next to
# the binary FIRST — and without this copy that lookup misses, so a re-run or an
# uninstall would fail closed with "integration assets missing" even though a
# valid install just completed. Atomic rename, best effort, same rationale as
# the installer copy above.
DL_ASSET="$DLDIR/cbm-integrations.json"
if [ -f "$DL_ASSET" ]; then
ASSET_TMP="$INSTALL_DIR/.cbm-integrations.json.$$"
if cp "$DL_ASSET" "$ASSET_TMP" 2>/dev/null &&
mv -f "$ASSET_TMP" "$INSTALL_DIR/cbm-integrations.json" 2>/dev/null; then
:
else
rm -f "$ASSET_TMP" 2>/dev/null || true
echo "note: could not place cbm-integrations.json in $INSTALL_DIR (asset resolves from ~/.cbm/assets)"
fi
fi
# Verify
VERSION=$("$DEST" --version 2>&1) || {
echo "error: installed binary failed to run" >&2
+10
View File
@@ -156,4 +156,14 @@ else
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 ==="
+9 -5
View File
@@ -72,7 +72,7 @@ esac
ARTIFACT_DIR="${CBM_SMOKE_ARTIFACT_DIR:-}"
if [ -n "$ARTIFACT_DIR" ]; then
ARTIFACT_DIR="$(cd "$ARTIFACT_DIR" && pwd)"
for required in LICENSE install.sh THIRD_PARTY_NOTICES.md; do
for required in cbm-integrations.json LICENSE install.sh THIRD_PARTY_NOTICES.md; do
[ -s "$ARTIFACT_DIR/$required" ] || {
echo "smoke-local: release artifact is missing $required" >&2
exit 2
@@ -123,16 +123,20 @@ mkdir -p "$FIXTURE_DIR" "$SMOKE_TEMP_DIR" "$SMOKE_HOME" "$SMOKE_XDG_CONFIG" \
"$SMOKE_APPDATA" "$SMOKE_LOCALAPPDATA"
cp "$BINARY" "$FIXTURE_DIR/codebase-memory-mcp"
if [ -n "$ARTIFACT_DIR" ]; then
cp "$ARTIFACT_DIR/LICENSE" "$ARTIFACT_DIR/install.sh" \
"$ARTIFACT_DIR/THIRD_PARTY_NOTICES.md" "$FIXTURE_DIR/"
cp "$ARTIFACT_DIR/cbm-integrations.json" "$ARTIFACT_DIR/LICENSE" \
"$ARTIFACT_DIR/install.sh" "$ARTIFACT_DIR/THIRD_PARTY_NOTICES.md" "$FIXTURE_DIR/"
else
cp "$ROOT/LICENSE" "$ROOT/install.sh" "$FIXTURE_DIR/"
cp "$ROOT/assets/cbm-integrations.json" "$ROOT/LICENSE" "$ROOT/install.sh" "$FIXTURE_DIR/"
"$ROOT/scripts/gen-third-party-notices.sh" "$FIXTURE_DIR/THIRD_PARTY_NOTICES.md"
fi
# The archive must carry cbm-integrations.json — install verifies it against the
# binary's embedded SHA-256 and fails closed without it. Member set and ORDER
# mirror scripts/package-release.sh (the Windows single-binary contract locks
# that order); a fixture that omits it would smoke a release layout we never ship.
EXPECTED_ARTIFACT="codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}.tar.gz"
tar -czf "$FIXTURE_DIR/$EXPECTED_ARTIFACT" -C "$FIXTURE_DIR" \
codebase-memory-mcp LICENSE install.sh THIRD_PARTY_NOTICES.md
codebase-memory-mcp cbm-integrations.json LICENSE install.sh THIRD_PARTY_NOTICES.md
if [ -n "$SUFFIX" ]; then
cp "$FIXTURE_DIR/$EXPECTED_ARTIFACT" \
"$FIXTURE_DIR/codebase-memory-mcp-${OS}-${ARCH}.tar.gz"
+8
View File
@@ -3068,16 +3068,24 @@ if [ -n "${SMOKE_DOWNLOAD_URL:-}" ]; then
fi
UPDATE_HOME=$(smoke_mktemp_dir)
mkdir -p "$UPDATE_HOME/.claude" "$UPDATE_HOME/.local/bin"
# This phase stages the binary by hand into a fresh HOME — it does NOT go
# through install.sh or `install`, so nothing populates ~/.cbm/assets. The
# integration asset ships next to the binary in every real layout (archive,
# build tree, installed dir), so stage it next to each staged copy here too;
# otherwise the uninstall this phase drives fails closed rendering templates.
BINARY_ASSET="$(dirname "$BINARY")/cbm-integrations.json"
if [[ "$BINARY" == *.exe ]]; then
cp "$BINARY" "$UPDATE_HOME/.local/bin/codebase-memory-mcp.exe"
mkdir -p "$UPDATE_HOME/retired-install"
cp "$BINARY" "$UPDATE_HOME/retired-install/codebase-memory-mcp.exe"
[ -f "$BINARY_ASSET" ] && cp "$BINARY_ASSET" "$UPDATE_HOME/.local/bin/cbm-integrations.json"
else
cp "$BINARY" "$UPDATE_HOME/.local/bin/codebase-memory-mcp"
chmod 755 "$UPDATE_HOME/.local/bin/codebase-memory-mcp"
mkdir -p "$UPDATE_HOME/retired-install"
cp "$BINARY" "$UPDATE_HOME/retired-install/codebase-memory-mcp"
chmod 755 "$UPDATE_HOME/retired-install/codebase-memory-mcp"
[ -f "$BINARY_ASSET" ] && cp "$BINARY_ASSET" "$UPDATE_HOME/.local/bin/cbm-integrations.json"
if [ "$(uname -s)" = "Darwin" ]; then
codesign --sign - --force "$UPDATE_HOME/.local/bin/codebase-memory-mcp" 2>/dev/null || true
codesign --sign - --force "$UPDATE_HOME/retired-install/codebase-memory-mcp" \