Repository navigation
341 lines (325 loc) · 15.2 KB
/
Copy pathci.yml
File metadata and controls
341 lines (325 loc) · 15.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
335
336
337
338
339
340
341
name: CI
on:
push:
branches: [main]
paths-ignore:
- "**.md"
- ".github/**"
- "!.github/workflows/**"
- "tools/hooks/**"
- ".claude/**"
pull_request:
branches: [main]
paths-ignore:
- "**.md"
- ".github/**"
- "!.github/workflows/**"
- "tools/hooks/**"
- ".claude/**"
env:
# Must match Cargo.toml's [package.metadata.oracle] version — the single
# source of truth — as must SQLITE_ORACLE_TARBALL's encoded form. Both are
# enforced by `make version-pin`. The oracle diffs skip green when no
# sqlite3 of exactly this version is found, so an unpinned runner would turn
# the corpus job into theatre rather than a gate.
SQLITE_ORACLE_VERSION: "3.53.4"
SQLITE_ORACLE_TARBALL: "sqlite-autoconf-3530400"
# cargo-mvl-limit is not published to crates.io, so the qualified-subset
# gate builds it from source at this pinned rev (verified against
# rust-limit v0.3.0). Bumping it is a reviewed change, like an oracle bump.
MVL_RUST_REV: "a64eb33e7c9ef9ad031d22a70405e59aa1ad3cb0"
jobs:
# Split out on its own so the check runs fast without blocking the
# heavier jobs from starting. It still fails the PR gate on its own if
# the branch is behind — see the step's own comment for why it exists.
branch-check:
name: Branch up to date
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Need main's history to compare against; the default shallow
# checkout has only this branch's tip.
fetch-depth: 0
# Stands in for branch protection's "Require branches to be up to date
# before merging", which is Pro-only on private repos. Without it, two
# PRs can each pass CI against an older main and break it together once
# both land: on 2026-08-16, #113 and #115 merged 13 seconds apart and
# main aborted with a stack overflow (#118) that neither CI run could
# have seen. Re-running against the real merge base is the whole fix.
#
# NOTE: this reports, it cannot block — enforcing "don't merge a red
# PR" still needs branch protection (make the repo public, or Pro).
- name: Branch must be up to date with main
run: |
set -euo pipefail
git fetch --quiet origin main
# NOT HEAD: for pull_request events actions/checkout checks out
# refs/pull/N/merge, which already contains main — comparing against
# that would make this check pass unconditionally. Compare the real
# branch tip instead.
head="${{ github.event.pull_request.head.sha }}"
git fetch --quiet origin "$head"
if git merge-base --is-ancestor origin/main "$head"; then
echo "up to date with origin/main ($(git rev-parse --short origin/main))"
exit 0
fi
behind=$(git rev-list --count "$head"..origin/main)
echo "::error::Branch is $behind commit(s) behind origin/main. CI would"
echo "::error::validate a state that never gets merged. Rebase and push:"
echo "::error:: git fetch origin && git rebase origin/main"
echo ""
echo "Commits on main not in this branch:"
git log --oneline "$head"..origin/main
exit 1
# Cheap, dependency-free policy gates plus the unit/public-API/proptest
# suite. Runs under stable only.
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: test
# `make test` includes tests whose oracle-fallback path (bootstrapping
# a scratch db via our own CLI instead of the pinned sqlite3) is
# exercised only when no oracle is present — build it here so this
# job's environment matches the old single-job gate exactly, rather
# than silently exercising an untested code path.
- name: Cache pinned sqlite3 oracle
id: cache-sqlite
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: ~/.local/sqlite-oracle
key: sqlite-oracle-${{ env.SQLITE_ORACLE_VERSION }}-${{ runner.os }}
- name: Build pinned sqlite3 oracle
if: steps.cache-sqlite.outputs.cache-hit != 'true'
run: |
set -euo pipefail
curl -fsSL "https://sqlite.org/2026/${SQLITE_ORACLE_TARBALL}.tar.gz" -o sqlite.tar.gz
tar xzf sqlite.tar.gz
cd "${SQLITE_ORACLE_TARBALL}"
./configure --prefix="$HOME/.local/sqlite-oracle"
make -j"$(nproc)"
make install
- name: Verify the oracle is the pinned, non-codec build
run: |
set -euo pipefail
ORACLE="$HOME/.local/sqlite-oracle/bin/sqlite3"
echo "ORACLE_SQLITE3=$ORACLE" >> "$GITHUB_ENV"
found="$("$ORACLE" -version | awk '{print $1}')"
if [ "$found" != "$SQLITE_ORACLE_VERSION" ]; then
echo "error: oracle is $found, expected $SQLITE_ORACLE_VERSION"; exit 1
fi
if "$ORACLE" :memory: "PRAGMA compile_options;" | grep -qi codec; then
echo "error: oracle is a codec build"; exit 1
fi
echo "oracle: $ORACLE ($found, non-codec)"
# Cheap, dependency-free gates first so a policy violation fails the
# job before the expensive steps run.
- run: make version-pin
- run: make check-mod-files
- run: make check-grammar-drift
# The qualified-subset gate. cargo-mvl-limit is not on crates.io, so it
# is built from source and cached on the pinned rev — without this step
# the gate silently never ran in CI, which is how four violations
# reached main (#90 lifetimes/dyn, v0.6.6's debug_assert).
- name: Cache cargo-mvl-limit
id: cache-mvl
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: ~/.cargo/bin/cargo-mvl-limit
key: cargo-mvl-limit-${{ runner.os }}-${{ env.MVL_RUST_REV }}
- name: Build cargo-mvl-limit
if: steps.cache-mvl.outputs.cache-hit != 'true'
run: |
set -euo pipefail
cargo install --git https://github.com/mvl-lang/mvl-rust \
--rev "$MVL_RUST_REV" rust-limit --bin cargo-mvl-limit --locked --force
- run: make check-mvl-limit
- run: make test
# Lint plus the supply-chain / spec-coverage gates. None of these need
# the pinned sqlite3 oracle, so this job never builds it.
lint-and-supply-chain:
name: Lint & supply chain
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: lint
- uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2
with:
# cargo-vet pinned to 0.10.2: a newer release changed
# supply-chain/imports.lock's schema (added a required
# `publisher.user-id` field), breaking CI against the
# committed file without any change to this repo — pin so a
# tool release can't silently break the gate again; bump
# deliberately alongside a regenerated imports.lock.
tool: cargo-deny,cargo-semver-checks,[email protected],cargo-audit
- run: make lint
- run: make check-deny
- name: Check semver compatibility
if: github.event_name == 'pull_request'
run: cargo semver-checks check-release
- name: Supply chain audit (cargo vet)
run: cargo vet
- name: Vulnerability audit (cargo audit)
run: make check-audit
- run: make check-license-headers
- run: make check-assurance
# The oracle-diff corpus, the only job that needs the pinned real
# sqlite3 build.
corpus:
name: Corpus
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: corpus
# ubuntu-latest ships its own sqlite3, but never the pinned version,
# and a mismatched oracle is worse than none: the Apple/system builds
# disagree with upstream on CSV row terminators, which is exactly how
# three CSV bugs reached main unnoticed (#55). Build the pin instead.
- name: Cache pinned sqlite3 oracle
id: cache-sqlite
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: ~/.local/sqlite-oracle
key: sqlite-oracle-${{ env.SQLITE_ORACLE_VERSION }}-${{ runner.os }}
- name: Build pinned sqlite3 oracle
if: steps.cache-sqlite.outputs.cache-hit != 'true'
run: |
set -euo pipefail
curl -fsSL "https://sqlite.org/2026/${SQLITE_ORACLE_TARBALL}.tar.gz" -o sqlite.tar.gz
tar xzf sqlite.tar.gz
cd "${SQLITE_ORACLE_TARBALL}"
./configure --prefix="$HOME/.local/sqlite-oracle"
make -j"$(nproc)"
make install
- name: Verify the oracle is the pinned, non-codec build
run: |
set -euo pipefail
ORACLE="$HOME/.local/sqlite-oracle/bin/sqlite3"
echo "ORACLE_SQLITE3=$ORACLE" >> "$GITHUB_ENV"
found="$("$ORACLE" -version | awk '{print $1}')"
if [ "$found" != "$SQLITE_ORACLE_VERSION" ]; then
echo "error: oracle is $found, expected $SQLITE_ORACLE_VERSION"; exit 1
fi
if "$ORACLE" :memory: "PRAGMA compile_options;" | grep -qi codec; then
echo "error: oracle is a codec build"; exit 1
fi
echo "oracle: $ORACLE ($found, non-codec)"
- run: make test-corpus
# Short, crash-only pass of every libFuzzer target (make fuzz-smoke) —
# blocking, unlike the `informational` job below: a crash here means one
# of the project's own no-panic-totality claims (spec 003 Req 6, spec
# 008 Req 2, spec 009's opcode-dispatch totality, #89) is false, which is
# exactly the class of bug this gate exists to catch before merge, not
# to merely report. Needs nightly (cargo-fuzz's ASan instrumentation).
fuzz-smoke:
name: Fuzz smoke
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # nightly
with:
toolchain: nightly
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: fuzz-smoke
workspaces: ". -> target\ntests/fuzz -> target"
- name: Cache cargo-fuzz
id: cache-cargo-fuzz
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: ~/.cargo/bin/cargo-fuzz
key: cargo-fuzz-${{ runner.os }}
- name: Install cargo-fuzz
if: steps.cache-cargo-fuzz.outputs.cache-hit != 'true'
run: cargo install cargo-fuzz --locked
- run: make fuzz-smoke
# Informational only (#96) — never blocks the PR gate. Isolated in its
# own job so `continue-on-error` steps and the nightly toolchain they
# need don't sit on the critical path of the actual gates above.
informational:
name: Informational
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# nightly first, stable last: dtolnay/rust-toolchain sets whichever
# toolchain it just installed as the default for every later step in
# this job (no opt-out flag) — installing stable last is what makes
# the bare `cargo test` in `make test-sqllogictest` run under stable
# rather than nightly.
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # nightly
with:
toolchain: nightly
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
shared-key: informational
- uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2
with:
tool: cargo-udeps
- name: Cache pinned sqlite3 oracle
id: cache-sqlite
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v4
with:
path: ~/.local/sqlite-oracle
key: sqlite-oracle-${{ env.SQLITE_ORACLE_VERSION }}-${{ runner.os }}
- name: Build pinned sqlite3 oracle
if: steps.cache-sqlite.outputs.cache-hit != 'true'
run: |
set -euo pipefail
curl -fsSL "https://sqlite.org/2026/${SQLITE_ORACLE_TARBALL}.tar.gz" -o sqlite.tar.gz
tar xzf sqlite.tar.gz
cd "${SQLITE_ORACLE_TARBALL}"
./configure --prefix="$HOME/.local/sqlite-oracle"
make -j"$(nproc)"
make install
- name: Verify the oracle is the pinned, non-codec build
run: |
set -euo pipefail
ORACLE="$HOME/.local/sqlite-oracle/bin/sqlite3"
echo "ORACLE_SQLITE3=$ORACLE" >> "$GITHUB_ENV"
found="$("$ORACLE" -version | awk '{print $1}')"
if [ "$found" != "$SQLITE_ORACLE_VERSION" ]; then
echo "error: oracle is $found, expected $SQLITE_ORACLE_VERSION"; exit 1
fi
if "$ORACLE" :memory: "PRAGMA compile_options;" | grep -qi codec; then
echo "error: oracle is a codec build"; exit 1
fi
echo "oracle: $ORACLE ($found, non-codec)"
- name: Check for unused dependencies
run: cargo +nightly udeps --all-targets
continue-on-error: true # informational until clean
# The sqllogictest slice is a coverage ratchet rather than a
# pass/fail contract: today it attempts ~7% of the vendored corpus
# and skips the rest as out-of-slice, so a red run here means "a
# query we DO support diverged", which is worth surfacing but is
# already covered by the gating suites above. Flip
# `continue-on-error` off once the slice's coverage is high enough
# that a divergence should block a merge on its own.
- name: sqllogictest slice (informational)
continue-on-error: true
run: make test-sqllogictest
# The runner rewrites tools/sqllogictest-status.json in place, and
# tools/assurance.py publishes those numbers. Without this, the
# committed metric could drift from what a run actually produces
# and nothing would say so. Informational like the step above —
# the point is to make drift visible in the job log, not to block.
- name: sqllogictest status drift (informational)
continue-on-error: true
run: |
if ! git diff --exit-code -- tools/sqllogictest-status.json; then
echo "::warning::tools/sqllogictest-status.json is stale — re-run 'make test-sqllogictest' and commit the result"
fi