Skip to content

Commit 84219bd

Browse files
authored
Merge pull request #1501 from DeusData/fix/external-ui-assets
fix(release): externalize runtime assets and harden VT verification
2 parents 2c50c77 + 5c18e3f commit 84219bd

127 files changed

Lines changed: 20663 additions & 2557 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/_build.yml‎

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Reusable: build binaries (standard + UI) on all platforms
1+
# Reusable: build release binaries on all supported platforms
22
name: Build
33

44
on:
@@ -12,6 +12,10 @@ on:
1212
description: 'Generate build provenance attestations for release artifacts'
1313
type: boolean
1414
default: false
15+
ui_only:
16+
description: 'Build only the UI runtime archives'
17+
type: boolean
18+
default: false
1519

1620
permissions:
1721
contents: read
@@ -64,6 +68,7 @@ jobs:
6468
node-version: "22"
6569

6670
- name: Build standard binary
71+
if: ${{ !inputs.ui_only }}
6772
env:
6873
VERSION: ${{ inputs.version }}
6974
CC: ${{ matrix.cc }}
@@ -75,17 +80,16 @@ jobs:
7580
scripts/build.sh "CC=$CC" "CXX=$CXX"
7681
fi
7782
78-
- name: Ad-hoc sign macOS binary
79-
if: startsWith(matrix.os, 'macos')
83+
- name: Ad-hoc sign macOS standard binary
84+
if: ${{ !inputs.ui_only && startsWith(matrix.os, 'macos') }}
8085
run: codesign --sign - --force build/c/codebase-memory-mcp
8186

82-
# Archive layout/name live in the ONE canonical script the local
83-
# artifact-flow smoke lane also runs (venue-parity contract).
8487
- name: Archive standard binary (canonical package-release.sh)
88+
if: ${{ !inputs.ui_only }}
8589
run: scripts/package-release.sh ${{ matrix.goos }} ${{ matrix.goarch }}
8690

8791
- name: Attest standard binary provenance
88-
if: ${{ inputs.attest }}
92+
if: ${{ inputs.attest && !inputs.ui_only }}
8993
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
9094
with:
9195
subject-path: codebase-memory-mcp-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
@@ -110,6 +114,8 @@ jobs:
110114
if: matrix.goos == 'linux' && matrix.goarch == 'amd64'
111115
run: scripts/security-ui.sh
112116

117+
# Archive layout/name live in the ONE canonical script the local
118+
# artifact-flow smoke lane also runs (venue-parity contract).
113119
- name: Archive UI binary (canonical package-release.sh)
114120
run: scripts/package-release.sh ${{ matrix.goos }} ${{ matrix.goarch }} --variant ui
115121

@@ -145,6 +151,7 @@ jobs:
145151
node-version: "22"
146152

147153
- name: Build standard binary
154+
if: ${{ !inputs.ui_only }}
148155
shell: msys2 {0}
149156
env:
150157
VERSION: ${{ inputs.version }}
@@ -155,14 +162,13 @@ jobs:
155162
scripts/build.sh CC=clang CXX=clang++
156163
fi
157164
158-
# Archive layout/name live in the ONE canonical script the local
159-
# artifact-flow smoke lane also runs (venue-parity contract).
160165
- name: Archive standard binary (canonical package-release.sh)
166+
if: ${{ !inputs.ui_only }}
161167
shell: msys2 {0}
162168
run: scripts/package-release.sh windows amd64 CC=clang CXX=clang++
163169

164170
- name: Attest standard binary provenance
165-
if: ${{ inputs.attest }}
171+
if: ${{ inputs.attest && !inputs.ui_only }}
166172
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
167173
with:
168174
subject-path: codebase-memory-mcp-windows-amd64.zip
@@ -178,6 +184,8 @@ jobs:
178184
scripts/build.sh --with-ui CC=clang CXX=clang++
179185
fi
180186
187+
# Archive layout/name live in the ONE canonical script the local
188+
# artifact-flow smoke lane also runs (venue-parity contract).
181189
- name: Archive UI binary (canonical package-release.sh)
182190
shell: msys2 {0}
183191
run: scripts/package-release.sh windows amd64 --variant ui CC=clang CXX=clang++
@@ -218,6 +226,7 @@ jobs:
218226
node-version: "22"
219227

