Skip to content

ci: GitHub Actions — lint + unit tests per component #1

ci: GitHub Actions — lint + unit tests per component

ci: GitHub Actions — lint + unit tests per component #1

Workflow file for this run

# adscope CI — lint + unit tests per component, no paid services, no secrets.
#
# Design: the GATE is four fast jobs that need NO heavy runtime services
# (no Postgres, no Redpanda, no Ollama, no built warehouse). Each mirrors
# exactly how a developer runs that component locally, so a green check here
# means the component compiles, lints clean, and its self-contained tests pass.
#
# Deliberately scoped OUT of CI (run locally against `docker compose up`):
# * collectors integration tests — the 10 `#[ignore]` tests in
# crates/cli/tests/compose_it.rs need the live compose stack; `cargo test`
# skips them by default, which is what we want here.
# * full `dbt build` — needs Postgres + the collector-loaded raw
# layer; this workflow only does `dbt deps` + `dbt parse` (compiles the
# project graph, no database connection). See the `warehouse` job.
# * rag pgvector integration + eval CLI — the pgvector test is behind the
# `integration` marker (needs a live pgvector Postgres) and the eval CLI
# needs the built DuckDB marts; both are deselected/omitted here.
# Pulling any of those into CI would require standing up the whole stack per
# run — slow and flaky. They are covered by `make` targets against a local
# `docker compose up` instead.
#
# Free-tier friendly: cargo + uv caches, concurrency cancels superseded runs,
# read-only token, all actions pinned to a major version.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# One in-flight run per ref; a new push cancels the previous one (saves minutes).
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
# Least privilege — CI only needs to read the checked-out code.
permissions:
contents: read
jobs:
# ---------------------------------------------------------------------------
# collectors (Rust workspace) — fmt + clippy (warnings = errors) + unit tests.
# SQLX_OFFLINE makes the sqlx compile-time query macros read the committed
# `.sqlx/` cache instead of connecting to Postgres, so no database is needed.
# `cargo test --workspace` runs unit tests; the `#[ignore]` compose-stack
# integration tests stay skipped.
# ---------------------------------------------------------------------------
collectors:
name: collectors (rust)
runs-on: ubuntu-latest
defaults:
run:
working-directory: collectors
env:
SQLX_OFFLINE: "true"
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
# Honors collectors/rust-toolchain.toml (channel = stable); adds the two
# components the lint steps need.
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo build
uses: Swatinem/rust-cache@v2
with:
workspaces: collectors
- name: Format check
run: cargo fmt --check
- name: Clippy (deny warnings)
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Unit tests
run: cargo test --workspace
# ---------------------------------------------------------------------------
# simulator (FastAPI, Python/uv) — ruff lint + format check + pytest.
# 93 tests, all in-process (FastAPI TestClient); no external services.
# Dev tools (pytest, ruff) live in [dependency-groups], synced by default.
# ---------------------------------------------------------------------------
simulator:
name: simulator (python)
runs-on: ubuntu-latest
defaults:
run:
working-directory: services/simulator
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v8
with:
enable-cache: true
cache-dependency-glob: services/simulator/uv.lock
- name: Sync (locked)
run: uv sync --locked
- name: Ruff lint
run: uv run ruff check
- name: Ruff format check
run: uv run ruff format --check
- name: Pytest
run: uv run pytest
# ---------------------------------------------------------------------------
# rag (NL -> guarded DuckDB SQL, Python/uv) — ruff lint + pytest.
# The default suite is deterministic and dependency-free: hashing embedder,
# in-memory vector store, FakeLLM — no model, no network, no Postgres.
# Dev tools live in [project.optional-dependencies].dev, hence `--extra dev`.
# `-m "not integration and not live_llm"` drops the pgvector + live-model
# tests (they self-skip anyway, but excluding them keeps intent explicit).
# ---------------------------------------------------------------------------
rag:
name: rag (python)
runs-on: ubuntu-latest
defaults:
run:
working-directory: rag
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v8
with:
enable-cache: true
cache-dependency-glob: rag/uv.lock
- name: Sync (locked, dev extra)
run: uv sync --locked --extra dev
- name: Ruff lint
run: uv run ruff check
- name: Pytest (deterministic core)
run: uv run pytest -m "not integration and not live_llm"
# ---------------------------------------------------------------------------
# warehouse (dbt-duckdb) — lightweight graph check, NOT a full build.
# `dbt parse` compiles the whole project (models, sources, refs, macros,
# Jinja, dbt_utils tests) into a manifest WITHOUT opening a database
# connection, so it catches broken SQL/refs with zero services. A real
# `dbt build` needs Postgres + the collector-loaded raw layer and is run
# locally via `make` against `docker compose up`. `dbt deps` fetches
# dbt_utils from the (free, no-auth) dbt Hub; DBT_PROFILES_DIR points at the
# in-repo profiles.yml.
# ---------------------------------------------------------------------------
warehouse:
name: warehouse (dbt)
runs-on: ubuntu-latest
defaults:
run:
working-directory: warehouse
env:
DBT_PROFILES_DIR: ${{ github.workspace }}/warehouse
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v8
with:
enable-cache: true
cache-dependency-glob: warehouse/uv.lock
- name: Sync (locked)
run: uv sync --locked
- name: dbt deps
run: uv run dbt deps
- name: dbt parse (compile graph, no database)
run: uv run dbt parse