Development¶
The project uses uv for dependencies, ruff for formatting and linting, pyrefly for types,
pytest for tests, and yarn/webpack for the frontend bundle.
Setup¶
make
make runs uv sync and installs the pre-commit hooks. The equivalent manual steps:
uv sync --locked --all-extras --dev
uv run pre-commit install
Python 3.11 or newer is required. CI runs 3.11 through 3.15.
Checks¶
Run the same order as CI:
uv run pyrefly check
uv run ruff check
uv run pytest tests
make lint runs pyrefly check and ruff check. make test runs the Python and
frontend test suites; make test-frontend runs only frontend/ tests.
Pytest defaults come from pyproject.toml (-xsv), so the suite stops on the first failure.
A focused run:
uv run pytest tests/test_peewee.py
uv run pytest tests/test_plugin.py::test_auth
uv run pytest tests -k "cookies"
The suite uses SQLite only; no Postgres or Mongo services are needed. Base and plugin tests run
under asyncio, trio and curio through the aiolib fixture; backend tests pin asyncio.
An autouse fixture writes a stub muffin_admin/main.js for the duration of the session and
restores any existing file afterwards, so tests do not need a built frontend and do not destroy
the local bundle.
Pre-commit¶
Hooks run on pre-commit, commit-msg and pre-push:
ruff formatandruff checkpyrefly checkuv lock --checkuv run pytest testson pre-pushmake -C frontend teston pre-push whenfrontend/changes- the usual YAML/TOML, whitespace and merge-conflict checks
If dependency metadata changes, run uv lock before committing. Commit messages must follow
the conventional types in .git-commits.yaml: build, chore, docs, feat, fix, merge,
ops, perf, refactor, style, test.
Docs¶
make docs # mkdocs build --strict into site/
make docs-serve # preview at http://127.0.0.1:8000
The docs dependency group is separate from dev, so database drivers and test tools are not
installed just to build the site. When adding a page, add it to nav in mkdocs.yml and link
it from docs/index.md.
.github/workflows/docs.yml builds the site on pull requests and publishes it to GitHub Pages
on pushes to develop. The repository must have Pages enabled with the source set to GitHub
Actions.
Examples¶
The examples run against SQLite:
make example-peewee # build the frontend, create example/db.sqlite, serve :8080
make example-sqlalchemy
They create a hundred users, messages and orders, with admin@admin.com:admin available for
login. The frontend dev server proxies to the backend on :5555:
make dev # backend on :5555 plus the webpack dev server
The example applications are example/peewee_orm/ and example/sqlalchemy_core/; both share
example/views.py and example/admin.css.
Frontend¶
make -C frontend # yarn install if needed, then webpack -> muffin_admin/main.js
make -C frontend lint # tsc --noEmit
make -C frontend test # vitest (data provider, file URLs, dashboard)
make -C frontend dev # webpack dev server
make -C frontend watch # rebuild on change
Webpack writes the bundle to muffin_admin/main.js (and the license notice to
muffin_admin/main.js.LICENSE.txt). Both are gitignored. yarn build in frontend/ only
compiles the TypeScript library into frontend/dist/.
Layout¶
muffin_admin/plugin.py: the Muffin plugin, routes,ra.jsonserializationmuffin_admin/handler.py:AdminOptions,AdminHandler, action decorator, marshmallow to react-admin mappingmuffin_admin/peewee/,muffin_admin/sqlalchemy/: backend handlers and optionsmuffin_admin/types.py:TypedDictand type aliases used by the handler APImuffin_admin/admin.html: the page template, read at import timefrontend/: react-admin application, built intomuffin_admin/main.js
Release¶
Releases are cut from develop into main:
make release # minor by default
make patch
make major
make release bumps the version with bump-my-version, updates uv.lock, creates a release
commit and tag, pushes main, and merges back into develop. bumpversion keeps three files
in sync:
pyproject.tomlfrontend/package.jsonfrontend/src/version.ts
.github/workflows/release.yml runs after tests pass on main. It builds and publishes the
frontend npm package, then runs make pypi in frontend/ so muffin_admin/main.js exists,
and only then runs uv build and uv publish. The Python package is built from the source
tree, and uv_build includes everything under muffin_admin/, so the bundle and its license
notice end up in both the wheel and the sdist.
A uv build outside the release workflow (a clean checkout without a frontend build) still
succeeds, but the resulting package has no main.js and the admin page will not work.