Skip to content

Commit 261d2d0

Browse files
committed
Run SDK integration against the matching native client
1 parent 2237505 commit 261d2d0

3 files changed

Lines changed: 83 additions & 65 deletions

File tree

‎.github/workflows/integration.yml‎

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
# Runs the canonical Query-farm/vgi integration sqllogictest suite against the
2-
# Python example worker on every push / PR. Rather than building the C++ extension
3-
# from source, it drives a prebuilt standalone `haybarn-unittest` and installs the
4-
# SIGNED vgi extension from the Haybarn community channel + its deps from core.
5-
# The .test files come from a pinned Query-farm/vgi checkout; ci/run-integration.sh
6-
# + ci/preprocess-require.awk adapt them for the standalone runner. See ci/README.md.
2+
# Python example worker on every push / PR. The native runner comes from the
3+
# Query-farm/vgi Actions build of the same commit as the test suite and statically
4+
# links the matching extension. Public package releases remain separate.
5+
# ci/run-integration.sh also supports the published standalone runner locally.
76
#
87
# (The separate ci.yml covers lint / type-check / unit + conformance tests / docs.
98
# Those drive the pure-Python in-process Client; this workflow is the only place
10-
# the worker is exercised through the real published DuckDB extension over the wire.)
9+
# the worker is exercised through the real DuckDB extension over the wire.)
1110
name: Integration suite
1211

1312
on:
@@ -19,20 +18,16 @@ on:
1918

2019
permissions:
2120
contents: read
21+
actions: read
2222

2323
concurrency:
2424
group: integration-${{ github.ref }}
2525
cancel-in-progress: true
2626

2727
env:
28-
# The Query-farm/vgi ref whose test/sql/integration suite we run. For now this
29-
# tracks the latest `main` (resolved at checkout time) so the Python port is
30-
# always validated against the newest C++ test suite. Pin to a specific commit
31-
# SHA here if/when reproducible, deliberate bumps are wanted (see ci/README.md).
32-
VGI_REF: main
33-
# The Haybarn release providing the prebuilt haybarn-unittest binary. Must be
34-
# ABI-compatible with the community-published vgi extension.
35-
HAYBARN_RELEASE: haybarn-v1.5.5-rc1
28+
# The test suite and native Actions artifact must come from this exact ref.
29+
# The downloader verifies its provenance against the checked-out commit.
30+
VGI_REF: 3a2249f815ff6e2a8b5289a3e6284326084e3630
3631
# CI is not a user: its ATTACHes should not land in Query.Farm's adoption data.
3732
# The vgi extension fires a telemetry event per successful ATTACH, and this
3833
# suite performs several hundred of them per lane, so opting out keeps the
@@ -81,18 +76,13 @@ jobs:
8176
- name: Install dependencies (vgi + fixtures + all extras)
8277
run: uv sync --all-extras --python 3.14
8378

84-
- name: Download haybarn-unittest
79+
- name: Download the matching native integration runner
8580
run: |
86-
gh release download "$HAYBARN_RELEASE" \
87-
--repo Query-farm-haybarn/haybarn \
88-
--pattern 'haybarn_unittest-linux-amd64.zip' \
89-
--output /tmp/haybarn-unittest.zip --clobber
90-
unzip -o -q /tmp/haybarn-unittest.zip -d /tmp/haybarn-unittest
91-
UNITTEST=$(find /tmp/haybarn-unittest -name 'haybarn-unittest' -type f | head -1)
92-
chmod +x "$UNITTEST"
93-
echo "HAYBARN_UNITTEST=$UNITTEST" >> "$GITHUB_ENV"
81+
python3 "$VGI_SRC/scripts/ci_fetch_native_runner.py" \
82+
--source "$VGI_SRC" --output "$RUNNER_TEMP/vgi-native"
9483
env:
9584
GH_TOKEN: ${{ github.token }}
85+
VGI_SRC: ${{ github.workspace }}/vgi-upstream
9686

9787
- name: Run integration suite (${{ matrix.lane }})
9888
run: ci/run-integration.sh

