Files
2026-08-08 23:58:58 +02:00

591 lines
27 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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
# The package wrappers own their own runtime-set publication and lease-lock
# implementations. Run the same suites against both host families so Unix
# inode/flock behavior and Windows handle/link behavior are each gating,
# without adding Node/Go provisioning to every sanitizer/container leg.
test-package-wrappers:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: pkg/go/go.mod
cache: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "22"
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Test Go, npm, and PyPI runtime-set wrappers
shell: bash
run: scripts/ci/test-package-wrappers.sh
# 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# 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
zip
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
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 + external UI pack
# 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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: 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 external UI assets and guard seams
shell: msys2 {0}
# --with-ui builds the frontend and its content-addressed asset pack, 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++ TEST_SEAMS=1
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