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
# The full `--all-features` test suite, split out of the PR gate (ci.yaml).
#
# What runs only here and not on the PR gate:
# * the Docker-backed vector-store integration tests in `tests/integrations/`
# (lancedb, mongodb, neo4j, postgres, qdrant, scylladb — plus the
# no-container sqlite and vectorize suites), the only runtime coverage the
# companion crates' `VectorStoreIndex` implementations have;
# * `tests/tool_facade_features.rs` (`facade-build-tests`), the slow
# nested-`cargo check` facade build guard;
# * the `image`-feature generation smoke tests (gemini nano-banana, xai);
# * every root test binary's `--all-features` configuration — the PR gate
# runs the default feature set plus `bedrock`.
#
# The PR gate still COMPILES all of the above on every PR: ci.yaml's clippy
# job lints `--all-features --all-targets`, and doctest/doc build all
# features, so a nightly failure is almost always a runtime regression rather
# than a stale compile break discovered at 06:00 — with one deliberate
# exception. `tests/tool_facade_features.rs` nested-checks the facade in
# *individual* feature configurations, and feature-unification means no
# `--all-features` build on the gate can catch a facade feature that forgets
# to forward a sub-feature, so that one class of compile break is caught only
# here (it costs ~4 minutes, which is exactly why it is not on the gate).
#
# When it runs:
# * nightly at 06:00 UTC;
# * on any PR that touches a companion crate, the integration tests, or the
# root manifest/lockfile (where the companion crates' `workspace = true`
# dependency versions actually resolve) — without this, a PR editing
# `crates/rig-qdrant/` or bumping its driver in Cargo.toml merges with
# zero runtime coverage of the code it changed, and the breakage surfaces
# either at 06:00 or, worse, in cd.yaml's `run-full-ci`, where it blocks
# release-plz on an already-merged commit. These PRs share the scheduled
# run's rust-cache entry, so the common PR pays nothing;
# * in the merge queue (`merge_group`), because the squashed batch that
# actually lands on main is not any PR head the filters above validated;
# * on demand via `gh workflow run nightly.yaml`;
# * via `workflow_call` from cd.yaml on every push to main, so release-plz
# never publishes a commit the full suite has not passed.
name: Nightly Full Test
on:
schedule:
- cron: "0 6 * * *"
# Exactly the crates that `tests/integrations/` actually exercises. Other
# companion crates (rig-milvus, rig-surrealdb, rig-helixdb, …) are omitted
# deliberately: they have no suite here, so triggering this lane on them
# would cost 20+ minutes and run nothing that covers the changed code.
pull_request:
paths:
- "crates/rig-lancedb/**"
- "crates/rig-mongodb/**"
- "crates/rig-neo4j/**"
- "crates/rig-postgres/**"
- "crates/rig-qdrant/**"
- "crates/rig-scylladb/**"
- "crates/rig-sqlite/**"
- "crates/rig-vectorize/**"
- "tests/integrations/**"
- "tests/integrations.rs"
# The facade feature-forwarding guard is the one test whose failure
# class (a facade feature forgetting a sub-feature, invisible to any
# `--all-features` build — see the header) nothing on the PR gate can
# catch, so a PR that edits it must run it.
- "tests/tool_facade_features.rs"
- ".github/workflows/nightly.yaml"
# The companion crates' dependency versions do not live under
# `crates/rig-*/`: they are `workspace = true` references resolved in
# the root manifest and lockfile. Without these two entries, a
# dependabot PR bumping mongodb/neo4rs/qdrant-client/lancedb touches
# only Cargo.toml + Cargo.lock, matches none of the globs above, and
# merges with zero integration-test coverage of the driver it changed —
# the exact failure mode this trigger exists to prevent. The cost is
# accepted: any PR that touches the root manifest or lockfile runs the
# full lane, because dependency changes are precisely the risk class
# this suite covers. (`crates/rig-core/**` is deliberately NOT listed:
# most PRs touch rig-core, and triggering the full lane on all of them
# would undo the fast/full split; a rig-core change that breaks a
# vector-store contract is caught by merge_group below at the latest.)
- "Cargo.toml"
- "Cargo.lock"
- "crates/*/Cargo.toml"
- "scripts/check-dependency-floors.py"
# The repo merges through a merge queue (ALLGREEN, squash, batches up to
# 5), so the commit that actually lands on main is the merge-group commit —
# not the PR head that the `pull_request` triggers above validated. Without
# this trigger, a companion-crate PR could go green on its head, get
# batched with other queued PRs, and land a combination the full suite
# never ran on, surfacing post-merge in cd.yaml where it blocks release-plz
# (the pre-split workflow ran the full sweep under merge_group, so this
# restores that guarantee). GitHub does not support `paths` filters on
# merge_group events, so every queued merge pays the full lane — accepted:
# the queue batches PRs, which amortizes the cost across everything in the
# batch.
merge_group:
workflow_dispatch: # cd.yaml already uses this; match it
workflow_call:
# The toolchain and the cache-fingerprint env (debuginfo trim,
# CARGO_INCREMENTAL) come from .github/actions/rust-setup, the same single
# source ci.yaml uses — which is what actually guarantees the two lanes
# share build fingerprints instead of a comment asking copies to stay
# identical.
env:
CARGO_TERM_COLOR: always
# The `full-` prefix keeps this group disjoint from ci.yaml's when both are
# invoked from the same cd.yaml run (inside a called workflow,
# `github.workflow` resolves to the CALLER's name). Keyed on the PR number
# rather than `head_ref` because two forks PRing from identically-named
# branches share a head_ref — under the old key they cancelled each other's
# runs.
concurrency:
group: full-${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
test-all-features:
name: stable / test all features
runs-on: ubuntu-latest
# `schedule` fires on any fork whose owner has enabled Actions, against
# the fork's default branch — so without this guard every fork owner gets
# a daily 06:00 run (Docker pulls and a full workspace build) for a suite
# they did not ask for. Fork *PRs* into this repo are unaffected:
# `github.repository` is the upstream repo there, and the suite is fully
# hermetic — cassettes replay with a dummy key, the vector-store tests
# stub their embeddings with `api_key("TEST")`, and no job receives
# provider secrets anywhere in CI.
#
# This also covers the `workflow_call` path, where `github.repository` is
# still the caller's repo — a fork's own cd.yaml skips this job and, with
# it, its release-plz step, which is the correct outcome on a fork.
if: github.repository == '0xPlaygrounds/rig'
steps:
- name: Checkout
uses: actions/checkout@v5
# protoc: required because this all-features lane enables rig-lancedb
# and the facade's feature-gated raw lancedb test dependency.
#
# No sccache: the all-features sweep mints by far the most
# per-compilation-unit cache entries, this lane is not
# latency-critical, and the repo's 10GB Actions cache quota is
# contended — see the sccache input's note in the rust-setup action.
- name: Install Rust stable
uses: ./.github/actions/rust-setup
with:
nextest: "true"
protoc: "true"
# The workspace-wide sweep over every feature. This is the step that
# was ci.yaml's `Test with latest nextest release` before the fast/full
# split narrowed that one to `--features bedrock`. No provider API keys
# here or anywhere in CI: everything that executes is hermetic (see
# ci.yaml's sweep-step note), and recording new cassettes is a local,
# human activity.
#
# A plain `run:` rather than `actions-rs/cargo@v1`: that action was
# archived in 2023 and pins Node 20.
- name: Test with all features
run: cargo nextest run --locked --all-features --retries 2
# The declared dependency requirements are floors, not pins (`tokio = "1"`
# is `^1`), and a downstream resolves anywhere inside them — usually to
# whatever its own lockfile already holds. So rig has to actually build at
# the lowest version it declares, or the floor is a lie that surfaces as a
# build break in someone else's tree (#2195). Cargo's `-Zdirect-minimal-
# versions` is nightly-only and dead-ends in this workspace's transitive
# graph, so `scripts/check-dependency-floors.py` does the equivalent on
# stable: `cargo update --precise` every direct dependency down to the
# lowest version the tree admits, then `cargo check --workspace
# --all-features --all-targets`. It runs here rather than on the PR gate
# because it is a full all-features build; the `pull_request` paths above
# still run it on every PR that touches a manifest, the lockfile, or the
# script itself, which is where a floor can silently rot (a PR that starts
# using an API newer than the declared floor).
dependency-floors:
name: stable / dependency floors
runs-on: ubuntu-latest
if: github.repository == '0xPlaygrounds/rig'
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Rust stable
uses: ./.github/actions/rust-setup
with:
protoc: "true"
- name: Build against the declared dependency floors
run: python3 scripts/check-dependency-floors.py