‎ci/README.md‎

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@
44
runs the canonical [Query-farm/vgi](https://github.com/Query-farm/vgi)
55
integration sqllogictest suite against this repo's Python example worker on
66
every push / PR. The same `.test` files run against the Python, Java, and Go
7-
ports, so a green run here is real wire-compatibility evidence — it exercises
8-
the worker through the *published* DuckDB extension, not a mock, and not the
9-
pure-Python in-process `Client` that the conformance tests in `ci.yml` use.
7+
ports, so a green run here verifies the worker through the native DuckDB
8+
extension over the wire. The native runner and test suite come from the same
9+
VGI commit. Public package releases remain separate from this development check.
1010

1111
(The separate [`ci.yml`](../.github/workflows/ci.yml) covers lint / mypy / unit
1212
+ conformance tests / docs / LocalStack S3 offload.)
1313

1414
## How it works (no C++ build)
1515

16-
Rather than building the vgi DuckDB extension from source (which needs the
17-
Haybarn vcpkg pipeline), CI drives a **prebuilt** standalone `haybarn-unittest`
18-
(the DuckDB/Haybarn sqllogictest runner, published in Haybarn's releases) and
19-
installs the **signed** vgi extension from the Haybarn community channel:
16+
CI downloads the native integration runner built by Query-farm/vgi Actions.
17+
It links vgi, httpfs, json and parquet, so the worker's wire contract is checked
18+
against the matching client without waiting for a community extension release.
2019

2120
1. **Install the workers** — `uv sync --all-extras` installs the `vgi-fixtures`
2221
workspace member, which puts the fixture console scripts
@@ -27,17 +26,27 @@ installs the **signed** vgi extension from the Haybarn community channel:
2726
`--http`, and the launcher's `--unix`, so one install covers every lane.
2827
2. **Checkout the test suite** — `Query-farm/vgi` at a pinned commit; its
2928
`test/sql/integration/*.test` files are the suite.
30-
3. **Download the runner** — `haybarn_unittest-linux-amd64.zip` from the pinned
31-
Haybarn release.
32-
4. **Preprocess** — the standalone runner links none of the extensions the tests
33-
gate on, so [`preprocess-require.awk`](preprocess-require.awk) rewrites each
34-
`require <ext>` into an explicit signed `INSTALL <ext> FROM {community,core};
35-
LOAD <ext>;`. `require-env` and everything else pass through.
36-
5. **Run** — [`run-integration.sh`](run-integration.sh) stages the preprocessed
37-
tree, wires the `VGI_*_WORKER` env vars at the `.venv/bin` scripts for the
38-
selected transport, `FORCE INSTALL`s the vgi extension (so the run uses what
39-
users can install today), then runs the suite in a single `haybarn-unittest`
40-
invocation. Any failed assertion exits non-zero and fails the job.
29+
3. **Download the runner** — the pinned checkout's `ci_fetch_native_runner.py`
30+
retrieves its Actions artifact and checks its provenance against that exact
31+
source commit. It exports `HAYBARN_UNITTEST`, `HAYBARN_CLI` and
32+
`RUNNER_LINKS_EXTENSIONS=1` for the next step.
33+
4. **Stage** — [`run-integration.sh`](run-integration.sh) copies the selected
34+
tests unchanged when extensions are linked into the runner. Their normal
35+
`require` gates load linked extensions; unavailable optional extensions keep
36+
their normal skip behavior.
37+
5. **Run** — the harness wires the `VGI_*_WORKER` environment variables to the
38+
`.venv/bin` scripts for the selected transport and executes the suite, with
39+
`simple_writable` in its own process. Native mode passes `--test-dir` so the
40+
runner discovers the staged tree instead of its compiled source directory.
41+
Failed assertions, empty runs, fatal signals and dead shared HTTP workers
42+
fail the job. The no-error-skip configuration keeps HTTP errors from silently
43+
skipping tests.
44+
45+
For local checks against the published extension, leave
46+
`RUNNER_LINKS_EXTENSIONS` unset or set it to `0`. This preserves the standalone
47+
runner path: [`preprocess-require.awk`](preprocess-require.awk) converts
48+
`require` gates to signed `INSTALL` and `LOAD` statements, and the harness
49+
warms the community/core extension cache before running.
4150

4251
The env wiring mirrors the vgi repo's Makefile `test_subprocess` / `test_shm` /
4352
`test_launcher` / `test_http` targets — the canonical local way to run this
@@ -77,9 +86,9 @@ matrix; `stdio` remains available for local compatibility testing:
7786
`test_launcher`), so those tests skip here; run the local stdio lane to cover
7887
them. One deliberate divergence from `test_launcher`: see
7988
`filter_echo_partitioned.test` below.
80-
- **`http`** — the whole suite over the stateless HTTP transport. Staging
81-
injects `LOAD httpfs` before each worker ATTACH (the prebuilt binary doesn't
82-
statically link httpfs).
89+
- **`http`** — the whole suite over the stateless HTTP transport. The native
90+
runner links httpfs; standalone mode injects `LOAD httpfs` before worker
91+
attachments.
8392

8493
The `simple_writable/*.test` write-path tests (INSERT/UPDATE/DELETE/RETURNING)
8594
run on **every** lane against `VGI_SIMPLE_WRITABLE_WORKER`
@@ -147,36 +156,32 @@ even though the fixture (`vgi-fixture-bad-enum-worker`) is installed.
147156
```bash
148157
uv sync --all-extras
149158
VGI_SRC=~/Development/vgi \
150-
HAYBARN_UNITTEST=/path/to/haybarn-unittest \
159+
HAYBARN_UNITTEST=~/Development/vgi/build/release/test/unittest \
160+
RUNNER_LINKS_EXTENSIONS=1 \
151161
TRANSPORT=stdio \
152162
ci/run-integration.sh
153163
```
154164

155-
Download `haybarn-unittest` for your platform from the pinned Haybarn release:
165+
To check the published extension instead, download `haybarn-unittest` for your
166+
platform from a compatible Haybarn release and leave `RUNNER_LINKS_EXTENSIONS=0`:
156167

157168
```bash
158169
gh release download "$HAYBARN_RELEASE" --repo Query-farm-haybarn/haybarn \
159170
--pattern 'haybarn_unittest-*.zip'
160171
```
161172

162-
For day-to-day local work against a hand-built C++ extension, prefer the vgi
163-
repo's own targets (`make test_subprocess`, `make test_http`, …) /
164-
`scripts/run_all_tests.sh` as described in `CLAUDE.md`; this harness exists for
165-
CI, where building the extension from source is impractical.
173+
The vgi repo's own targets (`make test_subprocess`, `make test_http`, …) and
174+
`scripts/run_all_tests.sh` remain available as described in `CLAUDE.md`.
166175

167176
## Version pins (and their coupling)
168177

169-
Two pins live in the workflow's `env:` block:
178+
The workflow's `VGI_REF` pins the test suite and the native Actions artifact:
170179

171180
| Pin | What | Why |
172181
|-----|------|-----|
173-
| `VGI_REF` | the `Query-farm/vgi` ref supplying the `.test` files | **for now tracks `main`** (latest at checkout time) so the port is always validated against the newest suite; switch to a commit SHA for reproducible, deliberate bumps |
174-
| `HAYBARN_RELEASE` | the Haybarn release supplying `haybarn-unittest` | must be ABI-compatible with the community vgi extension |
175-
176-
**The coupling to know about:** the vgi extension is pulled live from the
177-
community channel (`FORCE INSTALL vgi FROM community`), which always serves the
178-
*currently published* build — it is not version-pinned here. So CI verifies the
179-
worker against what users can install today. If `VGI_REF` points at a commit
180-
whose tests exercise a protocol feature the published extension doesn't yet ship
181-
(or vice-versa), that test can fail or skip — bump `VGI_REF` deliberately and
182-
re-validate against the current community extension.
182+
| `VGI_REF` | the `Query-farm/vgi` commit supplying the tests and native runner | the downloader requires an artifact built from that exact commit |
183+
184+
The optional standalone mode still installs the currently published community
185+
extension. Use a compatible test suite and worker when checking that release;
186+
an unreleased minor wire revision requires the matching native development
187+
runner. Changing the CI pin does not publish an extension or an SDK package.

‎ci/run-integration.sh‎

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
33
#
44
# Run the canonical Query-farm/vgi integration sqllogictest suite against the
5-
# Python example worker, using a prebuilt standalone `haybarn-unittest` and the
6-
# signed community vgi extension — no C++ build from source. See ci/README.md.
5+
# Python example worker, using either a native runner with linked extensions or
6+
# standalone `haybarn-unittest` and the signed community extension. See ci/README.md.
77
#
88
# Ported from vgi-go's harness. The Python differences:
99
# * The "worker binaries" are console scripts installed into the project venv
@@ -29,6 +29,8 @@
2929
# Optional:
3030
# BIN_DIR dir holding the fixture console scripts (default: $REPO/.venv/bin)
3131
# TRANSPORT stdio | shm | launch | http (default: launch)
32+
# RUNNER_LINKS_EXTENSIONS 1 for a native runner linking vgi/httpfs/json/parquet;
33+
# 0 for the published standalone runner (default)
3234
# VGI_RPC_SHM_SIZE_BYTES shm side-channel segment size (the shm lane)
3335
# STAGE scratch dir for the preprocessed test tree (default: mktemp)
3436
set -euo pipefail
@@ -41,6 +43,7 @@ REPO="$(cd "$HERE/.." && pwd)"
4143
BIN_DIR="${BIN_DIR:-$REPO/.venv/bin}"
4244
STAGE="${STAGE:-$(mktemp -d)}"
4345
TRANSPORT="${TRANSPORT:-launch}"
46+
RUNNER_LINKS_EXTENSIONS="${RUNNER_LINKS_EXTENSIONS:-0}"
4447
INTEGRATION="$VGI_SRC/test/sql/integration"
4548
[ -d "$INTEGRATION" ] || { echo "::error::no test/sql/integration under VGI_SRC=$VGI_SRC"; exit 1; }
4649

@@ -118,7 +121,11 @@ mkdir -p "$STAGE/test/sql/integration"
118121
-not -name 'nested_type_combinations.test' \
119122
"${EXTRA_SKIP[@]}" | while read -r f; do
120123
mkdir -p "$STAGE/test/sql/integration/$(dirname "$f")"
121-
awk -v http="$AWK_HTTP" -f "$HERE/preprocess-require.awk" "$f" > "$STAGE/test/sql/integration/$f"
124+
if [ "$RUNNER_LINKS_EXTENSIONS" = "1" ]; then
125+
cp "$f" "$STAGE/test/sql/integration/$f"
126+
else
127+
awk -v http="$AWK_HTTP" -f "$HERE/preprocess-require.awk" "$f" > "$STAGE/test/sql/integration/$f"
128+
fi
122129
done )
123130

