Skip to content

Commit 9b31dab

Browse files
Merge pull request #1 from lancedb/claude/project-improvement-areas-rdxfow
Harden CI/security, fill doc & test gaps, add governance files
2 parents dec174d + fcb8950 commit 9b31dab

23 files changed

Lines changed: 855 additions & 62 deletions

.github/dependabot.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
version: 2
2+
updates:
3+
# Keep the SHA-pinned GitHub Actions current (Dependabot bumps both the SHA and
4+
# the trailing version comment).
5+
- package-ecosystem: "github-actions"
6+
directory: "/"
7+
schedule:
8+
interval: "weekly"
9+
groups:
10+
github-actions:
11+
patterns: ["*"]
12+
commit-message:
13+
prefix: "ci"
14+
15+
# Python dependencies via uv (reads pyproject.toml + uv.lock). The cluster-matched
16+
# betas are intentionally pinned to the deployed Geneva/LanceDB build, and
17+
# pyarrow/numpy are left to geneva's own constraints — Dependabot must not touch
18+
# those, only the loose deps (typer, gradio, transformers, etc.).
19+
- package-ecosystem: "uv"
20+
directory: "/"
21+
schedule:
22+
interval: "weekly"
23+
groups:
24+
python:
25+
patterns: ["*"]
26+
commit-message:
27+
prefix: "deps"
28+
ignore:
29+
- dependency-name: "geneva"
30+
- dependency-name: "lancedb"
31+
- dependency-name: "pylance"
32+
- dependency-name: "pyarrow"
33+
- dependency-name: "numpy"

.github/workflows/ci.yml

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@ jobs:
1616
test:
1717
runs-on: ubuntu-latest
1818
steps:
19-
- uses: actions/checkout@v7
19+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
2020

2121
- name: Install uv
22-
uses: astral-sh/setup-uv@v8.2.0
22+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
2323
with:
2424
enable-cache: true
2525

2626
# Installs the full runtime stack + the dev group. geneva/lancedb/pylance
2727
# come from the public Gemfury indexes declared in pyproject.toml; if those
2828
# ever require auth, add UV_INDEX_LANCEDB_PASSWORD / UV_INDEX_LANCE_FORMAT_PASSWORD
2929
# repo secrets and pass them here as env.
30-
- name: Sync dependencies
31-
run: uv sync --group dev
30+
#
31+
# --locked asserts uv.lock is in sync with pyproject.toml and fails the job
32+
# on drift (e.g. a dependency edit without `uv lock`) instead of silently
33+
# updating it — guarding the deliberate cluster pins.
34+
- name: Sync dependencies (lockfile must be up to date)
35+
run: uv sync --locked --group dev
3236

3337
- name: Ruff lint
3438
run: uv run ruff check --output-format=github
@@ -47,8 +51,45 @@ jobs:
4751

4852
- name: Upload coverage report
4953
if: always()
50-
uses: actions/upload-artifact@v7
54+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
5155
with:
5256
name: coverage-xml
5357
path: coverage.xml
5458
if-no-files-found: ignore
59+
60+
# Security hardening: a dependency CVE scan and a secret scan, run in parallel
61+
# with `test`. The dependency audit is advisory (continue-on-error) — like the
62+
# `ty` step — because the geneva/lancedb/pylance betas come from Gemfury and may
63+
# not be known to the advisory DB. The secret scan IS allowed to fail the job.
64+
audit:
65+
runs-on: ubuntu-latest
66+
steps:
67+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
68+
with:
69+
fetch-depth: 0 # the secret scan walks git history
70+
71+
- name: Install uv
72+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
73+
with:
74+
enable-cache: true
75+
76+
# Export the locked dependency set (without this project) and audit the
77+
# pinned versions against the PyPI advisory DB.
78+
- name: Dependency audit (pip-audit, non-blocking)
79+
run: |
80+
uv export --frozen --no-emit-project --no-hashes \
81+
--format requirements-txt -o requirements.txt
82+
uvx pip-audit -r requirements.txt
83+
continue-on-error: true
84+
85+
# Secret scan with TruffleHog (Apache-2.0, fully open source). Run the
86+
# binary directly — no Action wrapper, no license — and walk git history,
87+
# failing the job only on actively verified secrets to avoid noise.
88+
- name: Secret scan (trufflehog)
89+
env:
90+
TRUFFLEHOG_VERSION: 3.95.6
91+
run: |
92+
curl -sSLf \
93+
"https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/trufflehog_${TRUFFLEHOG_VERSION}_linux_amd64.tar.gz" \
94+
| tar -xz -C /tmp trufflehog
95+
/tmp/trufflehog git file://. --only-verified --fail --no-update

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ ENV/
4747
env.bak/
4848
venv.bak/
4949