220228
- name: Build standard binary
229+
if: ${{ !inputs.ui_only }}
221230
shell: msys2 {0}
222231
env:
223232
VERSION: ${{ inputs.version }}
@@ -229,11 +238,12 @@ jobs:
229238
fi
230239
231240
- name: Archive standard binary (canonical package-release.sh)
241+
if: ${{ !inputs.ui_only }}
232242
shell: msys2 {0}
233243
run: scripts/package-release.sh windows arm64 CC=clang CXX=clang++
234244

235245
- name: Attest standard binary provenance
236-
if: ${{ inputs.attest }}
246+
if: ${{ inputs.attest && !inputs.ui_only }}
237247
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
238248
with:
239249
subject-path: codebase-memory-mcp-windows-arm64.zip
@@ -288,6 +298,7 @@ jobs:
288298
node-version: "22"
289299

290300
- name: Build standard binary (static)
301+
if: ${{ !inputs.ui_only }}
291302
env:
292303
VERSION: ${{ inputs.version }}
293304
run: |
@@ -297,16 +308,18 @@ jobs:
297308
scripts/build.sh CC=gcc CXX=g++ STATIC=1
298309
fi
299310
300-
- name: Verify static linking
311+
- name: Verify standard static linking
312+
if: ${{ !inputs.ui_only }}
301313
run: |
302314
file build/c/codebase-memory-mcp
303315
ldd build/c/codebase-memory-mcp 2>&1 | grep -q "not a dynamic executable" || ldd build/c/codebase-memory-mcp 2>&1 | grep -q "statically linked"
304316
305317
- name: Archive standard binary (canonical package-release.sh)
318+
if: ${{ !inputs.ui_only }}
306319
run: scripts/package-release.sh linux ${{ matrix.arch }}-portable
307320

308321
- name: Attest standard binary provenance
309-
if: ${{ inputs.attest }}
322+
if: ${{ inputs.attest && !inputs.ui_only }}
310323
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
311324
with:
312325
subject-path: codebase-memory-mcp-linux-${{ matrix.arch }}-portable.tar.gz
@@ -321,6 +334,11 @@ jobs:
321334
scripts/build.sh --with-ui CC=gcc CXX=g++ STATIC=1
322335
fi
323336
337+
- name: Verify UI static linking
338+
run: |
339+
file build/c/codebase-memory-mcp
340+
ldd build/c/codebase-memory-mcp 2>&1 | grep -q "not a dynamic executable" || ldd build/c/codebase-memory-mcp 2>&1 | grep -q "statically linked"
341+
324342
- name: Archive UI binary (canonical package-release.sh)
325343
run: scripts/package-release.sh linux ${{ matrix.arch }}-portable --variant ui
326344

‎.github/workflows/_smoke.yml‎

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Reusable: smoke test every binary (standard + UI, all platforms)
1+
# Reusable: smoke test shipped release binaries on all platforms
22
name: Smoke
33

44
on:
@@ -8,6 +8,10 @@ on:
88
description: 'Smoke the shipped binaries on the broad platform matrix (extra OS versions) instead of the core set'
99
type: boolean
1010
default: false
11+
ui_only:
12+
description: 'Smoke only the UI runtime archives'
13+
type: boolean
14+
default: false
1115