124131
# Some integration cases package an executable from the upstream support tree.
@@ -310,6 +317,9 @@ esac
310317

311318
cd "$STAGE"
312319

320+
if [ "$RUNNER_LINKS_EXTENSIONS" = "1" ]; then
321+
echo "Using extensions linked into the matching native runner."
322+
else
313323
echo "Warming the extension cache (vgi from community, deps from core) ..."
314324
mkdir -p "$STAGE/test"
315325
# FORCE INSTALL vgi re-downloads the currently-published community build,
@@ -335,6 +345,7 @@ INSTALL spatial FROM core;
335345
EOF
336346
"$HAYBARN_UNITTEST" "test/_warm.test" >/dev/null 2>&1 || echo "::warning::extension warm step did not fully succeed"
337347
rm -f "$STAGE/test/_warm.test"
348+
fi
338349

339350
# DuckDB's sqllogictest runner, given no --test-config, turns any error whose
340351
# text contains "HTTP" or "Unable to connect" into a SKIP and exits 0. Over the
@@ -343,6 +354,14 @@ rm -f "$STAGE/test/_warm.test"
343354
TEST_CONFIG="$VGI_SRC/test/configs/no_error_skip.json"
344355
[ -f "$TEST_CONFIG" ] || { echo "::error::missing $TEST_CONFIG (VGI_REF predates it?)"; exit 1; }
345356