50+
# requirements.txt exported by the CI dependency-audit job (uv.lock is the source of truth).
51+
requirements.txt
52+
5053
# --- Test / coverage / type-check caches ---
5154
.pytest_cache/
5255
.tox/

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ repos:
1313
- id: check-yaml
1414
- id: check-toml
1515
- id: check-merge-conflict
16+
- id: detect-private-key # block accidental private-key commits
1617
- id: debug-statements
1718
- id: mixed-line-ending
1819
- id: check-added-large-files

CONTRIBUTING.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Contributing to geneva-examples
2+
3+
Thanks for improving the Geneva UDF examples. This guide covers the local setup,
4+
the project's conventions, and the workflow for adding a new UDF or stage.
5+
6+
## Local setup
7+
8+
Requires Python ≥ 3.12 and [`uv`](https://docs.astral.sh/uv/).
9+
10+
```bash
11+
make install # uv sync --group dev + install the git pre-commit hook
12+
make check # the full CI gate: ruff lint + format-check + pytest (90% coverage)
13+
```
14+
15+
`geneva`, `lancedb`, and `pylance` are pinned betas served from Gemfury indexes
16+
(declared in [`pyproject.toml`](pyproject.toml)). If your environment can't reach
17+
those indexes, `uv sync` will fail on those packages — request access or run in an
18+
environment that has it.
19+
20+
Useful targets (see `make help`): `make lint-fix`, `make format`, `make test`,
21+
`make typecheck`, `make precommit`.
22+
23+
## Conventions
24+
25+
- **Formatting & linting:** `ruff` (config in `pyproject.toml`) is the single
26+
source of truth and gates every commit via pre-commit. Run `make format` before
27+
pushing. Line length is 88; existing `# noqa` suppressions are kept honest by
28+
`RUF100`, so don't add ones the selected rules won't use.
29+
- **Type checking:** `ty` runs in pre-commit and CI but is **non-blocking** by
30+
design — it's a preview tool with many false positives on the untyped ML deps.
31+
Prefer precise annotations; for the opaque Geneva/LanceDB runtime objects, use
32+
the structural `Protocol`s in
33+
[`geneva_examples/core/_types.py`](geneva_examples/core/_types.py) under a
34+
`TYPE_CHECKING` guard instead of importing the beta runtime types. Promoting
35+
`ty` to a blocking gate is deliberately deferred until its false-positive rate
36+
drops.
37+
- **Imports in UDF bodies:** a UDF/chunker body is a self-contained closure that
38+
ships to the remote workers. Nest its imports and helpers *inside* the factory
39+
function so they serialize with it; keep the driver/CLI code lightweight.
40+
41+
## Adding a new UDF
42+
43+
1. **Prototype in UDF Studio.** Run `uv run udf-studio`, pick a template, point it
44+
at sample data in `studio_data/`, and iterate on your `transform(value)` (UDF)
45+
or `chunk(value)` (chunker) locally — no cluster, GPU, or Ray. See the
46+
[README](README.md#udf-studio).
47+
2. **Add a factory + manifest.** Create a module in
48+
[`geneva_examples/udfs/`](geneva_examples/udfs/) following
49+
[`imageinfo.py`](geneva_examples/udfs/imageinfo.py): export a `build_*_udf(...)`
50+
factory and a `*_RUNTIME_PIP` list pinning the worker-side packages
51+
(env-overridable, like `GENEVA_PACKAGE_SPEC`).
52+
3. **Wire a stage CLI.** Add a Typer CLI under
53+
[`geneva_examples/pipeline/stages/`](geneva_examples/pipeline/stages/) modeled on
54+
[`lightweight.py`](geneva_examples/pipeline/stages/lightweight.py): load config,
55+
`connect`, build a `GenevaManifest`, build the UDF(s), and call the shared
56+
[`backfill_column()`](geneva_examples/pipeline/stages/_runner.py) runner. Add a
57+
`project.scripts` entry in `pyproject.toml`.
58+
59+
## Testing & the coverage policy
60+
61+
The suite enforces a **90% coverage gate**, but the pieces that need a live
62+
cluster, GPU, or model weights are listed in `[tool.coverage.run] omit` in
63+
`pyproject.toml` (the model UDFs, the pipeline/ops CLIs, the Gradio wiring). Their
64+
*pure* helpers are still unit-tested — they just don't inflate the percentage.
65+
66+
When you add code:
67+
68+
- Unit-test pure helpers directly (see `tests/test_udfs.py`,
69+
`tests/test_pipeline_runner.py`, `tests/test_ops_*.py`).
70+
- For CLI *wiring* that would otherwise hit a cluster, add a mocked smoke test in
71+
the style of [`tests/test_pipeline_smoke.py`](tests/test_pipeline_smoke.py): use
72+
`typer.testing.CliRunner` and monkeypatch `load_config`/`connect` plus an
73+
injected fake `geneva` module.
74+
- Reuse the synthetic-media fixtures in `tests/conftest.py`
75+
(`make_png`, `make_mp4`, `data_dir`).
76+
77+
Run `make check` before opening a PR.

0 commit comments

Comments
 (0)