-
Notifications
You must be signed in to change notification settings - Fork 1
331 lines (275 loc) · 11.8 KB
/
Copy pathci.yml
File metadata and controls
331 lines (275 loc) · 11.8 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
329
330
331
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
mastervault-mcp-server/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install mastervault-mcp-server dependencies
working-directory: mastervault-mcp-server
run: npm ci
- name: Lint frontend
working-directory: frontend
run: npm run lint
- name: Typecheck frontend
working-directory: frontend
run: npx tsc -b
- name: Typecheck app
working-directory: app
run: npx tsc -p tsconfig.json --noEmit
- name: Test frontend
working-directory: frontend
run: npm test
- name: Test app
working-directory: app
run: npm test
- name: Build frontend
working-directory: frontend
run: npm run build
- name: Build app
working-directory: app
run: npm run build
- name: Build bundled mastervault-mcp-server (the copy ModelForge packages)
working-directory: mastervault-mcp-server
run: npm run build:bundled
- name: Smoke-test the bundled mastervault-mcp-server over stdio
working-directory: mastervault-mcp-server
run: |
set -euo pipefail
mkdir -p /tmp/mastervault-ci-vault
echo '# CI test vault' > /tmp/mastervault-ci-vault/_orientation.md
printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"1"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| node dist-bundled/index.js /tmp/mastervault-ci-vault > worker-output.json
cat worker-output.json
test "$(grep -o 'mastervault_orient' worker-output.json | wc -l)" -ge 1
e2e:
# Electron E2E tests spin up a real Electron process and a Vite preview
# server per test, which is slower and flakier than the unit-test job
# above — kept as its own job so a flaky e2e run never blocks/hides a
# unit-test failure, and so it can be retried or skipped independently.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
e2e/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install e2e dependencies
working-directory: e2e
run: npm ci
- name: Install Playwright browsers
working-directory: e2e
run: npx playwright install --with-deps chromium
- name: Build frontend
working-directory: frontend
run: npm run build
- name: Build app
working-directory: app
run: npm run build
- name: Run e2e suite
working-directory: e2e
# Electron needs a real (or virtual) display even headless-ish, hence
# xvfb — there's no way to launch a BrowserWindow without one on Linux.
run: xvfb-run --auto-servernum npx playwright test
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: e2e/playwright-report
retention-days: 7
rust:
# lib/ (modelforge-native) is the napi-rs addon backing GGUF downloads
# and, more recently, the JSON datastore/audit-hashing primitives (see
# docs/ARCHITECTURE.md). fmt/clippy/cargo test only prove the Rust side
# is correct on whichever OS runs them — they say nothing about whether
# the resulting `.node` binary actually loads under Node on that OS,
# which is a materially different failure mode (wrong ABI, missing
# platform target, packaging mismatch). Previously this job only ran on
# ubuntu-latest, so nothing in CI ever built or loaded the addon on
# Windows or macOS despite installers shipping for both — see
# docs/RUST_MIGRATION_ASSESSMENT.md's platform-matrix section. The
# matrix below and the "napi build + require() smoke test" step close
# that gap.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: lib
steps:
- uses: actions/checkout@v4
- name: Install rustfmt and clippy
run: rustup component add rustfmt clippy
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
lib/target
key: ${{ runner.os }}-cargo-${{ hashFiles('lib/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
# fmt/clippy only need to run once — Rust source and formatting are
# identical across OSes, so repeating this on all three runners would
# just burn CI minutes for the same answer three times.
- name: Format check
if: matrix.os == 'ubuntu-latest'
run: cargo fmt --check
- name: Clippy
if: matrix.os == 'ubuntu-latest'
run: cargo clippy --all-targets -- -D warnings
- name: Build
run: cargo build --release
- name: Test
run: cargo test
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: lib/package-lock.json
- name: Install napi CLI
run: npm ci
- name: napi build (produces the real platform-specific .node binary)
run: npm run build:debug
# The actual gap this job exists to close: proves Node on *this* OS
# can load the binary napi build just produced, not just that Cargo
# compiled it. A wrong target triple, an ABI mismatch, or a packaging
# error would fail cargo build's cross-compile silently-succeed case
# but fail right here.
- name: Load the built addon under Node and verify its exports
shell: bash
run: |
node -e "
const addon = require('../app/native');
const expected = ['downloadGgufFile', 'DownloadManager', 'readJsonFileNative', 'writeJsonFileAtomicNative', 'sha256HexNative', 'appendJsonArrayElementNative'];
const missing = expected.filter((name) => !(name in addon));
if (missing.length > 0) {
console.error('Native addon loaded but is missing expected exports:', missing);
process.exit(1);
}
console.log('Native addon loaded successfully on ${{ matrix.os }} with all expected exports.');
"
sbom:
# Generates a CycloneDX SBOM per npm workspace so every release has an
# auditable, machine-readable dependency manifest — required groundwork
# for the supply-chain/provenance controls flagged in
# docs/ENTERPRISE_READINESS_ASSESSMENT.md, and useful on its own for
# answering "are we affected by CVE-X" without re-deriving the dependency
# tree by hand. Runs independently of the test job so a flaky SBOM
# generation never blocks the build/test signal.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
mastervault-mcp-server/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install mastervault-mcp-server dependencies
working-directory: mastervault-mcp-server
run: npm ci
- name: Generate SBOM (frontend)
working-directory: frontend
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-frontend.cdx.json
- name: Generate SBOM (app)
working-directory: app
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-app.cdx.json
- name: Generate SBOM (mastervault-mcp-server)
working-directory: mastervault-mcp-server
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-mastervault-mcp-server.cdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom-*.cdx.json
retention-days: 90
python-recommender:
# ml/hardware-recommender/ is a standalone project (its own venv, own
# train/download-dataset scripts) that isn't part of the app's build —
# only its exported ONNX model ships with the app
# (app/python/artifacts/hardware_recommender.onnx), loaded at runtime by
# app/python/recommender_worker.py. This job is deliberately scoped to
# what release publishing actually depends on: the recommender package's
# own unit tests (data/feature encoding logic, no dataset or GPU needed)
# and a smoke test that the packaged worker script can actually load the
# shipped artifact and answer a request — never train.py or
# download_dataset.py, which need a real (large) dataset pull.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: ml/hardware-recommender/requirements.txt
- name: Install dependencies (CPU-only torch for ML tests)
working-directory: ml/hardware-recommender
# torch's CPU wheels live on a separate index from the rest of
# PyPI. Install it separately to keep the training/test stack CPU-only
# and avoid a multi-gigabyte CUDA wheel download on a GPU-less runner;
# the packaged app uses ONNX Runtime for inference and does not ship
# torch in its managed runtime.
run: |
python -m pip install --upgrade pip
python -m pip install --only-binary=:all: --index-url https://download.pytorch.org/whl/cpu torch==2.13.0
# pip parses comments and blank lines correctly. Passing the file
# through shell command substitution turned the first comment into
# a literal `#` requirement and failed the job.
python -m pip install --only-binary=:all: -r requirements.txt
- name: Run hardware-recommender unit tests
working-directory: ml/hardware-recommender
run: python -m pytest tests/ -q
- name: Verify the packaged model artifacts exist
run: |
test -f app/python/artifacts/hardware_recommender.onnx
test -f app/python/artifacts/hardware_recommender.onnx.sha256
test -f app/python/artifacts/hardware_recommender.meta.json
- name: Smoke-test the packaged recommender worker against the shipped artifact
working-directory: app/python
run: |
printf '%s\n%s\n' \
'{"protocol":1,"id":"1","method":"health","params":{}}' \
'{"protocol":1,"id":"2","method":"recommend","params":{"model_params_b":7,"ram_gb":16,"vram_gb":8,"cpu_cores":8,"platform":"linux","gpu_backend":"cuda"}}' \
| python recommender_worker.py > worker-output.json
cat worker-output.json
test "$(grep -o '"ok": *true' worker-output.json | wc -l)" -eq 2