1216
permissions:
1317
contents: read
@@ -30,6 +34,7 @@ jobs:
3034
id: set
3135
env:
3236
BROAD: ${{ inputs.broad_platforms }}
37+
UI_ONLY: ${{ inputs.ui_only }}
3338
run: |
3439
CORE_UNIX='[
3540
{"os":"ubuntu-latest","goos":"linux","goarch":"amd64"},
@@ -72,13 +77,14 @@ jobs:
7277
WIN=$(jq -cn --argjson a "$CORE_WIN" '$a')
7378
PORTABLE=$(jq -cn --argjson a "$CORE_PORTABLE" '$a')
7479
fi
75-
# Expand each OS entry over both variants into a FLAT include list so
76-
# every {os,variant} runs as its own job. A {variant:[...],include:$LIST}
77-
# matrix collapses under GitHub's include-merge (later os entries
78-
# overwrite the variant combos last-wins), which silently dropped every
79-
# OS except the last — e.g. only windows-11-arm ran, never windows-latest.
80-
# Building the cartesian explicitly avoids that.
81-
VARIANTS='["standard","ui"]'
80+
# Keep the variant dimension explicit because the smoke wrappers use it
81+
# to require and validate the UI pack. VT-focused dry runs select only
82+
# UI; release compatibility retains both variants until migration.
83+
if [ "$UI_ONLY" = "true" ]; then
84+
VARIANTS='["ui"]'
85+
else
86+
VARIANTS='["standard","ui"]'
87+
fi
8288
UNIX_M=$(jq -cn --argjson a "$UNIX" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
8389
WIN_M=$(jq -cn --argjson a "$WIN" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
8490
PORTABLE_M=$(jq -cn --argjson a "$PORTABLE" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
@@ -133,8 +139,8 @@ jobs:
133139
scripts/security-install.sh "$BIN"
134140
scripts/security-network.sh "$BIN"
135141
136-
- name: MCP robustness test (linux-amd64 standard only)
137-
if: matrix.variant == 'standard' && matrix.goos == 'linux' && matrix.goarch == 'amd64'
142+
- name: MCP robustness test (selected linux-amd64 runtime)
143+
if: matrix.goos == 'linux' && matrix.goarch == 'amd64' && (inputs.ui_only || matrix.variant == 'standard')
138144
run: |
139145
BIN="$RUNNER_TEMP/cbm-artifact/codebase-memory-mcp"
140146
scripts/security-fuzz.sh "$BIN"
@@ -206,8 +212,8 @@ jobs:
206212
mkdir -p "$ARTIFACT_DIR"
207213
unzip -o "codebase-memory-mcp${SUFFIX}-windows-${ARCH}.zip" -d "$ARTIFACT_DIR"
208214
test -s "$ARTIFACT_DIR/codebase-memory-mcp.exe"
209-
# ONE binary per platform: a payload sibling would mean the
210-
# AV-flagged launcher stub came back.
215+
# ONE executable per runtime set: a payload sibling would mean the
216+
# removed launcher split came back.
211217
test ! -e "$ARTIFACT_DIR/codebase-memory-mcp.payload.exe"
212218
# Pre-flight the image from a profile-rooted directory: the shared
213219
# runner workspace is deliberately not a valid install root.

‎.github/workflows/_test.yml‎

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,37 @@ jobs:
174174
env:
175175
CCACHE_DIR: ${{ github.workspace }}/.ccache
176176

177+
# The package wrappers own their own runtime-set publication and lease-lock
178+
# implementations. Run the same suites against both host families so Unix
179+
# inode/flock behavior and Windows handle/link behavior are each gating,
180+
# without adding Node/Go provisioning to every sanitizer/container leg.
181+
test-package-wrappers:
182+
strategy:
183+
fail-fast: false
184+
matrix:
185+
os: [ubuntu-latest, windows-latest]
186+
runs-on: ${{ matrix.os }}
187+
timeout-minutes: 30
188+
steps:
189+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
190+
191+
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
192+
with:
193+
go-version-file: pkg/go/go.mod
194+
cache: false
195+
196+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
197+
with:
198+
node-version: "22"
199+
200+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
201+
with:
202+
python-version: "3.12"
203+
204+
- name: Test Go, npm, and PyPI runtime-set wrappers
205+
shell: bash
206+
run: scripts/ci/test-package-wrappers.sh
207+
177208
# ThreadSanitizer data-race gate. The threaded production surfaces (watcher,
178209
# subprocess, httpd, pipeline, mcp, worker pool, diagnostics, the runnable
179210
# daemon-coordination paths) plus allocator concurrency all run under TSan
@@ -394,6 +425,7 @@ jobs:
394425
mingw-w64-clang-${{ matrix.pkg }}-ccache
395426
make
396427
git
428+
zip
397429
398430
- name: Create protected per-user temp root
399431
# One implementation, shared with _soak.yml and the VM leg
@@ -502,7 +534,7 @@ jobs:
502534
run: repo/scripts/ci/verify-shard-union.sh manifests
503535

504536
# Windows product-surface regression guards. Distinct from test-windows above
505-
# (the sanitizer C suite): these drive a real product binary + embedded HTTP UI
537+
# (the sanitizer C suite): these drive a real product binary + external UI pack
506538
# over stdio / CLI / HTTP and fail if a Windows bug already fixed on main comes
507539
# back -- non-ASCII repo paths dropping definitions (#636/#357, fixed by #700),
508540
# the PreToolUse hook augmenter no-op on drive-letter cwd (#618, fixed by #619),
@@ -530,10 +562,6 @@ jobs:
530562
with:
531563
node-version: "22"
532564

533-
- name: Test npm wrapper failure handling
534-
working-directory: pkg/npm
535-
run: npm test
536-
537565
- name: Compiler cache (content-verified)
538566
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
539567
with:
@@ -543,11 +571,12 @@ jobs:
543571
ccache-guards-${{ github.ref }}-
544572
ccache-guards-
545573
546-
- name: Build product binary with embedded UI
574+
- name: Build product binary with external UI assets and guard seams
547575
shell: msys2 {0}
548-
# --with-ui builds the frontend (npm) and embeds it, so the drive-picker
549-
# guard's HTTP UI is available. Functional gate only (no sanitizers).
550-
run: scripts/build.sh --with-ui CC=clang CXX=clang++
576+
# --with-ui builds the frontend and its content-addressed asset pack, so
577+
# the drive-picker guard's HTTP UI is available. Functional gate only
578+
# (no sanitizers).
579+
run: scripts/build.sh --with-ui CC=clang CXX=clang++ TEST_SEAMS=1
551580
env:
552581
CCACHE_DIR: ${{ github.workspace }}/.ccache
553582
CCACHE_MAXSIZE: 1500M

‎.github/workflows/dry-run.yml‎

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ jobs:
9191
uses: ./.github/workflows/_build.yml
9292
with:
9393
attest: false
94+
ui_only: true
9495

9596
# ── Smoke test every binary ────────────────────────────────────
9697
# Run unless builds were skipped or a build leg FAILED. Every build leg
@@ -102,6 +103,7 @@ jobs:
102103
uses: ./.github/workflows/_smoke.yml
103104
with:
104105
broad_platforms: true
106+
ui_only: true
105107

106108
# ── Soak tests (optional, parallel with smoke) ────────────────
107109
soak:
@@ -149,22 +151,41 @@ jobs:
149151
# Same extraction the release verify job performs, so the bytes handed to
150152
# VirusTotal here are the bytes a user would download and run.
151153
- name: Extract archive artifacts
152-
run: scripts/ci/extract-release-archives.sh assets binaries
154+
run: |
155+
scripts/ci/extract-release-archives.sh assets binaries \
156+
--archive-scope=ui \
157+
--expect-archives=8 \
158+
--expect-binaries=8 \
159+
--expect-packs=8 \
160+
--expect-runtime-files=32
153161
154162
- name: VirusTotal scan
155163
uses: crazy-max/ghaction-virustotal@936d8c5c00afe97d3d9a1af26d017cfdf26800a2 # v5.0.0
156164
id: virustotal
157165
with:
158166
vt_api_key: ${{ secrets.VIRUS_TOTAL_SCANNER_API_KEY }}
159-
files: binaries/*
167+
files: binaries/objects/*
168+
request_rate: 4
160169

161-
# Same ZERO-tolerance gate as the release. A detection here is a real
162-
# finding even when it is a false positive: it is what the release gate
163-
# will do, so the dry run should say so now. Resolve it upstream (vendor
164-
# false-positive submission for the exact hashes, then re-run) — never by
165-
# loosening this gate.
170+
# Same ZERO-tolerance gate as the release. Every artifact must arrive
171+
# naturally clean; any malicious or suspicious verdict fails this run.
166172
- name: Wait for VirusTotal results
167173
env:
168174
VT_API_KEY: ${{ secrets.VIRUS_TOTAL_SCANNER_API_KEY }}
169175
VT_ANALYSIS: ${{ steps.virustotal.outputs.analysis }}
176+
VT_EXPECTED_SCAN_SET: binaries/scan-set.tsv
177+
VT_ASSOCIATIONS: binaries/associations.tsv
178+
VT_RESULTS_PATH: binaries/vt-results.tsv
179+
MIN_ENGINES: 50
170180
run: scripts/ci/check-virustotal.sh
181+
182+
- name: Preserve VirusTotal scope and result evidence
183+
if: ${{ always() }}
184+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
185+
with:
186+
name: virustotal-evidence-dry-run-${{ github.run_id }}
187+
path: |
188+
binaries/associations.tsv
189+
binaries/scan-set.tsv
190+
binaries/vt-results.tsv
191+
if-no-files-found: warn

0 commit comments

Comments
 (0)