-
Notifications
You must be signed in to change notification settings - Fork 2
334 lines (297 loc) · 12.2 KB
/
Copy pathci.yml
File metadata and controls
334 lines (297 loc) · 12.2 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
332
333
334
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# Weekly run so new RUSTSEC advisories surface via cargo-deny even when no
# commits land (full-review OL3). Off-peak minute per fleet guidance.
schedule:
- cron: "43 6 * * 1"
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
jobs:
changes:
name: Changes
runs-on: ci-pool-ops
timeout-minutes: 10
outputs:
all: ${{ steps.classify.outputs.all }}
docs: ${{ steps.classify.outputs.docs }}
workflow: ${{ steps.classify.outputs.workflow }}
rust: ${{ steps.classify.outputs.rust }}
web: ${{ steps.classify.outputs.web }}
docker: ${{ steps.classify.outputs.docker }}
release: ${{ steps.classify.outputs.release }}
skills: ${{ steps.classify.outputs.skills }}
security: ${{ steps.classify.outputs.security }}
mcp: ${{ steps.classify.outputs.mcp }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- name: Prepare trusted changed path classifier
run: |
set -euo pipefail
classifier="scripts/ci/changed_paths.py"
trusted="/tmp/cortex-changed-paths.py"
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
if ! git show "${{ github.event.pull_request.base.sha }}:$classifier" > "$trusted" 2>/dev/null; then
cat > "$trusted" <<'PY'
#!/usr/bin/env python3
import argparse
from pathlib import Path
keys = "all docs workflow rust web docker release skills security mcp".split()
parser = argparse.ArgumentParser()
parser.add_argument("--event", required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--write-changed-files", type=Path)
args, _ = parser.parse_known_args()
if args.write_changed_files:
args.write_changed_files.write_text("")
args.output.write_text("".join(f"{key}=true\n" for key in keys))
for key in keys:
print(f"{key}=true")
PY
fi
else
cp "$classifier" "$trusted"
fi
chmod +x "$trusted"
echo "CORTEX_CHANGED_PATHS=$trusted" >> "$GITHUB_ENV"
- name: Classify changed paths
id: classify
env:
EVENT_NAME: ${{ github.event_name }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: python3 "$CORTEX_CHANGED_PATHS" --event "$EVENT_NAME" --output "$GITHUB_OUTPUT" --write-changed-files changed-files.txt
version-sync:
name: Version Sync
runs-on: ci-pool-ops
timeout-minutes: 10
needs: [changes]
if: ${{ needs.changes.outputs.release == 'true' || needs.changes.outputs.skills == 'true' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.workflow == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
# Drives release/components.toml: every version-bearing file must agree
# with Cargo.toml, and plugin manifests must carry no top-level version.
- name: Check version sync
run: cargo xtask check-version-sync
- name: Check agent memory symlinks
run: bash scripts/check-agent-memory-symlinks.sh
- name: Check Rust module sizes
run: bash scripts/check-rust-module-size.sh --limit 500
identity:
name: Public Identity
runs-on: ci-pool-ops
timeout-minutes: 10
needs: [changes]
# check-public-identity.sh scans config/*, scripts/*, .github/*,
# .claude-plugin/*, plugins/*, docs/*, server.json and mcpb/manifest.json.
# It needs no Rust toolchain, so it runs standalone rather than riding along
# with a job whose gate covers only part of that surface.
if: >-
${{ needs.changes.outputs.docs == 'true'
|| needs.changes.outputs.rust == 'true'
|| needs.changes.outputs.release == 'true'
|| needs.changes.outputs.skills == 'true'
|| needs.changes.outputs.docker == 'true'
|| needs.changes.outputs.workflow == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Check public identity strings
run: |
bash scripts/check-public-identity.sh
bash scripts/check-private-identifiers.sh
bash scripts/test-private-identifiers.sh
bash scripts/test-render-deploy-templates.sh
fmt:
name: Formatting
runs-on: ci-pool-rust
timeout-minutes: 10
needs: [changes]
if: ${{ needs.changes.outputs.rust == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
with:
components: rustfmt
- name: Check formatting
run: cargo fmt -- --check
clippy:
name: Clippy
runs-on: ci-pool-rust
timeout-minutes: 30
needs: [changes]
if: ${{ needs.changes.outputs.rust == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
with:
components: clippy
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
test:
name: Tests
runs-on: ci-pool-rust
timeout-minutes: 30
needs: [changes]
if: ${{ needs.changes.outputs.rust == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
- name: Install nextest
uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2
with:
tool: cargo-nextest
- name: Run tests
# nextest gives each test its own process, which is the only safe way
# to run the env-mutating setup/heartbeat/plugin tests — libtest's
# shared-process model lets them race. This matches `just test` and the
# coverage job below. --locked: CI must never silently re-resolve
# Cargo.lock (full-review BM8 — an unlocked run is how rmcp drifted to
# 1.7 unnoticed). env -u mirrors the Justfile's canonical invocation.
run: env -u CORTEX_API_TOKEN -u NO_AUTH cargo nextest run --locked
- name: Run doctests
# nextest does not execute doctests; run them explicitly so coverage
# matches the previous `cargo test` invocation.
run: cargo test --doc --locked
docs-contract:
name: Docs Contract
runs-on: ci-pool-ops
timeout-minutes: 30
needs: [changes]
# When `rust` is true, the Tests job already runs docs_tests:: via nextest,
# so this job would rebuild and rerun the same tests for nothing. It exists
# to cover docs/release/skills/docker/workflow-only PRs where the Tests job
# is skipped but the include_str! docs contracts could still drift.
if: >-
${{ needs.changes.outputs.rust != 'true'
&& (needs.changes.outputs.docs == 'true'
|| needs.changes.outputs.release == 'true'
|| needs.changes.outputs.skills == 'true'
|| needs.changes.outputs.docker == 'true'
|| needs.changes.outputs.workflow == 'true') }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
- name: Verify documentation contracts
# These focused tests compile docs embedded with include_str!, catching
# drift in README and current operator docs without running the full suite.
run: |
env -u CORTEX_API_TOKEN -u NO_AUTH cargo test --lib --locked docs_tests::
coverage:
name: Coverage
runs-on: ci-pool-rust
timeout-minutes: 30
needs: [changes, test]
# PR runs already execute the full suite in the Tests job; the instrumented
# rebuild + second full run here only produces a --summary-only report that
# nothing consumes, so keep it to push/schedule/dispatch.
if: ${{ needs.changes.outputs.rust == 'true' && needs.test.result == 'success' && github.event_name != 'pull_request' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Rust and kache
uses: ./.github/actions/setup-rust-kache
with:
components: llvm-tools-preview
- name: Install coverage tools
uses: taiki-e/install-action@43aecc8d72668fbcfe75c31400bc4f890f1c5853 # v2
with:
tool: cargo-llvm-cov,cargo-nextest
- name: Run coverage summary
run: env -u CORTEX_API_TOKEN -u NO_AUTH cargo llvm-cov nextest --summary-only --locked
deny:
name: Dependency Check (cargo-deny)
runs-on: ci-pool-rust
timeout-minutes: 10
needs: [changes]
if: ${{ needs.changes.outputs.security == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2
with:
command: check
arguments: --all-features
mcp-integration:
name: MCP Integration Tests
runs-on: ci-pool-system
timeout-minutes: 30
needs: [changes, test]
if: ${{ needs.test.result == 'success' && (needs.changes.outputs.rust == 'true' || needs.changes.outputs.mcp == 'true') && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Run integration tests
env:
CORTEX_TOKEN: ci-integration-token
run: bash tests/test_live.sh
ci-gate:
name: CI Gate
runs-on: ci-pool-ops
timeout-minutes: 10
if: always()
needs:
- changes
- version-sync
- identity
- fmt
- clippy
- test
- docs-contract
- coverage
- deny
- mcp-integration
steps:
- name: Verify required jobs passed or were intentionally skipped
run: |
set -euo pipefail
require_success_or_skipped() {
local name="$1"
local result="$2"
case "$result" in
success|skipped) printf '%s=%s\n' "$name" "$result" ;;
*) echo "::error::$name concluded $result" >&2; exit 1 ;;
esac
}
require_success_or_skipped changes "${{ needs.changes.result }}"
require_success_or_skipped version-sync "${{ needs.version-sync.result }}"
require_success_or_skipped identity "${{ needs.identity.result }}"
require_success_or_skipped fmt "${{ needs.fmt.result }}"
require_success_or_skipped clippy "${{ needs.clippy.result }}"
require_success_or_skipped test "${{ needs.test.result }}"
require_success_or_skipped docs-contract "${{ needs.docs-contract.result }}"
require_success_or_skipped coverage "${{ needs.coverage.result }}"
require_success_or_skipped deny "${{ needs.deny.result }}"
require_success_or_skipped mcp-integration "${{ needs.mcp-integration.result }}"
npm:
name: npm launcher
runs-on: ci-pool-typescript
timeout-minutes: 10
needs: [changes]
if: ${{ needs.changes.outputs.release == 'true' || needs.changes.outputs.workflow == 'true' || needs.changes.outputs.docs == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
- name: Test npm launcher
run: npm test --prefix packages/cortex-rmcp
- name: Check npm launcher syntax
run: npm run check --prefix packages/cortex-rmcp
- name: Dry-run npm package
run: npm pack --dry-run --json ./packages/cortex-rmcp