Skip to content

Development

How to work on odp-releaser itself — set up an environment, run the checks, and build the docs. If you're setting up a repo that uses odp-releaser, you want Getting started instead.

Tooling

Three tools do most of the work here:

  • uv manages the environment and the lockfile. It can be installed a variety of ways.
  • nox is the task runner — every check CI runs has a session. Use ./noxfile.py to run it without installing, or uv tool install nox.
  • prek runs the git hooks (a Rust drop-in for pre-commit). uv tool install prek, or brew install prek on macOS.

There is also a dev container if you'd rather not set any of that up locally.

Quick development

The fastest way to start with development is to use nox. Running nox with no arguments lints and tests using every installed version of Python on your system, skipping ones that are not installed. Nox handles everything for you, including setting up a temporary virtual environment for each run.

You can also run specific sessions:

$ nox -s lint          # run pre-commit hooks against all files
$ nox -s tests         # the pytest suite
$ nox -s test_locale   # the pytest suite under LC_ALL=C, which catches Windows-ish bugs
$ nox -s pylint        # pylint
$ nox -s docs          # build and serve the docs
$ nox -s docs_clean    # remove the built site/
$ nox -s schemas       # regenerate the committed JSON Schemas
$ nox -s build         # make an SDist and wheel

Setting up a development environment manually

You can set up a development environment by running:

uv sync

That installs the package plus the test and docs dependency groups.

Pre-commit (via Prek)

You should prepare prek, which will help you by checking that commits pass required checks:

uv tool install prek # or brew install prek on macOS
prek install # Will install a pre-commit hook into the git repo

You can also/alternatively run prek run (changes only) or prek run --all-files to check even without installing the hook.

Alongside the usual formatters and linters, the hooks run this project's own validate-image-manifest and validate-deploy-targets checks against the test fixtures, zizmor against the workflows, and regenerate-json-schemas.

Testing

Use pytest to run the unit checks:

uv run pytest

Coverage

Use pytest-cov to generate coverage reports:

uv run pytest --cov=odp-releaser

JSON Schemas

The files in schemas/ are generated from the pydantic config models, so they go stale whenever a model changes. Regenerate them with:

nox -s schemas

nox -s schemas -- --check verifies they are current without writing anything. The test suite asserts the same thing, so a stale schema fails CI, and the regenerate-json-schemas pre-commit hook keeps them in step automatically.

Building docs

You can build and serve the docs using:

nox -s docs

You can build the docs only with:

nox -s docs --non-interactive

The docs are built with Zensical, configured in zensical.toml. Pages pull content from the code in three ways, so a build failure is often a code problem rather than a prose one:

  • mkdocstrings with the python handler renders the pydantic config models;
  • mkdocstrings-github renders the reusable workflows' and composite actions' inputs, outputs, and secrets straight from their YAML — so those are edited in .github/, not in markdown;
  • markdown-exec runs the fenced python/bash blocks marked exec="on" at build time, which is how the example configs and rendered comment templates stay accurate.

The Documentation workflow builds the site on every pull request, and deploys to GitHub Pages only from main.

Exercising a config

Both config files can be dry-run against canned payloads without touching GitHub — see Testing, which also covers how this repo's own CI exercises both reusable workflows end-to-end on every pull request.