Skip to content

Development

Common tasks

A justfile wraps the common operations:

just              # list all recipes
just sync         # uv sync
just lint         # ruff check (generated/ is excluded)
just typecheck    # ty check (generated/ is excluded)
just test         # pytest
just check        # lint + typecheck + test — what CI runs
just build        # build the sdist/wheel and verify generated/ made it into the wheel
just fetch-specs  # download OpenAPI specs into specs/
just generate     # regenerate Kiota clients from specs/ (needs Docker)

just fetch-specs and just generate both accept an optional list of service names to limit the operation, e.g. just generate annosaurus oni.

Regenerating clients

vars_client.generated is produced by Kiota and must never be hand-edited. scripts/services.conf is the single source of truth mapping each service to its spec-URL environment variable and Kiota class name — add a row there to onboard a new service.

export ANNOSAURUS_SPEC_URL=https://annosaurus.example.org/openapi.yaml
# ... one *_SPEC_URL per service you want to (re)fetch

just fetch-specs        # downloads specs/*.yaml
just generate           # runs Kiota via Docker, writes src/vars_client/generated/<service>/

vars_client.generated.__init__ hand-maintains convenience re-exports (from vars_client.generated import AnnosaurusClient, etc.) — Kiota only writes into the per-service subdirectories, so this file is safe from being clobbered by regeneration.

Testing

Tests live under tests/, using pytest + pytest-asyncio + respx to mock HTTP at the transport layer — no real VARS deployment needed. See tests/auth/ and tests/test_client.py for the patterns used to test the JWT exchange/refresh flows and end-to-end client wiring.

CI

.github/workflows/ci.yml runs just lint, just typecheck, just test, and a just build + wheel-content check on every push/PR to main. It can't reach VARS' internal OpenAPI spec URLs, so spec regeneration isn't part of CI — run just fetch-specs && just generate locally when upstream specs change, and commit the diff.

Docs

This site is itself built with Zensical:

just docs-generate  # regenerate docs/reference/ from specs/ + generated/
just docs-serve     # live preview at localhost:8000 (runs docs-generate first)
just docs-build     # build to site/ (runs docs-generate first)

The API Reference pages (docs/reference/) aren't hand-written or committed — scripts/gen_api_reference.py generates them from specs/*.yaml cross-referenced against the real generated Kiota code, so they can't drift from either. See Exploring the API for what that output looks like and why it's built this way.

.github/workflows/docs.yml builds and deploys it to GitHub Pages via the actions/deploy-pages action on every push to main.

Releasing

Versioning is automated with python-semantic-release, driven entirely by Conventional Commits on main (stable) and develop (prerelease, -rc.N suffix) — nothing to do by hand beyond writing properly-typed commit messages. feat: bumps minor, fix:/perf: bump patch, a BREAKING CHANGE: footer bumps major (even pre-1.0, since major_on_zero = true).

just release-dry-run  # print what the next version would be, without changing anything

The release job in .github/workflows/ci.yml runs after test/build pass on a push to main/develop: it bumps project.version and vars_client.__version__, updates CHANGELOG.md, tags and pushes the commit, cuts a GitHub release, and — for a non-prerelease version — builds and publishes to PyPI. Requires a PYPI_TOKEN repo secret; the tagging/release steps use the default GITHUB_TOKEN.