357+
# Native unittest defaults to its compiled source directory even after `cd`.
358+
# Select the staged tree explicitly so discovery respects the exclusions above
359+
# and spawned workers resolve relative fixture paths from the same directory.
360+
RUNNER_ARGS=()
361+
if [ "$RUNNER_LINKS_EXTENSIONS" = "1" ]; then
362+
RUNNER_ARGS=(--test-dir "$STAGE")
363+
fi
364+
346365
# Run the lane, streaming the native sqllogictest report (a progress line per
347366
# file + the final "All tests passed (.. N assertions ..)" summary). Out-of-scope
348367
# tests were dropped at staging; any failed assertion exits non-zero.
@@ -373,7 +392,7 @@ run_unittest() {
373392
# `set +e` rather than `|| true`: the latter runs before PIPESTATUS is read and
374393
# overwrites it with true's 0, silently swallowing every real test failure.
375394
set +e
376-
"$HAYBARN_UNITTEST" --test-config "$TEST_CONFIG" "$@" 2>&1 | tee "$log"
395+
"$HAYBARN_UNITTEST" "${RUNNER_ARGS[@]}" --test-config "$TEST_CONFIG" "$@" 2>&1 | tee "$log"
377396
rc="${PIPESTATUS[0]}"
378397
set -e
379398
if grep -q 'due to a fatal error condition' "$log"; then
@@ -382,6 +401,10 @@ run_unittest() {
382401
"otherwise have passed. This is invisible to the exit code by construction."
383402
rc=1
384403
fi
404+
if grep -Eq 'No tests ran|No test cases matched' "$log"; then
405+
echo "::error::the runner did not execute the selected tests"
406+
rc=1
407+
fi
385408
rm -f "$log"
386409
return "$rc"
387410
}

0 commit comments

Comments
 (0)