-
Notifications
You must be signed in to change notification settings - Fork 2.2k
328 lines (302 loc) · 15.3 KB
/
Copy path_smoke.yml
File metadata and controls
328 lines (302 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# Reusable: smoke test every binary (standard + UI, all platforms)
name: Smoke
on:
workflow_call:
inputs:
broad_platforms:
description: 'Smoke the shipped binaries on the broad platform matrix (extra OS versions) instead of the core set'
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 (additional OS versions)
# that download the SAME shipped artifact for their goos/goarch and verify it
# runs on a wider range of OS versions. No new artifacts are built — broad
# legs reuse the exact binaries produced by _build.yml.
setup-matrix:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
unix: ${{ steps.set.outputs.unix }}
windows: ${{ steps.set.outputs.windows }}
portable: ${{ steps.set.outputs.portable }}
steps:
- name: Compute matrices
id: set
env:
BROAD: ${{ inputs.broad_platforms }}
run: |
CORE_UNIX='[
{"os":"ubuntu-latest","goos":"linux","goarch":"amd64"},
{"os":"ubuntu-24.04-arm","goos":"linux","goarch":"arm64"},
{"os":"macos-14","goos":"darwin","goarch":"arm64"},
{"os":"macos-15-intel","goos":"darwin","goarch":"amd64"}
]'
# Broad legs reuse existing goos/goarch artifacts on additional OS
# versions to widen the run-anywhere signal without building new targets.
# Broad legs are REQUIRED gates (no optional / continue-on-error):
# every smoke leg must pass. No `optional` flags anywhere.
#
# NOTE: the *dynamic* linux binary links glibc 2.38+ and cannot run on
# older distros by design — older-glibc coverage is the -portable (static)
# binary's job, exercised green by the smoke-linux-portable broad legs
# (ubuntu-22.04 / 22.04-arm). So the dynamic broad legs stay on
# forward-compatible OSes only (macOS); running the dynamic binary on
# ubuntu-22.04 would fail Phase 1 (glibc too old), not a real regression.
BROAD_UNIX='[
{"os":"macos-15","goos":"darwin","goarch":"arm64"}
]'
CORE_WIN='[{"os":"windows-latest","arch":"amd64"}]'
# windows-11-arm now runs the NATIVE arm64 binary (build-windows-arm64),
# not the x86_64 binary under emulation — we smoke the artifact we ship.
BROAD_WIN='[{"os":"windows-2025","arch":"amd64"},{"os":"windows-11-arm","arch":"arm64"}]'
CORE_PORTABLE='[
{"arch":"amd64","runner":"ubuntu-latest"},
{"arch":"arm64","runner":"ubuntu-24.04-arm"}
]'
BROAD_PORTABLE='[
{"arch":"amd64","runner":"ubuntu-22.04"},
{"arch":"arm64","runner":"ubuntu-22.04-arm"}
]'
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')
PORTABLE=$(jq -cn --argjson a "$CORE_PORTABLE" --argjson b "$BROAD_PORTABLE" '$a + $b')
else
UNIX=$(jq -cn --argjson a "$CORE_UNIX" '$a')
WIN=$(jq -cn --argjson a "$CORE_WIN" '$a')
PORTABLE=$(jq -cn --argjson a "$CORE_PORTABLE" '$a')
fi
# Expand each OS entry over both variants into a FLAT include list so
# every {os,variant} runs as its own job. A {variant:[...],include:$LIST}
# matrix collapses under GitHub's include-merge (later os entries
# overwrite the variant combos last-wins), which silently dropped every
# OS except the last — e.g. only windows-11-arm ran, never windows-latest.
# Building the cartesian explicitly avoids that.
VARIANTS='["standard","ui"]'
UNIX_M=$(jq -cn --argjson a "$UNIX" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
WIN_M=$(jq -cn --argjson a "$WIN" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
PORTABLE_M=$(jq -cn --argjson a "$PORTABLE" --argjson v "$VARIANTS" '{include:[$a[] as $o | $v[] as $t | ($o + {variant:$t})]}')
echo "unix=$UNIX_M" >> "$GITHUB_OUTPUT"
echo "windows=$WIN_M" >> "$GITHUB_OUTPUT"
echo "portable=$PORTABLE_M" >> "$GITHUB_OUTPUT"
smoke-unix:
needs: setup-matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup-matrix.outputs.unix) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
- name: Extract binary
run: |
SUFFIX=${{ matrix.variant == 'ui' && '-ui' || '' }}
tar -xzf codebase-memory-mcp${SUFFIX}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz
chmod +x codebase-memory-mcp
- name: Start artifact server
run: |
mkdir -p /tmp/smoke-server
cp codebase-memory-mcp /tmp/smoke-server/
OS=${{ matrix.goos }}; ARCH=${{ matrix.goarch }}
SUFFIX=${{ matrix.variant == 'ui' && '-ui' || '' }}
tar -czf "/tmp/smoke-server/codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}.tar.gz" \
-C /tmp/smoke-server codebase-memory-mcp
if [ -n "$SUFFIX" ]; then
cp "/tmp/smoke-server/codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}.tar.gz" \
"/tmp/smoke-server/codebase-memory-mcp-${OS}-${ARCH}.tar.gz"
fi
# The linux binary self-updates from the fully-static "-portable" asset
# (build_update_url in src/cli/cli.c appends -portable on linux; _build.yml's
# build-linux-portable job ships it), so the smoke server must serve that name
# too -- otherwise `update` 404s and Phase 14 fails. Mirror the tarball(s) under
# the -portable name on linux only.
if [ "$OS" = "linux" ]; then
cp "/tmp/smoke-server/codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}.tar.gz" \
"/tmp/smoke-server/codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}-portable.tar.gz"
if [ -n "$SUFFIX" ]; then
cp "/tmp/smoke-server/codebase-memory-mcp${SUFFIX}-${OS}-${ARCH}-portable.tar.gz" \
"/tmp/smoke-server/codebase-memory-mcp-${OS}-${ARCH}-portable.tar.gz"
fi
fi
cd /tmp/smoke-server
sha256sum *.tar.gz > checksums.txt 2>/dev/null || shasum -a 256 *.tar.gz > checksums.txt
python3 -m http.server 18080 -d /tmp/smoke-server &
- name: Smoke test
run: scripts/smoke-test.sh ./codebase-memory-mcp
env:
SMOKE_DOWNLOAD_URL: http://localhost:18080
- name: Security audits
run: |
scripts/security-strings.sh ./codebase-memory-mcp
scripts/security-install.sh ./codebase-memory-mcp
scripts/security-network.sh ./codebase-memory-mcp
- name: MCP robustness test (linux-amd64 standard only)
if: matrix.variant == 'standard' && matrix.goos == 'linux' && matrix.goarch == 'amd64'
run: |
scripts/security-fuzz.sh ./codebase-memory-mcp
scripts/security-fuzz-random.sh ./codebase-memory-mcp 60
- name: ClamAV scan (Linux)
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update -qq && sudo apt-get install -y -qq clamav > /dev/null 2>&1
# apt auto-starts the clamav-freshclam daemon, which holds a lock on
# freshclam's log/db; stop it so the manual freshclam below can run
# (else: "Failed to lock the log file ... Resource temporarily unavailable").
sudo systemctl stop clamav-freshclam 2>/dev/null || true
sudo sed -i 's/^Example/#Example/' /etc/clamav/freshclam.conf 2>/dev/null || true
grep -q "DatabaseMirror" /etc/clamav/freshclam.conf 2>/dev/null || \
echo "DatabaseMirror database.clamav.net" | sudo tee -a /etc/clamav/freshclam.conf > /dev/null
sudo freshclam --quiet
clamscan --no-summary ./codebase-memory-mcp
- name: ClamAV scan (macOS)
if: startsWith(matrix.os, 'macos')
run: |
brew install clamav > /dev/null 2>&1
CLAMAV_ETC=$(brew --prefix)/etc/clamav
if [ ! -f "$CLAMAV_ETC/freshclam.conf" ]; then
cp "$CLAMAV_ETC/freshclam.conf.sample" "$CLAMAV_ETC/freshclam.conf" 2>/dev/null || true
sed -i '' 's/^Example/#Example/' "$CLAMAV_ETC/freshclam.conf" 2>/dev/null || true
echo "DatabaseMirror database.clamav.net" >> "$CLAMAV_ETC/freshclam.conf"
fi
freshclam --quiet --no-warnings 2>/dev/null || freshclam --quiet 2>/dev/null || echo "WARNING: freshclam update failed"
clamscan --no-summary ./codebase-memory-mcp
smoke-windows:
needs: setup-matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup-matrix.outputs.windows) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
with:
msystem: ${{ matrix.arch == 'arm64' && 'CLANGARM64' || 'CLANG64' }}
path-type: inherit
install: >-
mingw-w64-clang-${{ matrix.arch == 'arm64' && 'aarch64' || 'x86_64' }}-python3
mingw-w64-clang-${{ matrix.arch == 'arm64' && 'aarch64' || 'x86_64' }}-curl
unzip
zip
coreutils
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: binaries-windows-${{ matrix.arch }}
- name: Extract binary
shell: msys2 {0}
run: |
SUFFIX=${{ matrix.variant == 'ui' && '-ui' || '' }}
ARCH=${{ matrix.arch }}
unzip -o "codebase-memory-mcp${SUFFIX}-windows-${ARCH}.zip"
[ -n "$SUFFIX" ] && cp "codebase-memory-mcp${SUFFIX}.exe" codebase-memory-mcp.exe || true
- name: Start artifact server
shell: msys2 {0}
run: |
mkdir -p /tmp/smoke-server
cp codebase-memory-mcp.exe /tmp/smoke-server/
SUFFIX=${{ matrix.variant == 'ui' && '-ui' || '' }}
ARCH=${{ matrix.arch }}
cd /tmp/smoke-server
zip -q "codebase-memory-mcp${SUFFIX}-windows-${ARCH}.zip" codebase-memory-mcp.exe
if [ -n "$SUFFIX" ]; then
cp "codebase-memory-mcp${SUFFIX}-windows-${ARCH}.zip" "codebase-memory-mcp-windows-${ARCH}.zip"
fi
sha256sum *.zip > checksums.txt
# Pin to explicit IPv4: on windows-11-arm, `localhost` resolves to ::1 (IPv6)
# for msys2 curl while python's http.server is IPv4-only -> instant connect
# failure in smoke Phase 12a. --bind 127.0.0.1 + a 127.0.0.1 URL pin both ends.
python3 -m http.server 18080 --bind 127.0.0.1 -d /tmp/smoke-server &
- name: Smoke test
shell: msys2 {0}
run: scripts/smoke-test.sh ./codebase-memory-mcp.exe
env:
SMOKE_DOWNLOAD_URL: http://127.0.0.1:18080
SMOKE_ARCH: ${{ matrix.arch }}
- name: Security audits
shell: msys2 {0}
run: |
scripts/security-strings.sh ./codebase-memory-mcp.exe
scripts/security-install.sh ./codebase-memory-mcp.exe
- name: Windows Defender scan
shell: pwsh
run: |
& "C:\Program Files\Windows Defender\MpCmdRun.exe" -SignatureUpdate 2>$null
$result = & "C:\Program Files\Windows Defender\MpCmdRun.exe" -Scan -ScanType 3 -File "$PWD\codebase-memory-mcp.exe" -DisableRemediation
$code = $LASTEXITCODE
Write-Host $result
# MpCmdRun -Scan exit codes: 0 = clean, 2 = threat found. Any OTHER non-zero
# means the scan engine could not run at all (e.g. hr=0x800106ba: the Defender
# antimalware service is unavailable on the runner) — that is NOT a detection.
# Fail soft on an engine failure so a transient runner-side AV outage can't
# false-fail a release; only a real detection (exit 2) hard-blocks.
if ($code -eq 2) {
Write-Host "BLOCKED: Windows Defender flagged binary!"; exit 1
} elseif ($code -ne 0) {
Write-Host "::warning::Windows Defender scan could not run (exit $code) - skipping AV gate on this runner"
} else {
Write-Host "=== Windows Defender: clean ==="
}
smoke-linux-portable:
needs: setup-matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup-matrix.outputs.portable) }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: binaries-linux-${{ matrix.arch }}-portable
- name: Extract and smoke test
run: |
SUFFIX=${{ matrix.variant == 'ui' && '-ui' || '' }}
tar -xzf codebase-memory-mcp${SUFFIX}-linux-${{ matrix.arch }}-portable.tar.gz
chmod +x codebase-memory-mcp
scripts/smoke-test.sh ./codebase-memory-mcp
# The -portable binary is what all linux install/update paths now deliver;
# it MUST start on old glibc (Debian 11 / RHEL 8 / Ubuntu 20.04). Runs it
# in debian:bullseye (glibc 2.31) — the standard dynamic binary would fail
# here with `GLIBC_2.38 not found`.
- name: Old-glibc compatibility
run: scripts/ci/check-glibc-compat.sh ./codebase-memory-mcp
- name: Security audits
run: |
scripts/security-strings.sh ./codebase-memory-mcp
scripts/security-install.sh ./codebase-memory-mcp
scripts/security-network.sh ./codebase-memory-mcp
# ── Packaging check (non-gating) ──────────────────────────────────
# Builds pkg/glama/Dockerfile — the image Glama builds to score the
# server — and runs an MCP initialize + tools/list handshake to confirm the
# containerized stdio server still starts and introspects. Guards the Glama
# listing integration against drift (Dockerfile breakage, release-asset
# renames, introspection regressions).
#
# Also tests the brew tap/install flow.
#
# continue-on-error: a broken *directory* image must never block a release —
# the shipped product binaries are unaffected. The red X is the signal to fix
# the integration, not a release gate.
smoke-packages:
runs-on: ubuntu-latest
timeout-minutes: 10
continue-on-error: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Build Glama image and verify MCP introspection
run: bash pkg/glama/verify.sh
- name: Test homebrew installation
env:
HOMEBREW_NO_AUTO_UPDATE: 1
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
brew tap deusdata/codebase-memory-mcp "$GITHUB_WORKSPACE"
brew trust deusdata/codebase-memory-mcp
brew install codebase-memory-mcp
codebase-memory-mcp --version