Files
codebase-memory-mcp/scripts/ci/smoke-artifact.sh
T
Martin Vogel a54ea95719 fix(windows): ship one binary — remove the launcher stub flagged as a dropper
Windows shipped a PAIR: a small permanent launcher (codebase-memory-mcp.exe)
plus the real product binary (codebase-memory-mcp.payload.exe). The launcher
existed for exactly one reason — a running .exe cannot replace its own image
on Windows, so an in-process self-update needs a second resident binary to do
the swap.

That stub is statically indistinguishable from a dropper: a small, unsigned,
zero-prevalence PE whose whole job is verify-and-execute another binary.
Defender's ML scored it Trojan:Win32/Wacatac.B!ml and blocked the v0.9.1-rc.1
release at the VirusTotal gate. It is not fixable in our code on x64 —
bcrypt-free, stripped, VERSIONINFO'd, minimal-resource and even
resource-FREE builds on CI's own MSYS2 CLANG64 toolchain were all flagged,
while the product binary scans clean on every platform.

So remove the stub and move self-update OUT of the process into install.ps1,
which runs while cbm is NOT running: Windows' image lock only blocks a
process from replacing ITSELF.  now prints the exact PowerShell
command (with the Unblock-File hint for Mark-of-the-Web); install.ps1 is
idempotent, so re-running it IS the update — it stops the daemon, renames the
running binary aside (the one mutation Windows permits on a running image),
publishes the new one, and sweeps retired copies.

Windows now matches Linux and macOS: ONE binary per platform.

  * packaging, install.ps1, npm and PyPI wrappers all carry a single binary
  * the launcher/payload ABI contract and ~2500 lines of stub state machinery
    are deleted
  * every daemon start, CLI call and hook fire loses a process spawn, a named
    pipe handshake and an stdio relay
  * test_windows_bundle_contract.sh is rewritten as an INVERTED contract: it
    now asserts no shipped surface can reintroduce a launcher/payload pair,
    and that install.ps1 retires the running binary before publishing

Verified: VirusTotal 0/67 on the packaged binary and 0/58 on install.ps1 (no
certificate involved); macOS and Linux full suites green; Windows guards all
green including the new update-handoff contract; npm 10/10; PyPI 3/3.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-07-28 17:03:01 +02:00

122 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# smoke-artifact.sh — the local ARTIFACT-FLOW smoke lane. The release venue
# smokes the shipped archive (download → extract → canonical wrapper); before
# this lane existed, local CI only ever smoked source builds, so the whole
# packaging/archive-layout bug class could first appear in a release dry run.
#
# This driver reproduces the release flow end to end with local bytes:
# scripts/build.sh → scripts/package-release.sh → extract →
# the SAME canonical wrapper the remote venue runs, in artifact mode
# (CBM_SMOKE_ARTIFACT_DIR), whose completeness checks make a broken or
# incomplete archive a loud failure.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
usage() {
cat <<'EOF'
Usage: scripts/ci/smoke-artifact.sh <goos> <goarch> [--variant standard|ui]
[VAR=VAL ...]
Build → package (scripts/package-release.sh) → extract → smoke the EXTRACTED
artifact through the canonical wrapper, exactly like the release venue:
unix: scripts/smoke-local.sh with CBM_SMOKE_ARTIFACT_DIR
windows: test-infrastructure/vm/vm-smoke.sh with CBM_SMOKE_ARTIFACT_DIR
(run inside the VM/CI msys2 shell)
--variant ui builds --with-ui and smokes the ui archive (Phase 15 embedded
assets become mandatory — a standard binary cannot pass a ui run).
Make passthrough (VAR=VAL): CC= CXX= STATIC=1 ... forwarded to build steps.
Environment: BUILD_DIR (default build/c) — build tree used for the archive.
On failure the work directory is preserved for post-mortem (path printed).
EOF
}
GOOS=""
GOARCH=""
VARIANT="standard"
BUILD_ARGS=()
expect_value=""
for arg in "$@"; do
if [ "$expect_value" = "variant" ]; then
VARIANT="$arg"; expect_value=""; continue
fi
case "$arg" in
-h | --help) usage; exit 0 ;;
--variant) expect_value="variant" ;;
--variant=*) VARIANT="${arg#--variant=}" ;;
-*)
echo "smoke-artifact: unknown option '$arg'. Please consult --help." >&2
exit 2
;;
*=*) BUILD_ARGS+=("$arg") ;;
*)
if [ -z "$GOOS" ]; then GOOS="$arg"
elif [ -z "$GOARCH" ]; then GOARCH="$arg"
else
echo "smoke-artifact: unexpected argument '$arg'. Please consult --help." >&2
exit 2
fi
;;
esac
done
[ -n "$GOOS" ] && [ -n "$GOARCH" ] || { usage >&2; exit 2; }
case "$VARIANT" in
standard) SUFFIX=""; UI_FLAG=() ;;
ui) SUFFIX="-ui"; UI_FLAG=(--with-ui) ;;
*) echo "smoke-artifact: variant must be 'standard' or 'ui'. Please consult --help." >&2; exit 2 ;;
esac
case "$GOARCH" in
*-portable) BUILD_ARGS+=("STATIC=1") ;;
esac
BUILD_DIR="${BUILD_DIR:-build/c}"
export BUILD_DIR
scripts/build.sh ${UI_FLAG[@]+"${UI_FLAG[@]}"} \
BUILD_DIR="$BUILD_DIR" ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}
if [ "$GOOS" = "darwin" ]; then
codesign --sign - --force "$BUILD_DIR/codebase-memory-mcp"
fi
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/cbm-smoke-artifact.XXXXXX")"
cleanup() {
local status=$?
trap - EXIT
if [ "$status" -eq 0 ]; then
rm -rf "$WORK_DIR"
else
echo "smoke-artifact: preserved failed lane root at $WORK_DIR" >&2
fi
exit "$status"
}
trap cleanup EXIT
scripts/package-release.sh "$GOOS" "$GOARCH" --variant "$VARIANT" \
--out-dir "$WORK_DIR" ${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}
NAME="codebase-memory-mcp${SUFFIX}-${GOOS}-${GOARCH}"
EXTRACT_DIR="$WORK_DIR/extract"
mkdir -p "$EXTRACT_DIR"
if [ "$GOOS" = "windows" ]; then
unzip -q -o "$WORK_DIR/$NAME.zip" -d "$EXTRACT_DIR"
test -s "$EXTRACT_DIR/codebase-memory-mcp.exe"
# ONE binary per platform: a payload sibling means the AV-flagged launcher
# stub came back.
test ! -e "$EXTRACT_DIR/codebase-memory-mcp.payload.exe"
echo "=== smoke-artifact: smoking EXTRACTED $NAME.zip via vm-smoke.sh ==="
SMOKE_ARCH="$GOARCH" SMOKE_VARIANT="$VARIANT" \
CBM_SMOKE_ARTIFACT_DIR="$EXTRACT_DIR" \
bash test-infrastructure/vm/vm-smoke.sh
else
tar -xzf "$WORK_DIR/$NAME.tar.gz" -C "$EXTRACT_DIR"
chmod +x "$EXTRACT_DIR/codebase-memory-mcp"
echo "=== smoke-artifact: smoking EXTRACTED $NAME.tar.gz via smoke-local.sh ==="
CBM_SMOKE_ARTIFACT_DIR="$EXTRACT_DIR" \
scripts/smoke-local.sh "$EXTRACT_DIR/codebase-memory-mcp" "$VARIANT"
fi
echo "=== smoke-artifact: $NAME passed ==="