Skip to content

fix(sql): validate table qualifiers in single-table scope (SQLR-14) #287

fix(sql): validate table qualifiers in single-table scope (SQLR-14)

fix(sql): validate table qualifiers in single-table scope (SQLR-14) #287

Workflow file for this run

# Continuous integration — runs on every pull request + every push to
# main. Blocks PR merge when anything fails (once branch protection is
# configured on the GitHub side — see docs/release-plan.md Phase 6c).
#
# Jobs:
# - rust-build-and-test {ubuntu, macos, windows} cargo build + test
# - rust-lint ubuntu fmt + clippy + doc
# - python-sdk {ubuntu, macos, windows} maturin develop + pytest
# - nodejs-sdk {ubuntu, macos, windows} napi build + node --test
# - go-sdk {ubuntu, macos} cgo against libsqlrite_c + go test
# - wasm-build ubuntu wasm-pack build + size report
# - desktop-build ubuntu npm ci + cargo build -p sqlrite-desktop
# - journal-build ubuntu npm ci + cargo build -p sqlrite-journal (SQLR-41 example)
#
# All jobs use caching so a warm PR run takes 2–3 minutes on ubuntu.
# Go CI skips Windows for Phase 6b — Go's cgo on Windows needs a mingw
# setup that we haven't wired into the Go sidecar; deferred follow-up.
name: CI
on:
pull_request:
push:
branches: [main]
# Cancel previous CI runs on the same branch when a new push arrives.
# Saves compute + shortens PR feedback loops.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
# Suppress the "incremental compilation is not recommended for CI"
# advice line-noise across every Rust job. We don't want incremental
# on CI anyway (every run is from scratch).
CARGO_INCREMENTAL: '0'
# Colored cargo output survives into the GitHub Actions log view.
CARGO_TERM_COLOR: always
# Note: we intentionally do NOT set `RUSTFLAGS: '-D warnings'` at
# the workflow level. The codebase has ~24 pre-existing clippy
# warnings (mostly cosmetic — overindented docstrings, `Vec::new()
# + push` patterns, etc.) that will get cleaned up incrementally.
# Once the count is zero, we'll tighten by adding `-D warnings`
# here. Hard clippy errors (deny-by-default lints like
# `approx_constant`) still fail CI without this flag — that's the
# backstop for real correctness issues.
jobs:
# ---------------------------------------------------------------------------
# Rust: build + test the whole workspace (minus the desktop crate —
# it needs a frontend build, handled in its own job).
rust-build-and-test:
name: rust (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
defaults:
run:
# Bash on every OS — Windows uses Git Bash (preinstalled on
# `windows-latest`). Without this, Windows defaults to pwsh,
# which doesn't understand bash's backslash line-continuation
# in multi-line `run:` blocks.
shell: bash
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
# Bucket by OS + job — separate caches so a Linux miss
# doesn't steal macOS's target dir and vice versa.
shared-key: rust-build-and-test
# Exclusions explained:
#
# - `sqlrite-desktop` needs the Svelte frontend built first
# (handled in the `desktop-build` job).
#
# - `sqlrite-python` + `sqlrite-nodejs` are PyO3/napi-rs
# extension-module cdylibs. Their `extension-module` feature
# tells the Rust toolchain not to link libpython / libnode at
# compile time, so `--all-targets` fails on macOS when it
# tries to build the auto-generated test binary for their
# rlib targets (unresolved Python/Node symbols). The per-SDK
# `python-sdk` + `nodejs-sdk` jobs below exercise these
# crates through their native tooling (maturin / napi-rs).
#
# - `sqlrite-benchmarks` is the SQLR-4 / SQLR-16 bench harness.
# Excluded from CI for two reasons: criterion benches are
# noisy on shared GitHub runners (the published numbers come
# from a pinned local host per Q1), and the bundled rusqlite
# build pulls in a heavy C toolchain that we don't want to
# pay on every PR. Run locally with `make bench`.
- name: cargo build
run: |
cargo build --workspace \
--exclude sqlrite-desktop \
--exclude sqlrite-journal \
--exclude sqlrite-python \
--exclude sqlrite-nodejs \
--exclude sqlrite-benchmarks \
--all-targets
- name: cargo test
run: |
cargo test --workspace \
--exclude sqlrite-desktop \
--exclude sqlrite-journal \
--exclude sqlrite-python \
--exclude sqlrite-nodejs \
--exclude sqlrite-benchmarks
# ---------------------------------------------------------------------------
# Rust: lint — fmt + clippy + doc. One cell (ubuntu) because these
# are platform-independent checks. Runs in parallel with the matrix
# build/test job above.
rust-lint:
name: rust lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust + components
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
shared-key: rust-lint
- name: cargo fmt
run: cargo fmt --all -- --check
- name: cargo clippy
# No `-D warnings` for now — see the top-level env comment.
# Deny-by-default lints (the ones that surface real bugs,
# e.g. `approx_constant`) still fail without the flag.
# Exclude the extension-module SDK cdylibs for the same
# reason as `rust-build-and-test` — their test binaries
# can't link standalone.
run: |
cargo clippy --workspace \
--exclude sqlrite-desktop \
--exclude sqlrite-journal \
--exclude sqlrite-python \
--exclude sqlrite-nodejs \
--exclude sqlrite-benchmarks \
--all-targets
- name: cargo doc
# `--no-deps` skips deps docs (they build on docs.rs, not here).
# Not warnings-as-errors yet — same rationale as clippy above.
run: |
cargo doc --workspace \
--exclude sqlrite-desktop \
--exclude sqlrite-journal \
--exclude sqlrite-python \
--exclude sqlrite-nodejs \
--exclude sqlrite-benchmarks \
--no-deps
# ---------------------------------------------------------------------------
# Python SDK: build the PyO3 extension, install into a venv, run pytest.
python-sdk:
name: python-sdk (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
defaults:
run:
# Bash on every runner (Windows' git bash is available via
# `shell: bash`) so the same commands work everywhere — no
# PowerShell / cmd fork.
shell: bash
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: python-sdk-${{ matrix.os }}
# maturin develop needs a virtualenv or conda env to install
# into. We create one per job + export VIRTUAL_ENV so every
# subsequent step sees it. Cross-platform: Windows venvs have
# `Scripts/` instead of `bin/`, so we pick the right path.
- name: Create venv
run: |
python -m venv .venv
if [ -d .venv/bin ]; then
echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
else
echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
echo "$PWD/.venv/Scripts" >> "$GITHUB_PATH"
fi
- name: Install maturin + pytest
run: pip install maturin pytest
- name: maturin develop
working-directory: sdk/python
run: maturin develop
- name: pytest
working-directory: sdk/python
run: python -m pytest tests/
# ---------------------------------------------------------------------------
# Node.js SDK: napi-rs build produces a .node binary. `npm test`
# exercises the node:test suite against it.
nodejs-sdk:
name: nodejs-sdk (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: sdk/nodejs/package-lock.json
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: nodejs-sdk-${{ matrix.os }}
- name: npm ci
working-directory: sdk/nodejs
run: npm ci
- name: npm run build
working-directory: sdk/nodejs
run: npm run build
- name: npm test
working-directory: sdk/nodejs
run: npm test
# ---------------------------------------------------------------------------
# Go SDK: cgo-linked against libsqlrite_c. We build the FFI crate in
# release mode first so the .so / .dylib is available for go test.
#
# Skips Windows for Phase 6b — Go's cgo on Windows needs a mingw
# toolchain that isn't preinstalled on `windows-latest` runners; a
# follow-up task in the roadmap adds it.
go-sdk:
name: go-sdk (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache-dependency-path: sdk/go/go.mod
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: go-sdk-${{ matrix.os }}
- name: Build libsqlrite_c
# The Go driver's `#cgo LDFLAGS` references target/release.
run: cargo build --release -p sqlrite-ffi
- name: go test ./...
working-directory: sdk/go
run: go test -v ./...
# ---------------------------------------------------------------------------
# Go edge/IoT collector example (SQLR-43). Builds + tests the example
# app against the in-repo libsqlrite_c, so an engine/SDK API change
# that breaks the example is caught here. Same {ubuntu, macos} matrix
# and cgo wiring as the go-sdk job.
go-collector:
name: go-collector (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache-dependency-path: examples/go-collector/go.mod
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
shared-key: go-collector-${{ matrix.os }}
- name: Build libsqlrite_c
# The Go driver's `#cgo LDFLAGS` references target/release.
run: cargo build --release -p sqlrite-ffi
- name: go vet ./...
working-directory: examples/go-collector
run: go vet ./...
- name: go test ./...
working-directory: examples/go-collector
run: go test -v ./...
# ---------------------------------------------------------------------------
# WASM: only the build + size check (no tests — the WASM module's
# behavior is covered by the other SDKs' test suites; WASM just
# re-targets the same engine). One cell because WASM is a
# cross-compilation target, not a host OS.
wasm-build:
name: wasm-build
runs-on: ubuntu-latest
# Pinned binaryen version — see docs/release-plan.md
# ("Pinned binaryen / wasm-opt") for the bump procedure. Older
# binaryen rejects rustc's multi-table WASM output with
# "Only 1 table definition allowed in MVP", which the runner
# image's cache state used to surface non-deterministically
# (SQLR-58). Pinning keeps wasm-opt out of "whatever's cached"
# territory.
env:
BINARYEN_VERSION: version_122
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
with:
# sdk/wasm is its own workspace (see Cargo.toml comment);
# key it separately so it doesn't blow the main cache.
workspaces: sdk/wasm
shared-key: wasm-build
- name: Install pinned binaryen (wasm-opt)
# MUST run before wasm-pack: wasm-pack picks up wasm-opt from
# PATH if present, otherwise downloads whatever binaryen its
# own internal cache happens to have. Pinning + prepending
# to PATH forces a deterministic version across runner images.
run: |
set -euo pipefail
curl -fsSL "https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
-o "$RUNNER_TEMP/binaryen.tar.gz"
tar -xzf "$RUNNER_TEMP/binaryen.tar.gz" -C "$RUNNER_TEMP"
echo "$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
"$RUNNER_TEMP/binaryen-${BINARYEN_VERSION}/bin/wasm-opt" --version
- name: Install wasm-pack
uses: jetli/wasm-pack-action@v0.4.0
- name: wasm-pack build --target web --release
working-directory: sdk/wasm
run: |
# Sanity-check that the pinned wasm-opt is what wasm-pack sees.
which wasm-opt
wasm-opt --version
wasm-pack build --target web --release
- name: Report .wasm size
# Surfaces size regressions in PR logs. Not a hard limit yet;
# if/when we set a budget, convert to a failing check.
working-directory: sdk/wasm
run: |
size=$(stat -c '%s' pkg/sqlrite_wasm_bg.wasm 2>/dev/null || stat -f '%z' pkg/sqlrite_wasm_bg.wasm)
echo "::notice title=WASM bundle size::sqlrite_wasm_bg.wasm = $size bytes ($(( size / 1024 )) KiB)"
# ---------------------------------------------------------------------------
# Desktop: Tauri's Rust side + the Svelte frontend. Needs Node to
# build the frontend first because Tauri's build.rs looks for the
# compiled assets in `desktop/dist/`.
#
# One cell (ubuntu) for CI speed — release builds are tested in the
# Phase 6e desktop-release workflow's build matrix on all three OSes.
# Here we just need to prove the Rust side compiles.
desktop-build:
name: desktop-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: desktop/package-lock.json
- uses: dtolnay/rust-toolchain@stable
# Tauri needs Linux webkit + friends before cargo build runs.
- name: Install Tauri Linux deps
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf
- uses: Swatinem/rust-cache@v2
with:
shared-key: desktop-build
- name: npm ci
working-directory: desktop
run: npm ci
- name: Build frontend
working-directory: desktop
run: npm run build
- name: Build Rust side
run: cargo build -p sqlrite-desktop
# ---------------------------------------------------------------------------
# Journal example (SQLR-41): Tauri 2 + Svelte 5 daily-notes app under
# `examples/desktop-journal/`. Same shape as `desktop-build` — frontend
# first, then `cargo build -p sqlrite-journal`. Excluded from the
# default workspace commands for the same reasons (Svelte build is a
# prerequisite that the main rust-build-and-test job can't satisfy).
journal-build:
name: journal-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: examples/desktop-journal/package-lock.json
- uses: dtolnay/rust-toolchain@stable
- name: Install Tauri Linux deps
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf
- uses: Swatinem/rust-cache@v2
with:
shared-key: journal-build
- name: npm install
working-directory: examples/desktop-journal
# `npm ci` would be ideal, but the example doesn't ship a
# lockfile in version control (it's listed in .gitignore so
# downstream forkers don't have to fight a node-version-pinned
# lockfile). `npm install` is acceptable for an example app;
# the engine API surface is what we're really exercising.
run: npm install
- name: Build frontend
working-directory: examples/desktop-journal
run: npm run build
- name: Build Rust side (with ask)
run: cargo build -p sqlrite-journal --all-targets
- name: Build Rust side (no ask)
run: cargo build -p sqlrite-journal --no-default-features --all-targets
- name: Run journal unit tests
run: cargo test -p sqlrite-journal