mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-10 09:13:53 +08:00
Closes the closable local-vs-remote venue gaps so that, outside arch legs / YAML glue / release plumbing, every local red is a remote red and vice versa on shared legs: - scripts/package-release.sh: THE canonical archive step (names + five-file Windows bundle layout defined once); _build.yml's eight inline archive blocks become calls to it. - scripts/ci/smoke-artifact.sh: the artifact-flow smoke lane — build, package, extract, then the canonical wrapper in artifact mode; wired as run.sh smoke-artifact (compose service), win.sh smoke-artifact, and directly runnable on macOS. Archive-layout bugs now surface locally instead of in a release dry run. - glibc-floor leg (Dockerfile.glibc22 + compose + run.sh): portable binary smokes on ubuntu-22.04/glibc 2.35; the dynamic binary must refuse there (2.38+ floor by design). - Defender-ON parity (user directive): scripts/ci/ensure-defender.ps1 enables + VERIFIES real-time protection, fail-closed; runs in every Windows CI job (_test x2, _soak x3, _smoke, pr.yml) AND in the VM preflight; _smoke.yml's scan engine-failure soft-skip becomes a red gate. Expected cost: slower Windows jobs (AV scanning during install/build/test I/O); the next dry run proves the runner side. - Contracts: launcher-bundle five-file check retargeted onto package-release.sh + per-archive canonical-call association; venue-parity contract requires the new lanes, counts one ensure-defender step per Windows job, adds --help probes for the new entries. Extended contract fails on the pre-change tree (verified: 20 violations on HEAD). - VM README: ephemerality/Defender posture documented — utmctl has no snapshot verb, so per-run revert stays a manual qcow2 option; the sweep preflight remains the standing mechanism. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
306 lines
12 KiB
YAML
306 lines
12 KiB
YAML
# Reusable: soak tests (quick + ASan, all platforms)
|
|
name: Soak
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
duration_minutes:
|
|
description: 'Soak duration in minutes (per leg)'
|
|
type: number
|
|
default: 10
|
|
legs:
|
|
description: "Soak legs for the quick jobs, passed to scripts/soak-legs.sh --legs (e.g. 'quick,query-leak' or 'query-leak' for a long #581-only run)"
|
|
type: string
|
|
default: 'quick,query-leak'
|
|
run_asan:
|
|
description: 'Run ASan soak in addition to quick soak'
|
|
type: boolean
|
|
default: false
|
|
version:
|
|
description: 'Version string for build'
|
|
type: string
|
|
default: ''
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
soak-quick:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: ubuntu-latest
|
|
goos: linux
|
|
goarch: amd64
|
|
cc: gcc
|
|
cxx: g++
|
|
- os: ubuntu-24.04-arm
|
|
goos: linux
|
|
goarch: arm64
|
|
cc: gcc
|
|
cxx: g++
|
|
- os: macos-14
|
|
goos: darwin
|
|
goarch: arm64
|
|
cc: cc
|
|
cxx: c++
|
|
- os: macos-15-intel
|
|
goos: darwin
|
|
goarch: amd64
|
|
cc: cc
|
|
cxx: c++
|
|
runs-on: ${{ matrix.os }}
|
|
# Budget doctrine (this line has been silently violated TWICE): the job
|
|
# timeout must exceed the WORST-CASE run length, and duration_minutes is
|
|
# per LEG. Two 240-min legs (the nightly) need ~480 min + build + analysis;
|
|
# the previous 300 silently killed the second leg at the timeout — the same
|
|
# truncation class as the original hard-coded 30. Keep a literal (the
|
|
# inputs context is not reliably available at job-setup time): 560.
|
|
timeout-minutes: 560
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
- name: Install deps (Linux)
|
|
if: startsWith(matrix.os, 'ubuntu')
|
|
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 git
|
|
- name: Build
|
|
run: scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
|
|
# The soak SEQUENCE and its guards live in the ONE canonical entry
|
|
# scripts/soak-legs.sh — never in workflow YAML. Leg selection is a
|
|
# workflow input so a long #581-only run needs no separate workflow.
|
|
- name: Soak legs (${{ inputs.legs }}, ${{ inputs.duration_minutes }} min each)
|
|
run: scripts/soak-legs.sh --legs "${{ inputs.legs }}" build/c/codebase-memory-mcp ${{ inputs.duration_minutes }}
|
|
- name: Upload metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-quick-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
path: soak-results/
|
|
retention-days: 14
|
|
- name: Upload query-leak metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-query-leak-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
path: soak-results-query-leak/
|
|
retention-days: 14
|
|
|
|
soak-quick-windows:
|
|
runs-on: windows-latest
|
|
# Budget doctrine (see soak-quick): duration_minutes is per LEG, so two
|
|
# 240-min legs need ~480 min + build; 300 silently killed the second leg.
|
|
timeout-minutes: 560
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Ensure Windows Defender is active (venue parity)
|
|
shell: pwsh
|
|
run: scripts/ci/ensure-defender.ps1
|
|
- 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-python3
|
|
make
|
|
git
|
|
coreutils
|
|
# Same protected root the test legs and the VM soak use. Without it the
|
|
# soak ran under the runner's inherited LocalAppData\Temp ACLs while the
|
|
# local soak ran hardened — the two venues were soaking different shapes.
|
|
- name: Create protected per-user temp root
|
|
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: Build
|
|
shell: msys2 {0}
|
|
run: scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=clang CXX=clang++
|
|
# Both release-gating legs via the ONE canonical soak entry; only the
|
|
# protected-TEMP provisioning is venue text. soak-legs.sh resolves .exe.
|
|
- name: Soak legs (${{ inputs.legs }}, ${{ inputs.duration_minutes }} min each)
|
|
shell: msys2 {0}
|
|
run: |
|
|
export TEMP="$(cygpath -m "$CBM_CI_TEMP_ROOT")"
|
|
export TMP="$TEMP"
|
|
export TMPDIR="$(cygpath -u "$CBM_CI_TEMP_ROOT")"
|
|
scripts/soak-legs.sh --legs "${{ inputs.legs }}" build/c/codebase-memory-mcp ${{ inputs.duration_minutes }}
|
|
- name: Upload metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-quick-windows-amd64
|
|
path: soak-results/
|
|
retention-days: 14
|
|
- name: Upload query-leak metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-query-leak-windows-amd64
|
|
path: soak-results-query-leak/
|
|
retention-days: 14
|
|
|
|
soak-quick-windows-arm64:
|
|
# Native ARM64 Windows soak (CLANGARM64, no sanitizer — ASan is unavailable
|
|
# on native ARM64 Windows). Builds from source like soak-quick-windows.
|
|
runs-on: windows-11-arm
|
|
# Budget doctrine (see soak-quick): per-leg duration, two legs, 560.
|
|
timeout-minutes: 560
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Ensure Windows Defender is active (venue parity)
|
|
shell: pwsh
|
|
run: scripts/ci/ensure-defender.ps1
|
|
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
|
|
with:
|
|
msystem: CLANGARM64
|
|
path-type: inherit
|
|
install: >-
|
|
mingw-w64-clang-aarch64-clang
|
|
mingw-w64-clang-aarch64-zlib
|
|
mingw-w64-clang-aarch64-python3
|
|
make
|
|
git
|
|
coreutils
|
|
# Same protected root the test legs and the VM soak use. Without it the
|
|
# soak ran under the runner's inherited LocalAppData\Temp ACLs while the
|
|
# local soak ran hardened — the two venues were soaking different shapes.
|
|
- name: Create protected per-user temp root
|
|
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: Build
|
|
shell: msys2 {0}
|
|
run: scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=clang CXX=clang++
|
|
# Both release-gating legs via the ONE canonical soak entry; only the
|
|
# protected-TEMP provisioning is venue text. soak-legs.sh resolves .exe.
|
|
- name: Soak legs (${{ inputs.legs }}, ${{ inputs.duration_minutes }} min each)
|
|
shell: msys2 {0}
|
|
run: |
|
|
export TEMP="$(cygpath -m "$CBM_CI_TEMP_ROOT")"
|
|
export TMP="$TEMP"
|
|
export TMPDIR="$(cygpath -u "$CBM_CI_TEMP_ROOT")"
|
|
scripts/soak-legs.sh --legs "${{ inputs.legs }}" build/c/codebase-memory-mcp ${{ inputs.duration_minutes }}
|
|
- name: Upload metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-quick-windows-arm64
|
|
path: soak-results/
|
|
retention-days: 14
|
|
- name: Upload query-leak metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-query-leak-windows-arm64
|
|
path: soak-results-query-leak/
|
|
retention-days: 14
|
|
|
|
soak-asan:
|
|
if: ${{ inputs.run_asan }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: ubuntu-latest
|
|
goos: linux
|
|
goarch: amd64
|
|
cc: gcc
|
|
cxx: g++
|
|
- os: ubuntu-24.04-arm
|
|
goos: linux
|
|
goarch: arm64
|
|
cc: gcc
|
|
cxx: g++
|
|
- os: macos-14
|
|
goos: darwin
|
|
goarch: arm64
|
|
cc: cc
|
|
cxx: c++
|
|
- os: macos-15-intel
|
|
goos: darwin
|
|
goarch: amd64
|
|
cc: cc
|
|
cxx: c++
|
|
runs-on: ${{ matrix.os }}
|
|
# ASan soak runs a FIXED 15-min soak (hard-coded below, NOT driven by
|
|
# inputs.duration_minutes), but the ASan-instrumented build is slow and
|
|
# leak reporting adds teardown time. 60 keeps the budget comfortably above
|
|
# the 15-min run so it is never truncated. (Same class of bug as the
|
|
# soak-quick 30→240 mismatch above — keep the timeout above the run length.)
|
|
timeout-minutes: 240
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
- name: Install deps (Linux)
|
|
if: startsWith(matrix.os, 'ubuntu')
|
|
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev python3 git
|
|
- name: Build (ASan)
|
|
run: |
|
|
SANITIZE="-fsanitize=address,undefined -fno-omit-frame-pointer"
|
|
scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=${{ matrix.cc }} CXX=${{ matrix.cxx }} EXTRA_CFLAGS="$SANITIZE" EXTRA_LDFLAGS="$SANITIZE"
|
|
- name: ASan soak (15 min)
|
|
env:
|
|
ASAN_OPTIONS: "detect_leaks=1:halt_on_error=0:log_path=soak-results/asan"
|
|
# Single-leg variant of the canonical soak entry: the ASan build is too
|
|
# slow for the two-leg sequence; leg selection is a documented flag, not
|
|
# a divergent inline procedure.
|
|
run: scripts/soak-legs.sh --legs quick build/c/codebase-memory-mcp 15
|
|
- name: Upload metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-asan-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
path: soak-results/
|
|
retention-days: 14
|
|
|
|
soak-asan-windows:
|
|
if: ${{ inputs.run_asan }}
|
|
runs-on: windows-latest
|
|
# FIXED 15-min soak (hard-coded below). MSYS2/Wine + ASan build is the
|
|
# slowest path; 60 keeps the budget well above the run length so it is
|
|
# never truncated.
|
|
timeout-minutes: 240
|
|
steps:
|
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
|
|
- name: Ensure Windows Defender is active (venue parity)
|
|
shell: pwsh
|
|
run: scripts/ci/ensure-defender.ps1
|
|
- 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-python3
|
|
make
|
|
git
|
|
coreutils
|
|
- name: Build (ASan)
|
|
shell: msys2 {0}
|
|
run: |
|
|
SANITIZE="-fsanitize=address,undefined -fno-omit-frame-pointer"
|
|
scripts/build.sh ${{ inputs.version && format('--version {0}', inputs.version) || '' }} CC=clang CXX=clang++ EXTRA_CFLAGS="$SANITIZE" EXTRA_LDFLAGS="$SANITIZE"
|
|
- name: ASan soak (15 min, no leak detection)
|
|
shell: msys2 {0}
|
|
env:
|
|
ASAN_OPTIONS: "detect_leaks=0:halt_on_error=0:log_path=soak-results/asan"
|
|
# Single-leg variant of the canonical soak entry (see the unix ASan job).
|
|
run: scripts/soak-legs.sh --legs quick build/c/codebase-memory-mcp 15
|
|
- name: Upload metrics
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: soak-asan-windows-amd64
|
|
path: soak-results/
|
|
retention-days: 14
|