mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-12 09:47:20 +08:00
The x86-64 leg runs this lane without exclusions on purpose, to settle
which limits are architectural. It has now run, and it disproves part of
what the previous block asserted. That block claimed all seven excluded
suites "abort with stack-overflow". Five do. Two do not, and lumping
them together hid two different problems behind one rationale.
(A) stack-overflow, five suites: grammar_regression grammar_labels
pipeline lang_contract grammar_probe_e. Confirmed on BOTH arm64 and
x86-64 (CI logged 5), so it is not the aarch64 artifact an earlier
note claimed. The recursion guards bound DEPTH while the resource
exhausted is BYTES; that follow-up stands unchanged.
(B) cli: no overflow at all. On x86-64 it runs to completion, 253
passed / 5 failed, every failure in the install or activation path,
with "agent_config agent=OpenClaw op=mcp_install" above them. Green
on every other venue. MSan reported zero use-of-uninitialized-value
in it, so the exclusion costs no uninit coverage. Recorded as
undiagnosed rather than guessed at: the local lane is arm64 where
these suites hit (A) before reaching this code, so there is no
faithful venue to iterate in and each attempt is a ~30min round
trip. That is a follow-up with an owner, not a dismissal.
(C) incremental: an RSS BUDGET failure, 3054MB against a 2304MB limit
-- not an overflow either. MSan maps shadow (and origin) memory for
every allocation, so the budget cannot separate a leak from shadow.
FIXED rather than excluded: the assertion is now skipped under
__has_feature(memory_sanitizer) only, so the guard keeps its teeth
on every other platform, where inflating the budget would have
blinded it. The suite stays IN the lane.
Verified: with (C) fixed, incremental is 163 passed / 0 failed and ZERO
stack-overflows under the local arm64 MSan container -- so it never
belonged in the overflow list on either architecture.
msan-lane.sh no longer forces MSAN_EXCLUDE empty. That override existed
to ask the architectural question; it is answered, and keeping it would
re-red the gate for causes already recorded. Both venues now read the one
authoritative list in scripts/msan.sh, which still warns loudly that the
lane is partial.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
562 lines
26 KiB
YAML
562 lines
26 KiB
YAML
# Reusable: unit + integration tests on all platforms
|
|
name: Test
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
skip_perf:
|
|
description: 'Skip incremental perf tests (phases 2-7)'
|
|
type: boolean
|
|
default: true
|
|
broad_platforms:
|
|
description: 'Test the broad platform matrix (older glibc + extra OS versions) instead of the core set'
|
|
type: boolean
|
|
default: false
|
|
shard_suites:
|
|
description: 'Split each C-suite leg across parallel shard jobs (ubuntu x3, windows x2); off = one job per leg, byte-identical to the pre-shard topology'
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
# Emit the platform matrices as JSON. The CORE set is the default (fast,
|
|
# unchanged); the BROAD set adds extra free runners (older glibc /
|
|
# additional OS versions) for a wider "does it build everywhere" picture.
|
|
setup-matrix:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
outputs:
|
|
unix: ${{ steps.set.outputs.unix }}
|
|
windows: ${{ steps.set.outputs.windows }}
|
|
steps:
|
|
- name: Compute matrices
|
|
id: set
|
|
env:
|
|
BROAD: ${{ inputs.broad_platforms }}
|
|
SHARDS: ${{ inputs.shard_suites }}
|
|
run: |
|
|
CORE_UNIX='[
|
|
{"os":"ubuntu-latest","cc":"gcc","cxx":"g++"},
|
|
{"os":"ubuntu-24.04-arm","cc":"gcc","cxx":"g++"},
|
|
{"os":"macos-14","cc":"cc","cxx":"c++"},
|
|
{"os":"macos-15-intel","cc":"cc","cxx":"c++"}
|
|
]'
|
|
# Broad matrix legs are REQUIRED gates (no optional/continue-on-error
|
|
# escape hatches): the release dry run must be all-green, every platform.
|
|
BROAD_UNIX='[
|
|
{"os":"ubuntu-22.04","cc":"gcc","cxx":"g++"},
|
|
{"os":"ubuntu-22.04-arm","cc":"gcc","cxx":"g++"},
|
|
{"os":"macos-15","cc":"cc","cxx":"c++"}
|
|
]'
|
|
# Each Windows leg pins the msys2 environment + package arch to the
|
|
# RUNNER architecture so the build is native, never emulated:
|
|
# x86-64 runners -> CLANG64 (mingw-w64-clang-x86_64-*)
|
|
# ARM64 runner -> CLANGARM64 (mingw-w64-clang-aarch64-*)
|
|
# windows-11-arm previously used the x86-64 CLANG64 toolchain, so its
|
|
# binary ran under Windows-on-ARM x86-64 emulation and ASan's function
|
|
# interception crashed (interception_win: unhandled instruction). With
|
|
# the native ARM64 toolchain ASan instruments native ARM64 code, so it
|
|
# is a real (non-optional) gate, not a tolerated emulated-flake.
|
|
CORE_WIN='[{"os":"windows-latest","msystem":"CLANG64","pkg":"x86_64"}]'
|
|
BROAD_WIN='[{"os":"windows-2025","msystem":"CLANG64","pkg":"x86_64"},{"os":"windows-11-arm","msystem":"CLANGARM64","pkg":"aarch64"}]'
|
|
if [ "$BROAD" = "true" ]; then
|
|
UNIX=$(jq -cn --argjson a "$CORE_UNIX" --argjson b "$BROAD_UNIX" '$a + $b')
|
|
WIN=$(jq -cn --argjson a "$CORE_WIN" --argjson b "$BROAD_WIN" '$a + $b')
|
|
else
|
|
UNIX=$(jq -cn --argjson a "$CORE_UNIX" '$a')
|
|
WIN=$(jq -cn --argjson a "$CORE_WIN" '$a')
|
|
fi
|
|
# Suite sharding (opt-in via the shard_suites input): each platform's
|
|
# C-suite run is split across N runner jobs. CBM_TEST_SHARD="i/N"
|
|
# slices deterministically inside run-tests-parallel.sh; each
|
|
# shard's union guard proves it ran exactly its slice; shards are
|
|
# minted HERE from one expansion so a missing shard cannot be
|
|
# configured silently; and the shard-completeness job re-proves the
|
|
# cross-shard union at runtime. Ubuntu runners are plentiful (3
|
|
# shards); Windows gets 2 (every extra shard re-pays ~5 min of
|
|
# MSYS2 setup); macOS stays at 1 — mac legs are not the critical
|
|
# path and mac runner concurrency ceilings are the tightest.
|
|
if [ "$SHARDS" = "true" ]; then
|
|
UNIX=$(jq -c '[.[] | . as $e
|
|
| (if ($e.os | startswith("ubuntu")) then 3 else 1 end) as $n
|
|
| range(1; $n + 1) as $i
|
|
| $e + {shard: "\($i)/\($n)"}]' <<< "$UNIX")
|
|
WIN=$(jq -c '[.[] | . as $e
|
|
| range(1; 3) as $i
|
|
| $e + {shard: "\($i)/2"}]' <<< "$WIN")
|
|
fi
|
|
echo "unix={\"include\":$UNIX}" >> "$GITHUB_OUTPUT"
|
|
echo "windows={\"include\":$WIN}" >> "$GITHUB_OUTPUT"
|
|
|
|
test-unix:
|
|
needs: setup-matrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{ fromJSON(needs.setup-matrix.outputs.unix) }}
|
|
runs-on: ${{ matrix.os }}
|
|
# Broad-only legs (extra OS versions) are informational: visible but
|
|
# non-blocking, so a flaky/less-common runner can't block a release.
|
|
continue-on-error: ${{ matrix.optional == true }}
|
|
timeout-minutes: 240
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Install deps (Ubuntu)
|
|
if: startsWith(matrix.os, 'ubuntu')
|
|
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
|
|
|
|
- name: Install ccache (macOS)
|
|
if: startsWith(matrix.os, 'macos')
|
|
run: command -v ccache >/dev/null 2>&1 || brew install ccache
|
|
|
|
# Verified compiler cache: CCACHE_COMPILERCHECK=content keys every entry
|
|
# on the compiler-binary CONTENT plus the fully preprocessed input, so a
|
|
# hit is provably the identical compilation — a stale or foreign cache
|
|
# can only miss, never return wrong output (see scripts/env.sh).
|
|
# Keys embed github.ref; the final ref-less restore key falls back to
|
|
# the newest cache GitHub's scoping permits (a PR can only ever see its
|
|
# own ref and the base branch, and base-branch caches are written only
|
|
# by base-branch runs), so a fresh PR starts from main's warm cache
|
|
# instead of building cold. Content verification is what makes that
|
|
# safe; the fallback buys ~8-12 minutes on every first-of-ref build.
|
|
- name: Compiler cache (content-verified, restore)
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-${{ github.sha }}
|
|
# The cross-ref fallback is what keeps fresh PRs warm: without it a
|
|
# new PR always started from a cold cache (observed 0% hit legs).
|
|
# ccache verifies content hashes itself, so a stale cache can only
|
|
# miss, never mis-compile.
|
|
restore-keys: |
|
|
ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-
|
|
ccache-test-${{ matrix.os }}-${{ matrix.cc }}-
|
|
|
|
- name: Test
|
|
run: scripts/test.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
|
|
env:
|
|
CBM_TEST_SHARD: ${{ matrix.shard }}
|
|
CBM_TEST_LEG: ${{ matrix.os }}-${{ matrix.cc }}
|
|
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 1500M
|
|
|
|
# Every shard of a leg builds the identical runner, so one shard's
|
|
# cache carries the complete object set: shard 1 (or the unsharded
|
|
# job) saves, the rest only restore — same save volume as before
|
|
# sharding, no quota thrash, no same-key save races. Saving on failure
|
|
# too keeps red iterations warm.
|
|
- name: Shard manifest (completeness proof)
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: shard-manifest-${{ matrix.os }}-${{ matrix.cc }}-${{ strategy.job-index }}
|
|
path: build/c/test-logs/shard-manifest.txt
|
|
# The harness writes the manifest BEFORE any suite runs, so a
|
|
# missing file here means the job died before the harness started
|
|
# (contract step / build) — that failure is already the job's red;
|
|
# warn instead of stacking a second error on top of it. The
|
|
# shard-completeness job still gates the cross-shard union.
|
|
if-no-files-found: warn
|
|
|
|
- name: Compiler cache (save)
|
|
if: always() && (matrix.shard == null || startsWith(matrix.shard, '1/'))
|
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-${{ github.sha }}
|
|
|
|
- name: ccache stats
|
|
if: always()
|
|
run: ccache -s || true
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
|
|
# ThreadSanitizer data-race gate. The threaded production surfaces (watcher,
|
|
# subprocess, httpd, pipeline, mcp, worker pool, diagnostics, the runnable
|
|
# daemon-coordination paths) plus allocator concurrency all run under TSan
|
|
# here — see TEST_TSAN_SUITES in Makefile.cbm. Runs on both Linux
|
|
# architectures AND native ARM64 macOS: the threading code is shared, so a
|
|
# race is usually caught on all three, but scheduler differences mean each
|
|
# platform can surface a race the others miss. Windows has no TSan runtime on
|
|
# any toolchain (documented irreducible gap); this shared-code coverage is
|
|
# its substitute.
|
|
# Memory-diagnostics lane (user decision 2026-08-03: runner cost accepted).
|
|
# Newest pinned LLVM sanitizer runtime + straighter stacks over the full
|
|
# parallel suite — catches what an older compiler-rt can miss. Same
|
|
# canonical scripts/test.sh wave as every other leg.
|
|
test-diag:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Install deps
|
|
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
|
|
|
|
- name: Install LLVM 22 (pinned diagnostic toolchain)
|
|
run: |
|
|
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
|
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main" | sudo tee /etc/apt/sources.list.d/llvm-22.list
|
|
sudo apt-get update
|
|
sudo apt-get install -y clang-22
|
|
|
|
- name: Compiler cache (content-verified, restore)
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-diag-${{ github.ref }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-diag-${{ github.ref }}-
|
|
ccache-diag-
|
|
|
|
# Also enables the ASan checks that are off by default in every other
|
|
# lane: stack-use-after-return, stack-use-after-scope, and strict string
|
|
# checks. Mirrors `make -f Makefile.cbm diag`, including its reasoning
|
|
# for leaving detect_invalid_pointer_pairs out.
|
|
- name: Test (clang-22, ASan+UBSan + off-by-default ASan checks)
|
|
env:
|
|
ASAN_OPTIONS: "detect_stack_use_after_return=1:strict_string_checks=1:detect_stack_use_after_scope=1"
|
|
run: |
|
|
scripts/test.sh CC=clang-22 CXX=clang++-22 \
|
|
SANITIZE="-fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
|
|
|
- name: Compiler cache (save)
|
|
if: always()
|
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-diag-${{ github.ref }}-${{ github.sha }}
|
|
|
|
# MemorySanitizer lane (stage 2). Uninitialized READS are the one
|
|
# memory-error class no other lane covers dynamically. The image carries
|
|
# MSan-instrumented libc++/libc++abi/libunwind + zlib (vendored C deps
|
|
# instrument in-tree); scripts/msan.sh drives the build and the suite.
|
|
#
|
|
# THIS JOB IS THE AUTHORITATIVE VENUE for the lane. It previously ran WITHOUT
|
|
# the script's default exclusions to settle whether those were an aarch64
|
|
# artifact — the local ladder cannot answer that, having no faithful x86-64
|
|
# emulation. It has now answered it, and the answer SPLIT the list:
|
|
#
|
|
# - the five deep-recursion suites overflow their thread stacks on x86-64
|
|
# too, so that limit is NOT architectural;
|
|
# - `cli` does not overflow here at all — it fails in the install path for
|
|
# an unrelated, still-undiagnosed reason;
|
|
# - `incremental` was a shadow-memory RSS artifact, now fixed in the test
|
|
# itself rather than skipped, so it stays in the lane.
|
|
#
|
|
# Per-cause detail lives in scripts/msan.sh, which is the single authoritative
|
|
# list both venues now share. Zero use-of-uninitialized-value findings were
|
|
# reported across the lane on either architecture.
|
|
test-msan:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 180
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Buildx cache
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: /tmp/.buildx-msan
|
|
key: buildx-msan-${{ hashFiles('test-infrastructure/Dockerfile.msan') }}
|
|
|
|
- name: Build MSan image (cached layers)
|
|
run: scripts/ci/msan-lane.sh build
|
|
|
|
- name: MSan suite (exclusions per scripts/msan.sh)
|
|
run: scripts/ci/msan-lane.sh run
|
|
|
|
# macOS leak coverage. LeakSanitizer runs by default under ASan on Linux, so
|
|
# test-unix's ubuntu legs have always had it; on macOS it is off by default
|
|
# and Apple's clang refuses to turn it on, which left an entire platform with
|
|
# zero leak detection. Upstream (Homebrew) LLVM supports LSan on darwin/arm64,
|
|
# so this leg is the ordinary ASan suite built with that toolchain and run
|
|
# with detect_leaks=1. Mirrors the local `make -f Makefile.cbm test-lsan`.
|
|
test-lsan-macos:
|
|
runs-on: macos-latest
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Install LSan-capable toolchain
|
|
run: brew install llvm ccache
|
|
|
|
- name: Compiler cache (content-verified, restore)
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-lsan-${{ github.ref }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-lsan-${{ github.ref }}-
|
|
ccache-lsan-
|
|
|
|
- name: Test (Homebrew clang, ASan + LeakSanitizer)
|
|
env:
|
|
ASAN_OPTIONS: "detect_leaks=1:halt_on_error=1"
|
|
run: |
|
|
LLVM_PREFIX="$(brew --prefix llvm)"
|
|
scripts/test.sh CC="$LLVM_PREFIX/bin/clang" CXX="$LLVM_PREFIX/bin/clang++"
|
|
|
|
- name: Compiler cache (save)
|
|
if: always()
|
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-lsan-${{ github.ref }}-${{ github.sha }}
|
|
|
|
test-tsan:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- { os: ubuntu-latest, cc: clang, cxx: clang++ }
|
|
- { os: ubuntu-24.04-arm, cc: clang, cxx: clang++ }
|
|
- { os: macos-14, cc: cc, cxx: c++ }
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 120
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Install deps (Ubuntu)
|
|
if: runner.os == 'Linux'
|
|
run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev ccache
|
|
|
|
- name: Install deps (macOS)
|
|
if: runner.os == 'macOS'
|
|
run: brew install ccache
|
|
|
|
- name: Compiler cache (content-verified)
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-tsan-${{ matrix.os }}-${{ github.ref }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-tsan-${{ matrix.os }}-${{ github.ref }}-
|
|
ccache-tsan-${{ matrix.os }}-
|
|
|
|
- name: ThreadSanitizer tests
|
|
# Calls make directly (no env.sh), so route the compilers through
|
|
# ccache's masquerade dir: /usr/lib/ccache on Debian, libexec under the
|
|
# Homebrew prefix on macOS. On Linux, reduce mmap ASLR entropy first:
|
|
# modern (esp. aarch64) kernels randomize high enough that TSan's
|
|
# shadow mapping aborts with "unexpected memory mapping" before any
|
|
# test runs. macOS TSan needs no such adjustment.
|
|
run: |
|
|
if [ "$RUNNER_OS" = "macOS" ]; then
|
|
export PATH="$(brew --prefix ccache)/libexec:$PATH"
|
|
else
|
|
export PATH=/usr/lib/ccache:$PATH
|
|
sudo sysctl -w vm.mmap_rnd_bits=28 || true
|
|
fi
|
|
# The TSan leg lives in the canonical test entry (same file the
|
|
# compose test-tsan service runs); env.sh supplies the verified
|
|
# ccache masquerade.
|
|
scripts/test.sh --tsan CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 1500M
|
|
|
|
- name: ccache stats
|
|
if: always()
|
|
run: ccache -s || true
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
|
|
test-windows:
|
|
needs: setup-matrix
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{ fromJSON(needs.setup-matrix.outputs.windows) }}
|
|
runs-on: ${{ matrix.os }}
|
|
continue-on-error: ${{ matrix.optional == true }}
|
|
timeout-minutes: 240
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
# Defender posture: real-time protection is POLICY-LOCKED OFF on hosted
|
|
# Windows images (verified 2026-07-27: WinDefend starts and
|
|
# Set-MpPreference accepts, but RTP stays off — untamperable from the
|
|
# job). The local VM leg enforces Defender-ON via the canonical Defender
|
|
# preflight script, so AV-interaction coverage lives there; runners run
|
|
# without RTP by platform constraint.
|
|
|
|
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
|
|
with:
|
|
msystem: ${{ matrix.msystem }}
|
|
path-type: inherit
|
|
install: >-
|
|
mingw-w64-clang-${{ matrix.pkg }}-clang
|
|
mingw-w64-clang-${{ matrix.pkg }}-compiler-rt
|
|
mingw-w64-clang-${{ matrix.pkg }}-zlib
|
|
mingw-w64-clang-${{ matrix.pkg }}-python3
|
|
mingw-w64-clang-${{ matrix.pkg }}-ccache
|
|
make
|
|
git
|
|
|
|
- name: Create protected per-user temp root
|
|
# One implementation, shared with _soak.yml and the VM leg
|
|
# (test-infrastructure/vm/vm-run-tests.sh) — see the script's header for
|
|
# why the harness cannot run under a shared or ACL-inherited temp.
|
|
shell: pwsh
|
|
run: |
|
|
$root = & scripts/ci/new-protected-temp-root.ps1 `
|
|
-Prefix 'cbm-ci-tmp-' `
|
|
-ProtectDir (Join-Path $env:GITHUB_WORKSPACE "build\c")
|
|
"CBM_CI_TEMP_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
|
|
|
|
- name: Compiler cache (content-verified)
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-
|
|
ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-
|
|
|
|
- name: Test
|
|
shell: msys2 {0}
|
|
# AddressSanitizer is unavailable on native ARM64 Windows (LLVM ships no
|
|
# libclang_rt.asan for aarch64-w64-windows-gnu) and cannot intercept the
|
|
# system DLLs under x86-64 emulation either. Instead of running the
|
|
# ARM64 leg unsanitized, it runs UBSan in TRAP mode: -fsanitize-trap
|
|
# needs no runtime library (the very thing aarch64-w64-windows-gnu
|
|
# lacks), so it instruments natively and turns any undefined behavior
|
|
# into an illegal-instruction trap; -fstack-protector-strong adds
|
|
# stack-smash coverage the heap tools miss. To see WHICH check fired,
|
|
# reproduce under the emulated x86_64 UBSan (win.sh ubsan-build), which
|
|
# carries the full message. x86-64 Windows keeps full ASan+UBSan; the
|
|
# other native-ARM legs (Linux/macOS) carry ASan+LSan+TSan.
|
|
run: |
|
|
# Native tests resolve temp via TEMP/TMP (cbm_tmpdir); shell tools use
|
|
# TMPDIR. Route both through the protected root created above so no
|
|
# fixture lands under a shared or ACL-inherited ancestry.
|
|
export TEMP="$(cygpath -m "$CBM_CI_TEMP_ROOT")"
|
|
export TMP="$TEMP"
|
|
export TMPDIR="$(cygpath -u "$CBM_CI_TEMP_ROOT")"
|
|
# No per-leg flag logic here: test.sh itself applies the CLANGARM64
|
|
# trap-UBSan default, so local and CI build identical test binaries.
|
|
scripts/test.sh CC=clang CXX=clang++
|
|
env:
|
|
CBM_TEST_SHARD: ${{ matrix.shard }}
|
|
CBM_TEST_LEG: ${{ matrix.os }}-${{ matrix.msystem }}
|
|
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 1500M
|
|
|
|
- name: ccache stats
|
|
if: always()
|
|
shell: msys2 {0}
|
|
run: ccache -s || true
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
|
|
- name: Shard manifest (completeness proof)
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: shard-manifest-${{ matrix.os }}-${{ matrix.msystem }}-${{ strategy.job-index }}
|
|
path: build/c/test-logs/shard-manifest.txt
|
|
# The harness writes the manifest BEFORE any suite runs, so a
|
|
# missing file here means the job died before the harness started
|
|
# (contract step / build) — that failure is already the job's red;
|
|
# warn instead of stacking a second error on top of it. The
|
|
# shard-completeness job still gates the cross-shard union.
|
|
if-no-files-found: warn
|
|
|
|
# Shard 1 (or the unsharded job) saves the compiler cache; every shard
|
|
# builds the identical runner, so one cache carries the full object set.
|
|
- name: Compiler cache (save)
|
|
if: always() && (matrix.shard == null || startsWith(matrix.shard, '1/'))
|
|
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-${{ github.sha }}
|
|
|
|
# Cross-shard completeness: re-proves at runtime that each leg's shards
|
|
# agreed on the full suite list and that the union of their slices IS that
|
|
# list. This is the guard the per-shard union checks cannot provide: a
|
|
# mis-plumbed CBM_TEST_SHARD (two jobs running the same slice) passes every
|
|
# per-shard guard while a slice runs nowhere — only a cross-shard view
|
|
# catches it. Runs and gates on unsharded topologies too (every leg then
|
|
# contributes one 1/1 manifest whose slice must equal the full list).
|
|
shard-completeness:
|
|
needs: [test-unix, test-windows]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
# The union proof lives in the canonical scripts/ci entry, not inline
|
|
# YAML (venue-parity contract) — checkout (into repo/, so the manifest
|
|
# download below stays untouched) provides the script.
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
with:
|
|
path: repo
|
|
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
pattern: shard-manifest-*
|
|
path: manifests
|
|
- name: Verify cross-shard union per leg
|
|
run: repo/scripts/ci/verify-shard-union.sh manifests
|
|
|
|
# Windows product-surface regression guards. Distinct from test-windows above
|
|
# (the sanitizer C suite): these drive a real product binary + embedded HTTP UI
|
|
# over stdio / CLI / HTTP and fail if a Windows bug already fixed on main comes
|
|
# back -- non-ASCII repo paths dropping definitions (#636/#357, fixed by #700),
|
|
# the PreToolUse hook augmenter no-op on drive-letter cwd (#618, fixed by #619),
|
|
# the UI directory picker not enumerating drives (#548, roots field), and non-ASCII
|
|
# CLI arguments being mangled by the narrow-argv main() (#423/#20, fixed by the
|
|
# wide-argv entrypoint that reads GetCommandLineW).
|
|
test-windows-guards:
|
|
runs-on: windows-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
|
|
with:
|
|
msystem: CLANG64
|
|
path-type: inherit
|
|
install: >-
|
|
mingw-w64-clang-x86_64-clang
|
|
mingw-w64-clang-x86_64-zlib
|
|
mingw-w64-clang-x86_64-ccache
|
|
make
|
|
git
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: "22"
|
|
|
|
- name: Test npm wrapper failure handling
|
|
working-directory: pkg/npm
|
|
run: npm test
|
|
|
|
- name: Compiler cache (content-verified)
|
|
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ github.workspace }}/.ccache
|
|
key: ccache-guards-${{ github.ref }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-guards-${{ github.ref }}-
|
|
ccache-guards-
|
|
|
|
- name: Build product binary with embedded UI
|
|
shell: msys2 {0}
|
|
# --with-ui builds the frontend (npm) and embeds it, so the drive-picker
|
|
# guard's HTTP UI is available. Functional gate only (no sanitizers).
|
|
run: scripts/build.sh --with-ui CC=clang CXX=clang++
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 1500M
|
|
|
|
- name: Windows regression guards (#636/#357, #618, #548, #423/#20)
|
|
shell: pwsh
|
|
# -GuardsOnly runs the four green guards and gates on them. All guards run
|
|
# against the real supervisor->worker spawn path: under the mandatory
|
|
# coordination daemon, CBM_INDEX_SUPERVISOR=0 is a fail-closed refusal, so
|
|
# the driver no longer sets it anywhere.
|
|
run: ./scripts/test-windows.ps1 -GuardsOnly -Binary build/c/codebase-memory-mcp.exe
|