Contributing

Welcome to the tidydraws project! We’re excited that you’re interested in contributing. This document explains how to set up your development environment and contribute to this project.

One-Time Setup

Prerequisites

  • Python 3.11 or higher
  • uv package manager

Fork and clone

git clone https://github.com/YOUR_USERNAME/tidydraws.git
cd tidydraws

Install dependencies

uv sync --all-extras

This installs all runtime and dev dependencies (pytest, ruff, mypy, great-docs, etc.) and optional plotting libraries.

Starting a New Session

Each time you start a new development session, sync your environment to pick up any dependency changes:

uv sync --all-extras

Pre-commit hooks persist across sessions — no need to reinstall unless .pre-commit-config.yaml changes.

Upgrading a single dependency

To update a specific package (e.g. great-docs) to its latest allowed version:

uv sync --all-extras --upgrade-package great-docs

The --upgrade-package flag updates the lock file resolution for that package only (respecting the version constraint in pyproject.toml). --all-extras is required because several dev dependencies — including great-docs — are listed under [project.optional-dependencies] dev rather than in the [dependency-groups] section, so a bare uv sync would resolve the upgrade but skip installing them.

To upgrade every dependency to the latest compatible version:

uv sync --all-extras --upgrade

To upgrade the lock file without touching .venv (e.g. to inspect what changed):

uv lock --upgrade-package great-docs

After upgrading, run make install (which wraps uv sync --all-extras) to install any newly resolved packages.

Common Dev Commands

Run tests:

uv run pytest

Lint code:

uv run ruff check .

Type check:

uv run mypy .

Documentation

We use Great Docs (which wraps Quarto) for the documentation site. A single great-docs.yml at the repo root controls the build: it wires up the API reference, the narrative tutorials under docs/user_guide/, and the worked examples under docs/examples/.

Building Documentation Locally

  1. Build the site (output goes to the ephemeral great-docs/_site/ directory):

    uv run great-docs build      # or: make docs
  2. Preview locally with live reload at http://localhost:3000:

    uv run great-docs preview    # or: make docs-preview
  3. See what API symbols Great Docs can discover:

    uv run great-docs scan --verbose

The great-docs/ directory is ephemeral — it is regenerated on every build and is git-ignored. Never edit files inside it directly; change great-docs.yml or the source .qmd files under docs/ instead. To clear it, run make cleandocs.

Agent skills for the docs

This repo ships the Great Docs Agent Skills under .agents/skills/ (great-docs, configure-site, write-user-guide, revise-docstrings, author-skills), pinned via skills-lock.json. AI coding agents working on the docs pick these up automatically; you do not need to install anything. To refresh them against upstream, run npx skills add https://posit-dev.github.io/great-docs/ from the repo root and commit the result.

Making Changes

  1. Create a feature branch from main:

    git checkout -b feature/your-feature-name
  2. Make your changes following the project’s coding style and maintain compatibility.

  3. Add tests for your changes where appropriate.

  4. Run all checks before committing:

    uv run pytest
    uv run ruff check .
    uv run mypy .
  5. Commit your changes with a descriptive message.

  6. Push to your fork and create a pull request.

Pull Request Guidelines

  • Reference relevant issues in your PR description
  • Ensure all tests pass
  • Add or update documentation as needed
  • Keep changes focused and atomic

Releasing

Releases are cut by a repo admin running a single make target. The version lives in one place — tidydraws/__init__.py (__version__) — and is read dynamically by hatchling at build time, so pyproject.toml never carries a version literal.

To cut a release (admin only)

make release-patch   # 0.4.0 -> 0.4.1
make release-minor   # 0.4.0 -> 0.5.0
make release-major   # 0.4.0 -> 1.0.0

This runs bumpver, which:

  1. Bumps __version__ in tidydraws/__init__.py (the only version literal in the repo).
  2. Runs scripts/pre-bump.sh, which re-derives uv.lock and stages it so the lockfile lands in the same commit.
  3. Commits with message Bump version 0.4.0 -> 0.5.0.
  4. Creates and pushes tag 0.5.0.

The tag push then triggers the automated cascade:

flowchart LR
    A[make release-minor] --> B[bumpver: bump, lock, commit, tag, push]
    B --> C[release.yml: GitHub Release]
    C --> D[publish.yml: build sdist+wheel]
    D --> E[TestPyPI + verify install]
    E --> F[PyPI upload — gated by release environment review]

The final PyPI upload runs in the release environment, which requires admin approval in the Actions UI. Nothing reaches PyPI without that click.

Permissions and gating

  • Admin direct-push to main: enforce_admins is off, so admins can push the bump commit directly. Non-admin collaborators still need a PR.
  • Tag protection: only admins can push version tags (e.g. 0.5.0), so only admins can trigger a release.
  • PyPI environment: the release environment requires admin review before upload.

Non-admin collaborators and external contributors cannot cut releases at any stage.