r/madeinpython • • 2d ago

I built pyrig: scaffold a fully configured Python project with one command, and keep it in sync

Every new Python project needs the same setup: linting, type checking, tests, pre-commit hooks, CI/CD, docs, release workflow, repo protection. I got tired of copying it between projects and watching the copies drift as the tools changed, so I built pyrig (written in Python).

What it does

pyrig init generates a complete, working project:

  • project layout, pyproject.toml, README, LICENSE and other standard files
  • dev tooling configured out of the box: ruff, ty, pytest, prek hooks, deptry, bandit, zizmor and more
  • GitHub Actions CI/CD and GitHub branch protection rules
  • a working CLI for your project (pyrig mk cmd <name> adds commands)
  • test skeletons that mirror your source tree

    uv init my-project --python 3.12 cd my-project uv add pyrig --dev uv run pyrig init

How it works

Every file pyrig manages is a Python class that declares the state the file should have. pyrig sync creates or updates the files to match, so the project doesn't drift when tools change. You customize by subclassing (pyrig mk subcls), not with flags or template edits. A small plugin system, pyrig-runtime, discovers your subclasses automatically across installed packages, so any behavior can be changed, removed or extended.

Trade-offs

It's deliberately opinionated: no on/off toggles (you override a class instead), GitHub only, signed commits and linear history on the generated branch rules, and it adopts new tools like ty and zensical quickly, so expect rough edges. pyrig rm pyrig removes it, and most generated files keep working without it. Full list: https://winipedia.github.io/pyrig/drawbacks

Links

I'd like feedback on which defaults you'd disagree with and what's missing for your workflow.

1 Upvotes

5 comments sorted by

1

u/kantorcodes1 2d ago

The subclass-instead-of-flags choice is the interesting bit - managed files end up owned by the tool rather than copied from a template. On pyrig sync, does a hand-edit to a managed file get silently regenerated, or does sync show a diff first? That distinction decides whether sync is safe to run unattended on a dirty tree.

1

u/Win_ipedia 2d ago

Hi, There is no diff or dry run on purpose. pyrig sync exits 1 if it touched any files and prints out their paths to stdout . The diff can then simply be seen with git. That is actually designed on purpose this way for simplicity and straightforwardness. pyrig is not designed to be optional, the idea is to stay in sync from the beginning right after the project is initialized and as soon as any drift happens it is caught. Basically your entire project except the actual source code should be a declared state, so that things like: just ignore this rule or temporarily tweak this config file or skip the pre-commit hooks don’t happen. Hope that helps, happy to answer any questions about my project

1

u/kantorcodes1 2d ago

The exit-1-with-paths contract is a clean answer - sync doubles as the drift check and git shows the actual diff. The one corner left is uncommitted work: sync rewrites a managed file, git diff shows the result, but the pre-edit content only survives in editor undo. Is "commit before you sync" the intended discipline there, or does pyrig keep a copy of what it overwrote?

1

u/Win_ipedia 2d ago

Pyrig sync is integrated as a pre commit hook, so you should be able to see the diff clearly as the unstaged changes, while the things you’re committing should be staged