From b7afb3821bad1701055a6adc6882291c124940a1 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Tue, 2 Dec 2025 02:01:44 -0500 Subject: [PATCH 01/52] docs: update badges block and fix links to docs + demo actions --- README.md | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8f35f63..c3f519f 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,25 @@ - -[![Maturin User Guide](https://img.shields.io/badge/user-guide-brightgreen?logo=readthedocs&style=flat-square)](https://robust-python.dev/cookiecutter) -[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json&style=flat-square)](https://github.com/astral-sh/uv) -[![Python Versions](https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square)](https://github.com/robust-python/cookiecutter-robust-python) -[![Python demo status](https://github.com/robust-python/cookiecutter-robust-python/actions/workflows/python-demo.yml/badge.svg?style=flat-square)](https://github.com/robust-python/robust-python-demo/actions) -[![Maturin demo status](https://github.com/robust-python/cookiecutter-robust-python/actions/workflows/maturin-demo.yml/badge.svg)](https://github.com/robust-python/robust-maturin-demo/actions) -[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/XZAHSBgqXU) - +[![User Guide][user-guide-badge]][user-guide-page] +[![uv][uv-badge]][uv-page] +[![Python Versions][python-versions-badge]][python-versions-page] +[![Python demo status][robust-python-demo-status-badge]][robust-python-demo-status-page] +[![Maturin demo status][robust-maturin-demo-status-badge]][robust-maturin-demo-status-page] +[![Discord][discord-badge]][discord-page] + +[user-guide-badge]: https://img.shields.io/badge/user-guide-brightgreen?logo=readthedocs&style=flat-square +[user-guide-page]: https://cookiecutter-robust-python.readthedocs.io/ +[uv-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json&style=flat-square +[uv-page]: https://github.com/astral-sh/uv +[python-versions-badge]: https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square +[python-versions-page]: https://github.com/robust-python/cookiecutter-robust-python +[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo +[robust-python-demo-status-page]: https://github.com/robust-python/robust-python-demo +[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo +[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo +[discord-badge]: https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square +[discord-page]: https://discord.gg/XZAHSBgqXU # cookiecutter-robust-python @@ -52,13 +63,10 @@ After generating your project, set it up for development: ```bash cd my-awesome-project -# Set up virtual environment and install dependencies -uvx nox -s setup-venv - -# Initialize git repository with main/develop branches -uvx nox -s setup-git +# Sets up venv + git (same as nox -s setup-venv && nox -s setup-git) +uvx nox -t env -# Set up remote repository (requires empty remote repo to exist) +# Sets up remote repository (requires empty remote repo to exist) uvx nox -s setup-remote ``` From 9bf9d86d0ba6e296c6b3dc8121a0c4cbe33e7bd3 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Wed, 3 Dec 2025 22:36:59 -0500 Subject: [PATCH 02/52] docs: improve readme overall --- README.md | 167 +++++++++++++++++++++++------------------------------- 1 file changed, 70 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index c3f519f..872740f 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,71 @@ # cookiecutter-robust-python -**A template making robust design the default** +**A template optimizing for best practices without sacrificing adaptability.** -**[📚 View Documentation](https://cookiecutter-robust-python.readthedocs.io/)** | **[🐛 Report a Bug](https://github.com/robust-python/cookiecutter-robust-python/issues)** | **[✨ Request a Feature](https://github.com/robust-python/cookiecutter-robust-python/issues)** +--- + + +
+Table of Contents + +- [About](#about) + - [Key Features](#key-features) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Usage](#usage) + - [Project Setup](#project-setup) +- [Roadmap](#roadmap) +- [Why does this project exist?](#why-does-this-project-exist) +- [Contributing](#contributing) + +
+ +## About + +--- + +**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. + +The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. --- -## Quick Start +### Key Features +- Uses [cruft] to allow for easily transitioning: + - Using [maturin] vs not + - repository/CICD provider ([github], [gitlab], [bitbucket]) + - Supported Python Versions +- Out of the box support/testing for major OS's (windows, linux, macos) and all currently supported [python] versions +- Automated template demos for integration testing generated CICD +- Rich documentation explaining [tooling decisions] and rationale +- and just about any other typical CI workflow you can think of (linting, release process, security, etc.) + +### Tooling Summary +- [cruft] for project generation/update +- [uv] for dependency management +- [nox] for CI execution +- [commitizen] for version/changelog management +- [ruff] for linting/formatting +- [basedpyright] for type checking +- [pip-audit] for security vulnerability checking +- [maturin] (optional) for rust integration when needed + +## Getting Started ### Prerequisites -The only requirement is [uv]. +The only requirement is installing [uv]. + +Besides that, it may be useful to install the following to avoid `uvx` installing dependencies at unexpected times: +```terminaloutput +uv tool install nox +uv tool install cruft +uv tool install ruff +uv tool install basedpyright +uv tool install maturin +``` -### Create Your Project +### Usage Navigate to where you want to create your project and run: ```bash @@ -72,21 +125,6 @@ uvx nox -s setup-remote **Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. - -## Most Notable Features -- Modern tooling with [uv], [ruff], [cruft], etc. -- Built for supporting most OS's (windows, linux, macos) and all currently supported [python] versions -- Platform agnostic CI/CD ([github], [gitlab], [bitbucket]) -- CI/CD that parities local [nox] sessions for all [python] -- [maturin] support that can be added at any time during the project's lifecycle -- Designed to be a maintainable template over time through the use of automated demos and integration tests -- Rich documentation explaining tooling decisions and rationale - -For a general overview of where we are at with this template, please see the [roadmap](#roadmap) section. - -## Example Output -For an example of this template's output, please visit the [demo](https://github.com/robust-python/robust-python-demo) which is kept up to date with the current state of this template. - ## Roadmap This is a really brief/condensed idea of what is planned for this template, and where it stands currently: @@ -103,18 +141,18 @@ This is a really brief/condensed idea of what is planned for this template, and - [ ] Ensure maturin template works locally - [ ] Add modified CI/CD for the maturin version - [ ] Add CI/CD for the cookiecutter itself -- [ ] Add github actions to automate demo publishing on merge to main or develop in cookiecutter -- [ ] Better define out templates for issues, pull requests, etc. +- [x] Add github actions to automate demo publishing on merge to main or develop in cookiecutter +- [x] Better define out templates for issues, pull requests, etc. - [ ] Improve generated changelogs - [ ] Clean up documentation and make it readable - [ ] Possibly swap documentation to follow MADR (Maybe during clean up process, but low priority for the time being) -- [ ] Move to an organization (Will be done whenever there are other users besides myself) +- [x] Move to an organization (Will be done whenever there are other users besides myself) - [ ] Add any missing automation for administrative tasks - [ ] Designate backup plans for the projects lifecycle over time -# Why does this project exist? +## Why does this project exist? Unfortunately, the [Hypermodern Python Cookiecutter] is no longer maintained nor modern. While it will always have a place in my heart, there have been far too many improvements in Python tooling to keep using it as is. @@ -125,86 +163,19 @@ to new tooling such as [ruff], [uv], [maturin], etc., I found the process of upd The [Hypermodern Python Cookiecutter] remains as a fantastic sendoff point for devs interested in building a 2021-style Python Package. However, there were a handful of issues with it that prevented it from being able to adapt to new Python developments over the years. -# Okay, so what's different this time? - -The [Robust Python Cookiecutter] exists to solve a few main concerns - -- [Template Update Propagation](#template-update-propagation) -- [Project Domain Expansion](#project-domain-expansion) -- [Documenting Tooling Decisions](#documenting-tooling-decisions) -- [CI/CD Vendor Lock](#cicd-vendor-lock) -- [Project Neglect](#project-neglect) - -## Template Update Propagation - -One of the main issues I encountered with [my personal fork] of the [Hypermodern Python Cookiecutter] was that any change -I made to my repos would mean a later conflict if I tried to rerun [cookiecutter] to sync a change from a different project. - -Thankfully, [cruft] exists specifically to help with this issue. It enables us to periodically create PR's to add in any fixes -the [Robust Python Cookiecutter] may have added. - -Additionally, extra care is put in to use tooling specific config files whenever possible to help reduce merge conflicts occurring -in the pyproject.toml. - -## Project Domain Expansion - -Now, I'm not one to advocate for mixing languages in a project. However, there is a unique case that has arisen with the creation of [maturin]. - -There are a plethora of great projects such as [ruff], [uv], [polars], [just], etc. all making use of [maturin] to get the performance improvements of [rust] while -submitting their package to both pypi and crates.io - -Now, this definitely is not required by any means to make a good Python package, however this pattern only seems to be picking up momentum and has honestly been a massive boon -to Python's ecosystem overall. - -That being said, it's generally good practice to avoid the complexity of this dual language system unless you actually need the performance bump for your use case. However knowing ahead of time if performance -will be an issue is rather tricky, and a much easier route is to just prepare as though you _might_ swap to it some day. - -The [Robust Python Cookiecutter] includes an `add_rust_extension` flag that not only toggles [maturin] vs a traditional Python package, -but that can be used in combination with [cruft] to swap to [maturin] at any time with just about no risk to CI/CD / etc. - -Additionally, the [Robust Python Cookiecutter] is designed with both normal and [monorepos] in mind. So whether you need to just add -a quick [rust] module for performance or you are trying to publish a series of crates and packages, either case will be handled using a setup inspired by [polars]. - -## Documenting Tooling Decisions - -One of the really stand out features of the [Hypermodern Python Cookiecutter] was its incredibly detailed documentation. -It did a pretty great job of describing the tooling to use, but there was a distinct lack of **_why_** these decisions were made. - -It may seem like a small detail, but detailing why a decision was made has an incredibly important effect on the maintainablity of the template. - -#### **It allows maintainers to check if a decision should change in one click.** - -Rather than having to go through a mini crusade to determine whether we use [poetry] or [uv], we can just point to the -[existing reasoning](https://cookiecutter-robust-python.readthedocs.io/en/latest/topics/02_dependency_management.md#option-2--term--poetry) to see if it still is true or not. - -Overall, it's rather rare that people debate over tooling for no reason. Most things have merit in some cases, and a large goal of this template is identifying the tools that have the most merit in almost all cases. - -## CI/CD Vendor Lock - -Now don't get me wrong, I love [github-actions] and do pretty much everything in my power to avoid [bitbucket-pipelines]. -However, not all jobs have the luxury of GitHub, and I would love to be able to just use the same template for both my personal and professional projects. - -The [Robust Python Cookiecutter] focuses on being as modular as possible for areas that connect to the CI/CD pipeline. Additionally, there will always be either alternative -CI/CD options or at a minimum basic examples of what the translated CI/CD pipeline would look like. - -Finally, the main reason that this task is even possible is that the [Robust Python Cookiecutter] mirrors all of the CI/CD steps in it's local dev tooling. -The local [noxfile] is designed to match up directly with the CI/CD each step of the way. - -The [Hypermodern Python Cookiecutter] did this where it could afford to also, however the lack of [uv] meant it would significantly increase CI/CD times if done everywhere. -Thankfully now we can spin up a venv with a tiny fraction of the overhead that used to exist. +The goal is for [cookiecutter-robust-python] to fill the gap that exists for a best practices template that is structured to be adaptable from the start. -## Project Neglect -This is most certainly not a knock against claudio. The work they did on [cookiecutter-hypermodern-python] laid the way for countless other devs to start -implementing best practices in their python packages. +## Contributing -However, Open Source work is draining, and is especially so for a project template including metacode. +For more information on contributing to the [Robust Python Cookiecutter], please visit the [contributing] docs. -I can guarantee that if the [Robust Python Cookiecutter] ever sees any number of users, I will immediately transfer it to an organization to enable at least a handful -of trusted individuals to ensure the project is taken care of. +[basedpyright]: https://github.com/DetachHead/basedpyright [bitbucket]: https://bitbucket.org [bitbucket-pipelines]: https://support.atlassian.com/bitbucket-cloud/docs/write-a-pipe-for-bitbucket-pipelines/ +[commitizen]: https://commitizen-tools.github.io/commitizen/ +[contributing]: CONTRIBUTING.md [cookiecutter]: https://cookiecutter.readthedocs.io/en/stable/ [cookiecutter-hypermodern-python]: https://github.com/cjolowicz/cookiecutter-hypermodern-python [cookiecutter-robust-python]: https://github.com/robust-python/cookiecutter-robust-python @@ -219,6 +190,7 @@ of trusted individuals to ensure the project is taken care of. [my personal fork]: https://github.com/56kyle/cookiecutter-hypermodern-python [nox]: https://nox.thea.codes/ [noxfile]: https://github.com/robust-python/cookiecutter-robust-python/blob/main/%7B%7Bcookiecutter.project_name%7D%7D/noxfile.py +[pip-audit]: https://github.com/pypa/pip-audit [poetry]: https://python-poetry.org/docs/ [polars]: https://github.com/pola-rs/polars [python]: https://www.python.org/ @@ -226,5 +198,6 @@ of trusted individuals to ensure the project is taken care of. [ruff]: https://docs.astral.sh/ruff/ [rust]: https://www.rust-lang.org/learn [rye]: https://rye.astral.sh/ +[tooling decisions]: https://cookiecutter-robust-python.readthedocs.io/en/latest/our-chosen-toolchain.html [install uv]: https://docs.astral.sh/uv/getting-started/installation/ [uv]: https://docs.astral.sh/uv/ From cf9c69d0dc038016c87480f17de719557e3aa9ed Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Wed, 3 Dec 2025 22:39:32 -0500 Subject: [PATCH 03/52] docs: move about summary to instead be the main summary --- README.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 872740f..ace629b 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,13 @@ # cookiecutter-robust-python -**A template optimizing for best practices without sacrificing adaptability.** - --- +**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. + +The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. + +---
Table of Contents @@ -52,14 +55,6 @@ ## About ---- - -**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. - -The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. - ---- - ### Key Features - Uses [cruft] to allow for easily transitioning: - Using [maturin] vs not From 7fae0bd9a202eb0ec4eadecee205c8cec0a0d2fb Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:23:54 -0500 Subject: [PATCH 04/52] docs: add shameless star request to readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index ace629b..b192560 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ [discord-page]: https://discord.gg/XZAHSBgqXU +

+⭐ Star us on GitHub — it motivates us a lot and helps to pay the rent! +

+ # cookiecutter-robust-python --- @@ -120,6 +124,10 @@ uvx nox -s setup-remote **Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. +### General CI + + + ## Roadmap This is a really brief/condensed idea of what is planned for this template, and where it stands currently: From 9fb391f493d87ebf16368f3f51d79dd7df0170ce Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:24:44 -0500 Subject: [PATCH 05/52] docs: remove unneeded line break after title due to automatic one in github markdown --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b192560..374977a 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,6 @@ # cookiecutter-robust-python ---- - **[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. From 77a696c7d59f106951c6dd5f92e79cff03b0d368 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:38:18 -0500 Subject: [PATCH 06/52] docs: update usage information in readme --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 374977a..6b67010 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ uv tool install maturin Navigate to where you want to create your project and run: ```bash -uvx cruft create https://github.com/robust-python/cookiecutter-robust-python.git +uvx cruft create https://github.com/robust-python/cookiecutter-robust-python ``` This will prompt you for a few inputs to customize your project: @@ -113,17 +113,15 @@ After generating your project, set it up for development: ```bash cd my-awesome-project -# Sets up venv + git (same as nox -s setup-venv && nox -s setup-git) -uvx nox -t env - -# Sets up remote repository (requires empty remote repo to exist) +uvx nox -s setup-venv +uvx nox -s setup-git +gh repo create my-awesome-project uvx nox -s setup-remote ``` -**Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. - -### General CI +###### *Just a general note, sessions named "setup-..." are not idempotent* +From there all that is left is setting up various integrations like Pypi publishing and Readthedocs as desired. ## Roadmap From 1a7f8125cc15aaed6a9e1c56d4ccacce33b469ee Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 02:03:27 -0500 Subject: [PATCH 07/52] docs: add a section showing different permutation statuses for provider and maturin vs not --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b67010..b9a8008 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,6 @@ [uv-page]: https://github.com/astral-sh/uv [python-versions-badge]: https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square [python-versions-page]: https://github.com/robust-python/cookiecutter-robust-python -[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo -[robust-python-demo-status-page]: https://github.com/robust-python/robust-python-demo -[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo -[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo [discord-badge]: https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square [discord-page]: https://discord.gg/XZAHSBgqXU @@ -150,6 +146,14 @@ This is a really brief/condensed idea of what is planned for this template, and - [ ] Designate backup plans for the projects lifecycle over time
+### Current Status + +| vendor | Demo Statuses | +|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [github] | [![Python demo status][robust-python-demo-status-badge]][robust-python-demo-status-page][![Maturin demo status][robust-maturin-demo-status-badge]][robust-maturin-demo-status-page] | +| [gitlab] | [![Python demo status][robust-gitlab-python-demo-status-badge]][robust-gitlab-python-demo-status-page][![Maturin demo status][robust-gitlab-maturin-demo-status-badge]][robust-gitlab-maturin-demo-status-page] | +| [bitbucket] | [![Python demo status][robust-bitbucket-python-demo-status-badge]][robust-bitbucket-python-demo-status-page][![Maturin demo status][robust-bitbucket-maturin-demo-status-badge]][robust-bitbucket-maturin-demo-status-page] | + ## Why does this project exist? @@ -193,7 +197,19 @@ For more information on contributing to the [Robust Python Cookiecutter], please [poetry]: https://python-poetry.org/docs/ [polars]: https://github.com/pola-rs/polars [python]: https://www.python.org/ +[robust-bitbucket-maturin-demo-status-badge]: https://img.shields.io/bitbucket/pipelines/robust-python/robust-maturin-demo/main?style=flat-square +[robust-bitbucket-maturin-demo-status-page]: https://bitbucket.org/robust-python/robust-maturin-demo/src +[robust-bitbucket-python-demo-status-badge]: https://img.shields.io/bitbucket/pipelines/robust-python/robust-python-demo/main?style=flat-square +[robust-bitbucket-python-demo-status-page]: https://bitbucket.org/robust-python/robust-python-demo/src [robust python cookiecutter]: https://github.com/robust-python/cookiecutter-robust-python +[robust-gitlab-python-demo-status-badge]: https://img.shields.io/gitlab/pipeline-status/robust-python%2Frobust-python-demo?branch=main&style=flat-square +[robust-gitlab-python-demo-status-page]: https://gitlab.com/robust-python/robust-python-demo +[robust-gitlab-maturin-demo-status-badge]: https://img.shields.io/gitlab/pipeline-status/robust-python%2Frobust-maturin-demo?branch=main&style=flat-square +[robust-gitlab-maturin-demo-status-page]: https://gitlab.com/robust-python/robust-maturin-demo +[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo +[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo +[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo +[robust-python-demo-status-page]: https://gitlab.com/robust-python/robust-python-demo [ruff]: https://docs.astral.sh/ruff/ [rust]: https://www.rust-lang.org/learn [rye]: https://rye.astral.sh/ From d0b3785668247f403dc529e4d334ae68b81aba5b Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 02:15:55 -0500 Subject: [PATCH 08/52] docs: slightly clarify wording in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9a8008..f432677 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC ## About ### Key Features -- Uses [cruft] to allow for easily transitioning: +- Uses [cruft] to allow for easily transitioning between: - Using [maturin] vs not - repository/CICD provider ([github], [gitlab], [bitbucket]) - Supported Python Versions From 7d358ffcbf8653065d794fe07ce6c5646ccae689 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 03:05:09 -0500 Subject: [PATCH 09/52] docs: add current-status to the table of contents in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f432677..42db1c0 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [Usage](#usage) - [Project Setup](#project-setup) - [Roadmap](#roadmap) + - [Current Status](#current-status) - [Why does this project exist?](#why-does-this-project-exist) - [Contributing](#contributing) From 319e155e8f9bc6df87551c53eda1dda6c404ff40 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Tue, 2 Dec 2025 02:01:44 -0500 Subject: [PATCH 10/52] docs: update badges block and fix links to docs + demo actions --- README.md | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 8f35f63..c3f519f 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,25 @@ - -[![Maturin User Guide](https://img.shields.io/badge/user-guide-brightgreen?logo=readthedocs&style=flat-square)](https://robust-python.dev/cookiecutter) -[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json&style=flat-square)](https://github.com/astral-sh/uv) -[![Python Versions](https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square)](https://github.com/robust-python/cookiecutter-robust-python) -[![Python demo status](https://github.com/robust-python/cookiecutter-robust-python/actions/workflows/python-demo.yml/badge.svg?style=flat-square)](https://github.com/robust-python/robust-python-demo/actions) -[![Maturin demo status](https://github.com/robust-python/cookiecutter-robust-python/actions/workflows/maturin-demo.yml/badge.svg)](https://github.com/robust-python/robust-maturin-demo/actions) -[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/XZAHSBgqXU) - +[![User Guide][user-guide-badge]][user-guide-page] +[![uv][uv-badge]][uv-page] +[![Python Versions][python-versions-badge]][python-versions-page] +[![Python demo status][robust-python-demo-status-badge]][robust-python-demo-status-page] +[![Maturin demo status][robust-maturin-demo-status-badge]][robust-maturin-demo-status-page] +[![Discord][discord-badge]][discord-page] + +[user-guide-badge]: https://img.shields.io/badge/user-guide-brightgreen?logo=readthedocs&style=flat-square +[user-guide-page]: https://cookiecutter-robust-python.readthedocs.io/ +[uv-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json&style=flat-square +[uv-page]: https://github.com/astral-sh/uv +[python-versions-badge]: https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square +[python-versions-page]: https://github.com/robust-python/cookiecutter-robust-python +[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo +[robust-python-demo-status-page]: https://github.com/robust-python/robust-python-demo +[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo +[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo +[discord-badge]: https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square +[discord-page]: https://discord.gg/XZAHSBgqXU # cookiecutter-robust-python @@ -52,13 +63,10 @@ After generating your project, set it up for development: ```bash cd my-awesome-project -# Set up virtual environment and install dependencies -uvx nox -s setup-venv - -# Initialize git repository with main/develop branches -uvx nox -s setup-git +# Sets up venv + git (same as nox -s setup-venv && nox -s setup-git) +uvx nox -t env -# Set up remote repository (requires empty remote repo to exist) +# Sets up remote repository (requires empty remote repo to exist) uvx nox -s setup-remote ``` From 707334ac34e9442f6abc6a6a693d16437a5c9062 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Wed, 3 Dec 2025 22:36:59 -0500 Subject: [PATCH 11/52] docs: improve readme overall --- README.md | 167 +++++++++++++++++++++++------------------------------- 1 file changed, 70 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index c3f519f..872740f 100644 --- a/README.md +++ b/README.md @@ -30,18 +30,71 @@ # cookiecutter-robust-python -**A template making robust design the default** +**A template optimizing for best practices without sacrificing adaptability.** -**[📚 View Documentation](https://cookiecutter-robust-python.readthedocs.io/)** | **[🐛 Report a Bug](https://github.com/robust-python/cookiecutter-robust-python/issues)** | **[✨ Request a Feature](https://github.com/robust-python/cookiecutter-robust-python/issues)** +--- + + +
+Table of Contents + +- [About](#about) + - [Key Features](#key-features) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Usage](#usage) + - [Project Setup](#project-setup) +- [Roadmap](#roadmap) +- [Why does this project exist?](#why-does-this-project-exist) +- [Contributing](#contributing) + +
+ +## About + +--- + +**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. + +The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. --- -## Quick Start +### Key Features +- Uses [cruft] to allow for easily transitioning: + - Using [maturin] vs not + - repository/CICD provider ([github], [gitlab], [bitbucket]) + - Supported Python Versions +- Out of the box support/testing for major OS's (windows, linux, macos) and all currently supported [python] versions +- Automated template demos for integration testing generated CICD +- Rich documentation explaining [tooling decisions] and rationale +- and just about any other typical CI workflow you can think of (linting, release process, security, etc.) + +### Tooling Summary +- [cruft] for project generation/update +- [uv] for dependency management +- [nox] for CI execution +- [commitizen] for version/changelog management +- [ruff] for linting/formatting +- [basedpyright] for type checking +- [pip-audit] for security vulnerability checking +- [maturin] (optional) for rust integration when needed + +## Getting Started ### Prerequisites -The only requirement is [uv]. +The only requirement is installing [uv]. + +Besides that, it may be useful to install the following to avoid `uvx` installing dependencies at unexpected times: +```terminaloutput +uv tool install nox +uv tool install cruft +uv tool install ruff +uv tool install basedpyright +uv tool install maturin +``` -### Create Your Project +### Usage Navigate to where you want to create your project and run: ```bash @@ -72,21 +125,6 @@ uvx nox -s setup-remote **Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. - -## Most Notable Features -- Modern tooling with [uv], [ruff], [cruft], etc. -- Built for supporting most OS's (windows, linux, macos) and all currently supported [python] versions -- Platform agnostic CI/CD ([github], [gitlab], [bitbucket]) -- CI/CD that parities local [nox] sessions for all [python] -- [maturin] support that can be added at any time during the project's lifecycle -- Designed to be a maintainable template over time through the use of automated demos and integration tests -- Rich documentation explaining tooling decisions and rationale - -For a general overview of where we are at with this template, please see the [roadmap](#roadmap) section. - -## Example Output -For an example of this template's output, please visit the [demo](https://github.com/robust-python/robust-python-demo) which is kept up to date with the current state of this template. - ## Roadmap This is a really brief/condensed idea of what is planned for this template, and where it stands currently: @@ -103,18 +141,18 @@ This is a really brief/condensed idea of what is planned for this template, and - [ ] Ensure maturin template works locally - [ ] Add modified CI/CD for the maturin version - [ ] Add CI/CD for the cookiecutter itself -- [ ] Add github actions to automate demo publishing on merge to main or develop in cookiecutter -- [ ] Better define out templates for issues, pull requests, etc. +- [x] Add github actions to automate demo publishing on merge to main or develop in cookiecutter +- [x] Better define out templates for issues, pull requests, etc. - [ ] Improve generated changelogs - [ ] Clean up documentation and make it readable - [ ] Possibly swap documentation to follow MADR (Maybe during clean up process, but low priority for the time being) -- [ ] Move to an organization (Will be done whenever there are other users besides myself) +- [x] Move to an organization (Will be done whenever there are other users besides myself) - [ ] Add any missing automation for administrative tasks - [ ] Designate backup plans for the projects lifecycle over time -# Why does this project exist? +## Why does this project exist? Unfortunately, the [Hypermodern Python Cookiecutter] is no longer maintained nor modern. While it will always have a place in my heart, there have been far too many improvements in Python tooling to keep using it as is. @@ -125,86 +163,19 @@ to new tooling such as [ruff], [uv], [maturin], etc., I found the process of upd The [Hypermodern Python Cookiecutter] remains as a fantastic sendoff point for devs interested in building a 2021-style Python Package. However, there were a handful of issues with it that prevented it from being able to adapt to new Python developments over the years. -# Okay, so what's different this time? - -The [Robust Python Cookiecutter] exists to solve a few main concerns - -- [Template Update Propagation](#template-update-propagation) -- [Project Domain Expansion](#project-domain-expansion) -- [Documenting Tooling Decisions](#documenting-tooling-decisions) -- [CI/CD Vendor Lock](#cicd-vendor-lock) -- [Project Neglect](#project-neglect) - -## Template Update Propagation - -One of the main issues I encountered with [my personal fork] of the [Hypermodern Python Cookiecutter] was that any change -I made to my repos would mean a later conflict if I tried to rerun [cookiecutter] to sync a change from a different project. - -Thankfully, [cruft] exists specifically to help with this issue. It enables us to periodically create PR's to add in any fixes -the [Robust Python Cookiecutter] may have added. - -Additionally, extra care is put in to use tooling specific config files whenever possible to help reduce merge conflicts occurring -in the pyproject.toml. - -## Project Domain Expansion - -Now, I'm not one to advocate for mixing languages in a project. However, there is a unique case that has arisen with the creation of [maturin]. - -There are a plethora of great projects such as [ruff], [uv], [polars], [just], etc. all making use of [maturin] to get the performance improvements of [rust] while -submitting their package to both pypi and crates.io - -Now, this definitely is not required by any means to make a good Python package, however this pattern only seems to be picking up momentum and has honestly been a massive boon -to Python's ecosystem overall. - -That being said, it's generally good practice to avoid the complexity of this dual language system unless you actually need the performance bump for your use case. However knowing ahead of time if performance -will be an issue is rather tricky, and a much easier route is to just prepare as though you _might_ swap to it some day. - -The [Robust Python Cookiecutter] includes an `add_rust_extension` flag that not only toggles [maturin] vs a traditional Python package, -but that can be used in combination with [cruft] to swap to [maturin] at any time with just about no risk to CI/CD / etc. - -Additionally, the [Robust Python Cookiecutter] is designed with both normal and [monorepos] in mind. So whether you need to just add -a quick [rust] module for performance or you are trying to publish a series of crates and packages, either case will be handled using a setup inspired by [polars]. - -## Documenting Tooling Decisions - -One of the really stand out features of the [Hypermodern Python Cookiecutter] was its incredibly detailed documentation. -It did a pretty great job of describing the tooling to use, but there was a distinct lack of **_why_** these decisions were made. - -It may seem like a small detail, but detailing why a decision was made has an incredibly important effect on the maintainablity of the template. - -#### **It allows maintainers to check if a decision should change in one click.** - -Rather than having to go through a mini crusade to determine whether we use [poetry] or [uv], we can just point to the -[existing reasoning](https://cookiecutter-robust-python.readthedocs.io/en/latest/topics/02_dependency_management.md#option-2--term--poetry) to see if it still is true or not. - -Overall, it's rather rare that people debate over tooling for no reason. Most things have merit in some cases, and a large goal of this template is identifying the tools that have the most merit in almost all cases. - -## CI/CD Vendor Lock - -Now don't get me wrong, I love [github-actions] and do pretty much everything in my power to avoid [bitbucket-pipelines]. -However, not all jobs have the luxury of GitHub, and I would love to be able to just use the same template for both my personal and professional projects. - -The [Robust Python Cookiecutter] focuses on being as modular as possible for areas that connect to the CI/CD pipeline. Additionally, there will always be either alternative -CI/CD options or at a minimum basic examples of what the translated CI/CD pipeline would look like. - -Finally, the main reason that this task is even possible is that the [Robust Python Cookiecutter] mirrors all of the CI/CD steps in it's local dev tooling. -The local [noxfile] is designed to match up directly with the CI/CD each step of the way. - -The [Hypermodern Python Cookiecutter] did this where it could afford to also, however the lack of [uv] meant it would significantly increase CI/CD times if done everywhere. -Thankfully now we can spin up a venv with a tiny fraction of the overhead that used to exist. +The goal is for [cookiecutter-robust-python] to fill the gap that exists for a best practices template that is structured to be adaptable from the start. -## Project Neglect -This is most certainly not a knock against claudio. The work they did on [cookiecutter-hypermodern-python] laid the way for countless other devs to start -implementing best practices in their python packages. +## Contributing -However, Open Source work is draining, and is especially so for a project template including metacode. +For more information on contributing to the [Robust Python Cookiecutter], please visit the [contributing] docs. -I can guarantee that if the [Robust Python Cookiecutter] ever sees any number of users, I will immediately transfer it to an organization to enable at least a handful -of trusted individuals to ensure the project is taken care of. +[basedpyright]: https://github.com/DetachHead/basedpyright [bitbucket]: https://bitbucket.org [bitbucket-pipelines]: https://support.atlassian.com/bitbucket-cloud/docs/write-a-pipe-for-bitbucket-pipelines/ +[commitizen]: https://commitizen-tools.github.io/commitizen/ +[contributing]: CONTRIBUTING.md [cookiecutter]: https://cookiecutter.readthedocs.io/en/stable/ [cookiecutter-hypermodern-python]: https://github.com/cjolowicz/cookiecutter-hypermodern-python [cookiecutter-robust-python]: https://github.com/robust-python/cookiecutter-robust-python @@ -219,6 +190,7 @@ of trusted individuals to ensure the project is taken care of. [my personal fork]: https://github.com/56kyle/cookiecutter-hypermodern-python [nox]: https://nox.thea.codes/ [noxfile]: https://github.com/robust-python/cookiecutter-robust-python/blob/main/%7B%7Bcookiecutter.project_name%7D%7D/noxfile.py +[pip-audit]: https://github.com/pypa/pip-audit [poetry]: https://python-poetry.org/docs/ [polars]: https://github.com/pola-rs/polars [python]: https://www.python.org/ @@ -226,5 +198,6 @@ of trusted individuals to ensure the project is taken care of. [ruff]: https://docs.astral.sh/ruff/ [rust]: https://www.rust-lang.org/learn [rye]: https://rye.astral.sh/ +[tooling decisions]: https://cookiecutter-robust-python.readthedocs.io/en/latest/our-chosen-toolchain.html [install uv]: https://docs.astral.sh/uv/getting-started/installation/ [uv]: https://docs.astral.sh/uv/ From 2c4464a644457e17a14c59843d838d45675f38c6 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Wed, 3 Dec 2025 22:39:32 -0500 Subject: [PATCH 12/52] docs: move about summary to instead be the main summary --- README.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 872740f..ace629b 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,13 @@ # cookiecutter-robust-python -**A template optimizing for best practices without sacrificing adaptability.** - --- +**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. + +The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. + +---
Table of Contents @@ -52,14 +55,6 @@ ## About ---- - -**[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. - -The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. - ---- - ### Key Features - Uses [cruft] to allow for easily transitioning: - Using [maturin] vs not From 18be14b073f450bafb376d2d0ca358faf7c82527 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:23:54 -0500 Subject: [PATCH 13/52] docs: add shameless star request to readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index ace629b..b192560 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ [discord-page]: https://discord.gg/XZAHSBgqXU +

+⭐ Star us on GitHub — it motivates us a lot and helps to pay the rent! +

+ # cookiecutter-robust-python --- @@ -120,6 +124,10 @@ uvx nox -s setup-remote **Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. +### General CI + + + ## Roadmap This is a really brief/condensed idea of what is planned for this template, and where it stands currently: From bbe0e7f8a74f6612648113a9a929d9b7a479cfcc Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:24:44 -0500 Subject: [PATCH 14/52] docs: remove unneeded line break after title due to automatic one in github markdown --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index b192560..374977a 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,6 @@ # cookiecutter-robust-python ---- - **[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. From b378d483dee56aaf226634bac19e77c8fd6feb37 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 00:38:18 -0500 Subject: [PATCH 15/52] docs: update usage information in readme --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 374977a..6b67010 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ uv tool install maturin Navigate to where you want to create your project and run: ```bash -uvx cruft create https://github.com/robust-python/cookiecutter-robust-python.git +uvx cruft create https://github.com/robust-python/cookiecutter-robust-python ``` This will prompt you for a few inputs to customize your project: @@ -113,17 +113,15 @@ After generating your project, set it up for development: ```bash cd my-awesome-project -# Sets up venv + git (same as nox -s setup-venv && nox -s setup-git) -uvx nox -t env - -# Sets up remote repository (requires empty remote repo to exist) +uvx nox -s setup-venv +uvx nox -s setup-git +gh repo create my-awesome-project uvx nox -s setup-remote ``` -**Quick setup:** Run `uvx nox -t env` to execute all environment setup tasks at once. - -### General CI +###### *Just a general note, sessions named "setup-..." are not idempotent* +From there all that is left is setting up various integrations like Pypi publishing and Readthedocs as desired. ## Roadmap From fbd0751beb21a495ae713e48284e42960d18245f Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 02:03:27 -0500 Subject: [PATCH 16/52] docs: add a section showing different permutation statuses for provider and maturin vs not --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6b67010..b9a8008 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,6 @@ [uv-page]: https://github.com/astral-sh/uv [python-versions-badge]: https://img.shields.io/pypi/pyversions/robust-python-demo?style=flat-square [python-versions-page]: https://github.com/robust-python/cookiecutter-robust-python -[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo -[robust-python-demo-status-page]: https://github.com/robust-python/robust-python-demo -[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo -[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo [discord-badge]: https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white&style=flat-square [discord-page]: https://discord.gg/XZAHSBgqXU @@ -150,6 +146,14 @@ This is a really brief/condensed idea of what is planned for this template, and - [ ] Designate backup plans for the projects lifecycle over time
+### Current Status + +| vendor | Demo Statuses | +|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [github] | [![Python demo status][robust-python-demo-status-badge]][robust-python-demo-status-page][![Maturin demo status][robust-maturin-demo-status-badge]][robust-maturin-demo-status-page] | +| [gitlab] | [![Python demo status][robust-gitlab-python-demo-status-badge]][robust-gitlab-python-demo-status-page][![Maturin demo status][robust-gitlab-maturin-demo-status-badge]][robust-gitlab-maturin-demo-status-page] | +| [bitbucket] | [![Python demo status][robust-bitbucket-python-demo-status-badge]][robust-bitbucket-python-demo-status-page][![Maturin demo status][robust-bitbucket-maturin-demo-status-badge]][robust-bitbucket-maturin-demo-status-page] | + ## Why does this project exist? @@ -193,7 +197,19 @@ For more information on contributing to the [Robust Python Cookiecutter], please [poetry]: https://python-poetry.org/docs/ [polars]: https://github.com/pola-rs/polars [python]: https://www.python.org/ +[robust-bitbucket-maturin-demo-status-badge]: https://img.shields.io/bitbucket/pipelines/robust-python/robust-maturin-demo/main?style=flat-square +[robust-bitbucket-maturin-demo-status-page]: https://bitbucket.org/robust-python/robust-maturin-demo/src +[robust-bitbucket-python-demo-status-badge]: https://img.shields.io/bitbucket/pipelines/robust-python/robust-python-demo/main?style=flat-square +[robust-bitbucket-python-demo-status-page]: https://bitbucket.org/robust-python/robust-python-demo/src [robust python cookiecutter]: https://github.com/robust-python/cookiecutter-robust-python +[robust-gitlab-python-demo-status-badge]: https://img.shields.io/gitlab/pipeline-status/robust-python%2Frobust-python-demo?branch=main&style=flat-square +[robust-gitlab-python-demo-status-page]: https://gitlab.com/robust-python/robust-python-demo +[robust-gitlab-maturin-demo-status-badge]: https://img.shields.io/gitlab/pipeline-status/robust-python%2Frobust-maturin-demo?branch=main&style=flat-square +[robust-gitlab-maturin-demo-status-page]: https://gitlab.com/robust-python/robust-maturin-demo +[robust-maturin-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-maturin-demo/release-python.yml?branch=main&style=flat-square&label=robust-maturin-demo +[robust-maturin-demo-status-page]: https://github.com/robust-python/robust-maturin-demo +[robust-python-demo-status-badge]: https://img.shields.io/github/actions/workflow/status/robust-python/robust-python-demo/release-python.yml?branch=main&style=flat-square&label=robust-python-demo +[robust-python-demo-status-page]: https://gitlab.com/robust-python/robust-python-demo [ruff]: https://docs.astral.sh/ruff/ [rust]: https://www.rust-lang.org/learn [rye]: https://rye.astral.sh/ From a4e1621592d6483d7693ae7076dcfc5e55811bed Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 02:15:55 -0500 Subject: [PATCH 17/52] docs: slightly clarify wording in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9a8008..f432677 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC ## About ### Key Features -- Uses [cruft] to allow for easily transitioning: +- Uses [cruft] to allow for easily transitioning between: - Using [maturin] vs not - repository/CICD provider ([github], [gitlab], [bitbucket]) - Supported Python Versions From 57d9357dd457d95f13d324f04b854d62f2bb4ccd Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 03:05:09 -0500 Subject: [PATCH 18/52] docs: add current-status to the table of contents in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f432677..42db1c0 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [Usage](#usage) - [Project Setup](#project-setup) - [Roadmap](#roadmap) + - [Current Status](#current-status) - [Why does this project exist?](#why-does-this-project-exist) - [Contributing](#contributing) From cffc54e51d0c4596af705d16bbb275164923da5f Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 06:33:42 -0500 Subject: [PATCH 19/52] fix: add two additional fetches that ensure branch actually exists locally so CICD doesn't error on update demo workflow --- scripts/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/util.py b/scripts/util.py index d09b47d..8f00037 100644 --- a/scripts/util.py +++ b/scripts/util.py @@ -131,6 +131,8 @@ def require_clean_and_up_to_date_demo_repo(demo_path: Path) -> None: try: with work_in(demo_path): git("fetch") + git("fetch", "origin", f"{DEMO.main_branch}:{DEMO.main_branch}") + git("fetch", "origin", f"{DEMO.develop_branch}:{DEMO.develop_branch}") git("status", "--porcelain") validate_is_synced_ancestor(ancestor=DEMO.main_branch, descendent=DEMO.develop_branch) except Exception as e: From 48ede1245c4bdb00467d90e232062f4e4c908398 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 06:35:53 -0500 Subject: [PATCH 20/52] fix: remove backup creation of develop branch due to more purposeful checkout existing --- scripts/util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/util.py b/scripts/util.py index 8f00037..9cc862e 100644 --- a/scripts/util.py +++ b/scripts/util.py @@ -132,7 +132,6 @@ def require_clean_and_up_to_date_demo_repo(demo_path: Path) -> None: with work_in(demo_path): git("fetch") git("fetch", "origin", f"{DEMO.main_branch}:{DEMO.main_branch}") - git("fetch", "origin", f"{DEMO.develop_branch}:{DEMO.develop_branch}") git("status", "--porcelain") validate_is_synced_ancestor(ancestor=DEMO.main_branch, descendent=DEMO.develop_branch) except Exception as e: From 33ad187d00ece85b3d0ef57841a2c9b558d35250 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 06:43:53 -0500 Subject: [PATCH 21/52] fix: replace check against commit hash to instead check against HEAD in update-demo workflow --- scripts/update-demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index d9a151f..1c8d127 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -68,7 +68,7 @@ def update_demo( fg=typer.colors.YELLOW ) - if not is_ancestor(last_update_commit, template_commit): + if not is_ancestor(last_update_commit, "HEAD"): raise ValueError( f"The last update commit '{last_update_commit}' is not an ancestor of the current commit " f"'{template_commit}'." From 3758046df7fd35eec551e56aff08941bc8ff2bee Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 06:47:52 -0500 Subject: [PATCH 22/52] fix: try removing ancestry check to see if it actually matters --- scripts/update-demo.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 1c8d127..e622fa6 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -68,12 +68,6 @@ def update_demo( fg=typer.colors.YELLOW ) - if not is_ancestor(last_update_commit, "HEAD"): - raise ValueError( - f"The last update commit '{last_update_commit}' is not an ancestor of the current commit " - f"'{template_commit}'." - ) - typer.secho(f"Updating demo project at {demo_path=}.", fg="yellow") with work_in(demo_path): if get_current_branch() != desired_branch_name: From 215cefb2ce398b89c5e5e375b2dac03987922bec Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 18:26:47 -0500 Subject: [PATCH 23/52] Revert "fix: try removing ancestry check to see if it actually matters" This reverts commit 3758046df7fd35eec551e56aff08941bc8ff2bee. --- scripts/update-demo.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index e622fa6..1c8d127 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -68,6 +68,12 @@ def update_demo( fg=typer.colors.YELLOW ) + if not is_ancestor(last_update_commit, "HEAD"): + raise ValueError( + f"The last update commit '{last_update_commit}' is not an ancestor of the current commit " + f"'{template_commit}'." + ) + typer.secho(f"Updating demo project at {demo_path=}.", fg="yellow") with work_in(demo_path): if get_current_branch() != desired_branch_name: From 5759c2bd6e286b5f5643825a4d53b653c01c662c Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 18:27:01 -0500 Subject: [PATCH 24/52] Revert "fix: replace check against commit hash to instead check against HEAD in update-demo workflow" This reverts commit 33ad187d00ece85b3d0ef57841a2c9b558d35250. --- scripts/update-demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 1c8d127..d9a151f 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -68,7 +68,7 @@ def update_demo( fg=typer.colors.YELLOW ) - if not is_ancestor(last_update_commit, "HEAD"): + if not is_ancestor(last_update_commit, template_commit): raise ValueError( f"The last update commit '{last_update_commit}' is not an ancestor of the current commit " f"'{template_commit}'." From 039f66d3448d8cb43062bbd412eb72b148d3a92d Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 18:52:21 -0500 Subject: [PATCH 25/52] chore: add some debug messaging --- scripts/update-demo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index d9a151f..5eabd11 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -50,6 +50,7 @@ def update_demo( demo_name: str = get_demo_name(add_rust_extension=add_rust_extension) demo_path: Path = demos_cache_folder / demo_name + typer.secho(f"template:\n\tcurrent_branch: {get_current_branch()}\n\tcurrent_commit: {get_current_commit()}") if branch_override is not None: typer.secho(f"Overriding current branch name for demo reference. Using '{branch_override}' instead.") desired_branch_name: str = branch_override @@ -76,6 +77,7 @@ def update_demo( typer.secho(f"Updating demo project at {demo_path=}.", fg="yellow") with work_in(demo_path): + typer.secho(f"template:\n\tcurrent_branch: {get_current_branch()}\n\tcurrent_commit: {get_current_commit()}") if get_current_branch() != desired_branch_name: git("checkout", "-b", desired_branch_name, DEMO.develop_branch) From cbbce26f84efd94a2a2941503362dcd97179a3ff Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 18:55:05 -0500 Subject: [PATCH 26/52] chore: add fetch depth to both template and demo --- .github/workflows/update-demo.yml | 1 + scripts/update-demo.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index 509c94e..b6cf2a2 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -19,6 +19,7 @@ jobs: - name: Checkout Template uses: actions/checkout@v4 with: + fetch-depth: 0 repository: ${{ github.repository }} path: "${{ github.workspace }}/cookiecutter-robust-python" diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 5eabd11..ace5ca0 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -77,7 +77,7 @@ def update_demo( typer.secho(f"Updating demo project at {demo_path=}.", fg="yellow") with work_in(demo_path): - typer.secho(f"template:\n\tcurrent_branch: {get_current_branch()}\n\tcurrent_commit: {get_current_commit()}") + typer.secho(f"demo:\n\tcurrent_branch: {get_current_branch()}\n\tcurrent_commit: {get_current_commit()}") if get_current_branch() != desired_branch_name: git("checkout", "-b", desired_branch_name, DEMO.develop_branch) From 2a5f13c645c7951a9d5ed51252823318ac412df5 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 18:57:39 -0500 Subject: [PATCH 27/52] fix: add git config to update-demo workflow --- .github/workflows/update-demo.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index b6cf2a2..67ad7a9 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -39,6 +39,11 @@ jobs: with: python-version-file: "${{ github.workspace }}/cookiecutter-robust-python/.github/workflows/.python-version" + - name: Configure Git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - name: Update Demo working-directory: "${{ github.workspace }}/cookiecutter-robust-python" run: "uvx nox -s 'update-demo(${{ inputs.demo_name }})' -- --branch-override ${{ github.head_ref }}" From 3a46af39021f5c0808aa6ae69c67819d01befdf8 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 19:02:20 -0500 Subject: [PATCH 28/52] chore: move git configuration to act in the template repo --- .github/workflows/update-demo.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index 67ad7a9..0f00abd 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -23,6 +23,12 @@ jobs: repository: ${{ github.repository }} path: "${{ github.workspace }}/cookiecutter-robust-python" + - name: Configure Git + working-directory: "${{ github.workspace }}/cookiecutter-robust-python" + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - name: Checkout Demo uses: actions/checkout@v4 with: @@ -39,11 +45,6 @@ jobs: with: python-version-file: "${{ github.workspace }}/cookiecutter-robust-python/.github/workflows/.python-version" - - name: Configure Git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - name: Update Demo working-directory: "${{ github.workspace }}/cookiecutter-robust-python" run: "uvx nox -s 'update-demo(${{ inputs.demo_name }})' -- --branch-override ${{ github.head_ref }}" From d294b578d51e0c042d1be7d760739203e192223d Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 19:20:50 -0500 Subject: [PATCH 29/52] chore: try swapping sync-demos to just be on non workflow targeted branch push --- .github/workflows/sync-demos.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-demos.yml b/.github/workflows/sync-demos.yml index 04fcb30..84853e0 100644 --- a/.github/workflows/sync-demos.yml +++ b/.github/workflows/sync-demos.yml @@ -1,8 +1,10 @@ name: sync-demo.yml on: - pull_request: + push: branches: - - develop + - "!main" + - "!develop" + - "!release/*" jobs: update-demo: From 00ee317f63a008d032f63564731b8212382fade4 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Thu, 4 Dec 2025 19:23:01 -0500 Subject: [PATCH 30/52] fix: adjust syntax to use branches-ignore in sync-demos workflow --- .github/workflows/sync-demos.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/sync-demos.yml b/.github/workflows/sync-demos.yml index 84853e0..2daa232 100644 --- a/.github/workflows/sync-demos.yml +++ b/.github/workflows/sync-demos.yml @@ -1,10 +1,10 @@ name: sync-demo.yml on: push: - branches: - - "!main" - - "!develop" - - "!release/*" + branches-ignore: + - "main" + - "develop" + - "release/*" jobs: update-demo: From 2072585cea4bf6385460c27a68f02ec77eca3cf1 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 21:57:34 -0500 Subject: [PATCH 31/52] fix: add passthrough for branch name to differentiate branch name source for pull requests and straight pushes --- .github/workflows/merge-demo-feature.yml | 1 + .github/workflows/sync-demos.yml | 1 + .github/workflows/update-demo.yml | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/merge-demo-feature.yml b/.github/workflows/merge-demo-feature.yml index 80fe139..6f633b6 100644 --- a/.github/workflows/merge-demo-feature.yml +++ b/.github/workflows/merge-demo-feature.yml @@ -21,6 +21,7 @@ jobs: - "robust-maturin-demo" with: demo_name: ${{ matrix.demo_name }} + branch: ${{ github.ref_name }} merge-demo-feature: name: Merge Demo Feature diff --git a/.github/workflows/sync-demos.yml b/.github/workflows/sync-demos.yml index 2daa232..6a1a1ef 100644 --- a/.github/workflows/sync-demos.yml +++ b/.github/workflows/sync-demos.yml @@ -17,3 +17,4 @@ jobs: - "robust-maturin-demo" with: demo_name: ${{ matrix.demo_name }} + branch: ${{ github.ref_name }} diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index 0f00abd..f9c333e 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -5,6 +5,9 @@ on: demo_name: required: true type: string + branch: + required: true + type: string env: COOKIECUTTER_ROBUST_PYTHON__DEMOS_CACHE_FOLDER: ${{ github.workspace }} @@ -47,4 +50,4 @@ jobs: - name: Update Demo working-directory: "${{ github.workspace }}/cookiecutter-robust-python" - run: "uvx nox -s 'update-demo(${{ inputs.demo_name }})' -- --branch-override ${{ github.head_ref }}" + run: "uvx nox -s 'update-demo(${{ inputs.demo_name }})' -- --branch-override ${{ inputs.branch }}" From 0bbfdf5fe57b1699a85bb5814938f7277219cdce Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:00:17 -0500 Subject: [PATCH 32/52] fix: add --global kwargs to git config calls to see if that fixes the error --- .github/workflows/update-demo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index f9c333e..84936aa 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -29,8 +29,8 @@ jobs: - name: Configure Git working-directory: "${{ github.workspace }}/cookiecutter-robust-python" run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" - name: Checkout Demo uses: actions/checkout@v4 From dcbebc392e76793fb44b6261d86978603c3ebc63 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:06:43 -0500 Subject: [PATCH 33/52] chore: add token to demo checkout to see if it helps --- .github/workflows/update-demo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index 84936aa..b5558eb 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -39,6 +39,7 @@ jobs: repository: "${{ github.repository_owner }}/${{ inputs.demo_name }}" path: ${{ inputs.demo_name }} ref: develop + token: ${{ secrets.GITHUB_TOKEN }} - name: Set up uv uses: astral-sh/setup-uv@v6 From 1aef815843c5d062b22b45cd9489b5db713d8da7 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:31:51 -0500 Subject: [PATCH 34/52] feat: replace default github token with premade PAT named DEMO_TOKEN --- .github/workflows/update-demo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index b5558eb..ea15a73 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -39,7 +39,7 @@ jobs: repository: "${{ github.repository_owner }}/${{ inputs.demo_name }}" path: ${{ inputs.demo_name }} ref: develop - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.DEMO_TOKEN }} - name: Set up uv uses: astral-sh/setup-uv@v6 From 82707d78fe5d20bd8af032bd81747ff564ca4d34 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:36:51 -0500 Subject: [PATCH 35/52] feat: add workflow_call secrets passthrough --- .github/workflows/update-demo.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index ea15a73..cfc9508 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -8,6 +8,9 @@ on: branch: required: true type: string + secrets: + DEMO_TOKEN: + required: true env: COOKIECUTTER_ROBUST_PYTHON__DEMOS_CACHE_FOLDER: ${{ github.workspace }} From fd2614dc1c5419a8f368ab315db526429b5dfbca Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:39:37 -0500 Subject: [PATCH 36/52] feat: add passthrough of DEMO_TOKEN wherever needed --- .github/workflows/merge-demo-feature.yml | 1 + .github/workflows/sync-demos.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/merge-demo-feature.yml b/.github/workflows/merge-demo-feature.yml index 6f633b6..d654c8f 100644 --- a/.github/workflows/merge-demo-feature.yml +++ b/.github/workflows/merge-demo-feature.yml @@ -43,6 +43,7 @@ jobs: repository: "${{ github.repository_owner }}/${{ inputs.demo_name }}" path: ${{ inputs.demo_name }} ref: develop + token: ${{ secrets.DEMO_TOKEN }} - name: Set up uv uses: astral-sh/setup-uv@v6 diff --git a/.github/workflows/sync-demos.yml b/.github/workflows/sync-demos.yml index 6a1a1ef..a9c0c56 100644 --- a/.github/workflows/sync-demos.yml +++ b/.github/workflows/sync-demos.yml @@ -18,3 +18,5 @@ jobs: with: demo_name: ${{ matrix.demo_name }} branch: ${{ github.ref_name }} + secrets: + DEMO_TOKEN: ${{ secrets.DEMO_TOKEN }} From 095e6bf0790540139f93e710e3dd67ad93995d26 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 22:46:16 -0500 Subject: [PATCH 37/52] feat: add GH_TOKEN adjacent to DEMO_TOKEN usages --- .github/workflows/merge-demo-feature.yml | 2 ++ .github/workflows/update-demo.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/merge-demo-feature.yml b/.github/workflows/merge-demo-feature.yml index d654c8f..ab20739 100644 --- a/.github/workflows/merge-demo-feature.yml +++ b/.github/workflows/merge-demo-feature.yml @@ -31,6 +31,8 @@ jobs: demo_name: - "robust-python-demo" - "robust-maturin-demo" + env: + GH_TOKEN: ${{ secrets.DEMO_TOKEN }} steps: - name: Checkout Template uses: actions/checkout@v4 diff --git a/.github/workflows/update-demo.yml b/.github/workflows/update-demo.yml index cfc9508..3801530 100644 --- a/.github/workflows/update-demo.yml +++ b/.github/workflows/update-demo.yml @@ -17,6 +17,7 @@ env: COOKIECUTTER_ROBUST_PYTHON__APP_AUTHOR: ${{ github.repository_owner }} ROBUST_PYTHON_DEMO__APP_AUTHOR: ${{ github.repository_owner }} ROBUST_MATURIN_DEMO__APP_AUTHOR: ${{ github.repository_owner }} + GH_TOKEN: ${{ secrets.DEMO_TOKEN }} jobs: update-demo: From 037f27b3adddc5226d6c6d16800f4fed7f30e101 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:04:20 -0500 Subject: [PATCH 38/52] feat: add debugging logs marking the location of demo PR's made --- scripts/update-demo.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index ace5ca0..14687ff 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -148,8 +148,10 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: """Creates a PR to merge the given branch into develop.""" gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) + if "no pull requests match your search" not in search_results.stdout: - typer.secho(f"Skipping PR creation due to existing PR found for branch {branch}") + url: str = _get_pr_url(branch=branch) + typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return body: str = _get_demo_feature_pr_body(demo_path=demo_path, commit_start=commit_start) @@ -162,6 +164,16 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: "--repo": f"{DEMO.app_author}/{DEMO.app_name}", } gh("pr", "create", *itertools.chain.from_iterable(pr_kwargs.items())) + url: str = _get_pr_url(branch=branch) + typer.secho(f"Created PR URL for branch {branch} at {url}") + + +def _get_pr_url(branch: str) -> str: + """Returns the url of the current branch's PR.""" + result: subprocess.CompletedProcess = gh("pr", "view", branch, "--json", "url", "--jq", ".url") + if result.returncode != 0: + raise ValueError(f"Failed to find a PR URL for branch {branch}.") + return result.stdout.strip() def _get_demo_feature_pr_body(demo_path: Path, commit_start: str) -> str: From d5f4b78a319987e371cf8c1b7382257e99e6edca Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:08:52 -0500 Subject: [PATCH 39/52] feat: remove extra url marker for stability temporarily --- scripts/update-demo.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 14687ff..8a202ef 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -164,8 +164,6 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: "--repo": f"{DEMO.app_author}/{DEMO.app_name}", } gh("pr", "create", *itertools.chain.from_iterable(pr_kwargs.items())) - url: str = _get_pr_url(branch=branch) - typer.secho(f"Created PR URL for branch {branch} at {url}") def _get_pr_url(branch: str) -> str: From d6129d74a4fada128fa3a7dc3890fb67b10e3612 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:24:15 -0500 Subject: [PATCH 40/52] fix: add utf-8 decodes where missing --- scripts/update-demo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 8a202ef..8124c30 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -149,7 +149,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) - if "no pull requests match your search" not in search_results.stdout: + if "no pull requests match your search" not in search_results.stdout.decode("utf-8").strip(): url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return @@ -171,7 +171,7 @@ def _get_pr_url(branch: str) -> str: result: subprocess.CompletedProcess = gh("pr", "view", branch, "--json", "url", "--jq", ".url") if result.returncode != 0: raise ValueError(f"Failed to find a PR URL for branch {branch}.") - return result.stdout.strip() + return result.stdout.decode("utf-8").strip() def _get_demo_feature_pr_body(demo_path: Path, commit_start: str) -> str: From 3e872f2d4af71339c332c51022e67d7d0db0614c Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:26:54 -0500 Subject: [PATCH 41/52] fix: remove decodes that aren't relevant --- scripts/update-demo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 8124c30..8a202ef 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -149,7 +149,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) - if "no pull requests match your search" not in search_results.stdout.decode("utf-8").strip(): + if "no pull requests match your search" not in search_results.stdout: url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return @@ -171,7 +171,7 @@ def _get_pr_url(branch: str) -> str: result: subprocess.CompletedProcess = gh("pr", "view", branch, "--json", "url", "--jq", ".url") if result.returncode != 0: raise ValueError(f"Failed to find a PR URL for branch {branch}.") - return result.stdout.decode("utf-8").strip() + return result.stdout.strip() def _get_demo_feature_pr_body(demo_path: Path, commit_start: str) -> str: From f61fb7c737ca0ea5df9592e4d9dd78a73ed7dabf Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:30:10 -0500 Subject: [PATCH 42/52] chore: add some debugging statements --- scripts/update-demo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 8a202ef..64d9a8c 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -148,6 +148,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: """Creates a PR to merge the given branch into develop.""" gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) + typer.secho(f"_create_demo_pr - {search_results.stdout}") if "no pull requests match your search" not in search_results.stdout: url: str = _get_pr_url(branch=branch) @@ -169,6 +170,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: def _get_pr_url(branch: str) -> str: """Returns the url of the current branch's PR.""" result: subprocess.CompletedProcess = gh("pr", "view", branch, "--json", "url", "--jq", ".url") + typer.secho(f"_get_pr_url - {result.stdout=}") if result.returncode != 0: raise ValueError(f"Failed to find a PR URL for branch {branch}.") return result.stdout.strip() From d657da3f54a049df148506be84a74ad50c499f4d Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:33:12 -0500 Subject: [PATCH 43/52] fix: shrink the scope of text checked for to ensure differing gh cli versions don't differ wording --- scripts/update-demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 64d9a8c..92059d8 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -150,7 +150,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) typer.secho(f"_create_demo_pr - {search_results.stdout}") - if "no pull requests match your search" not in search_results.stdout: + if "no pull requests" not in search_results.stdout: url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return From 2426bb75661d7a37d72a0e2b84f22706a1ba30dc Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:38:34 -0500 Subject: [PATCH 44/52] chore: add a string wrapper to ensure stdout is comparing properly --- scripts/update-demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 92059d8..9e0aaeb 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -150,7 +150,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) typer.secho(f"_create_demo_pr - {search_results.stdout}") - if "no pull requests" not in search_results.stdout: + if "no pull requests" not in str(search_results.stdout): url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return From 64091d91ffbe3d05da1af10edaa2ab65852600df Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:41:07 -0500 Subject: [PATCH 45/52] chore: add more debugging --- scripts/update-demo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 9e0aaeb..ca97504 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -148,9 +148,11 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: """Creates a PR to merge the given branch into develop.""" gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) - typer.secho(f"_create_demo_pr - {search_results.stdout}") + typer.secho(f"_create_demo_pr - {search_results.stdout=}") if "no pull requests" not in str(search_results.stdout): + typer.secho(f"{str(search_results.stdout)=}") + typer.secho(f"no_pull_requests") url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return From 20dc779b9579f42fe7a4e21d796a02abeeb2eeab Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:57:12 -0500 Subject: [PATCH 46/52] fix: swap to just checking return code --- scripts/update-demo.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index ca97504..ae0e8f0 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -148,11 +148,8 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: """Creates a PR to merge the given branch into develop.""" gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) - typer.secho(f"_create_demo_pr - {search_results.stdout=}") - if "no pull requests" not in str(search_results.stdout): - typer.secho(f"{str(search_results.stdout)=}") - typer.secho(f"no_pull_requests") + if search_results.returncode != 0: url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return @@ -172,7 +169,6 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: def _get_pr_url(branch: str) -> str: """Returns the url of the current branch's PR.""" result: subprocess.CompletedProcess = gh("pr", "view", branch, "--json", "url", "--jq", ".url") - typer.secho(f"_get_pr_url - {result.stdout=}") if result.returncode != 0: raise ValueError(f"Failed to find a PR URL for branch {branch}.") return result.stdout.strip() From 889ade99bfecd377a1e9d73590bed8d76d99f875 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Sun, 7 Dec 2025 23:59:39 -0500 Subject: [PATCH 47/52] feat: add additional note pointing toward newly made PR url --- scripts/update-demo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index ae0e8f0..705ed78 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -164,6 +164,8 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: "--repo": f"{DEMO.app_author}/{DEMO.app_name}", } gh("pr", "create", *itertools.chain.from_iterable(pr_kwargs.items())) + url: str = _get_pr_url(branch=branch) + typer.secho(f"Created PR for branch '{branch}' at '{url}'.") def _get_pr_url(branch: str) -> str: From d01ac59e5d1ac3b46d24b5bc23dfc12ab98c7045 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Mon, 8 Dec 2025 00:04:26 -0500 Subject: [PATCH 48/52] fix: adjust inversed returncode chec --- scripts/update-demo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-demo.py b/scripts/update-demo.py index 705ed78..397af93 100644 --- a/scripts/update-demo.py +++ b/scripts/update-demo.py @@ -149,7 +149,7 @@ def _create_demo_pr(demo_path: Path, branch: str, commit_start: str) -> None: gh("repo", "set-default", f"{DEMO.app_author}/{DEMO.app_name}") search_results: subprocess.CompletedProcess = gh("pr", "list", "--state", "open", "--search", branch) - if search_results.returncode != 0: + if search_results.returncode == 0: url: str = _get_pr_url(branch=branch) typer.secho(f"Skipping PR creation due to existing PR found for branch {branch} at {url}") return From c538b9bc1ec50a9cad345cc7648f8277c2fbcd90 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Mon, 8 Dec 2025 00:18:50 -0500 Subject: [PATCH 49/52] feat: add pin image for use in readme --- docs/_static/pin.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/_static/pin.svg diff --git a/docs/_static/pin.svg b/docs/_static/pin.svg new file mode 100644 index 0000000..d3ef87e --- /dev/null +++ b/docs/_static/pin.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From aa0873348176f9fa3e701ab26aaa222e63a0ba0e Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Mon, 8 Dec 2025 00:32:50 -0500 Subject: [PATCH 50/52] docs: add pin images and generally pull a lot of inspiration from the areg-sdk repo --- README.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 42db1c0..ccccf81 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ ⭐ Star us on GitHub — it motivates us a lot and helps to pay the rent! -# cookiecutter-robust-python +--- **[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. @@ -36,8 +36,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC --- -
-Table of Contents +## Table of Contents[![](./docs/_static/pin.svg)](#table-of-contents) - [About](#about) - [Key Features](#key-features) @@ -50,9 +49,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [Why does this project exist?](#why-does-this-project-exist) - [Contributing](#contributing) -
- -## About +## About[![](./docs/_static/pin.svg)](#table-of-contents) ### Key Features - Uses [cruft] to allow for easily transitioning between: @@ -74,7 +71,12 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [pip-audit] for security vulnerability checking - [maturin] (optional) for rust integration when needed -## Getting Started + + + +--- + +## Getting Started[![](./docs/_static/pin.svg)](#table-of-contents) ### Prerequisites The only requirement is installing [uv]. @@ -116,12 +118,15 @@ gh repo create my-awesome-project uvx nox -s setup-remote ``` -###### *Just a general note, sessions named "setup-..." are not idempotent* +> ⚠️ Scripts and nox sessions prefixed with "setup-" are usually not idempotent, although some will try to warn you if misused. From there all that is left is setting up various integrations like Pypi publishing and Readthedocs as desired. + -## Roadmap +--- + +## Roadmap[![](./docs/_static/pin.svg)](#table-of-contents) This is a really brief/condensed idea of what is planned for this template, and where it stands currently:
@@ -155,8 +160,11 @@ This is a really brief/condensed idea of what is planned for this template, and | [gitlab] | [![Python demo status][robust-gitlab-python-demo-status-badge]][robust-gitlab-python-demo-status-page][![Maturin demo status][robust-gitlab-maturin-demo-status-badge]][robust-gitlab-maturin-demo-status-page] | | [bitbucket] | [![Python demo status][robust-bitbucket-python-demo-status-badge]][robust-bitbucket-python-demo-status-page][![Maturin demo status][robust-bitbucket-maturin-demo-status-badge]][robust-bitbucket-maturin-demo-status-page] | + + +--- -## Why does this project exist? +## Why does this project exist?[![](./docs/_static/pin.svg)](#table-of-contents) Unfortunately, the [Hypermodern Python Cookiecutter] is no longer maintained nor modern. While it will always have a place in my heart, there have been far too many improvements in Python tooling to keep using it as is. @@ -169,8 +177,11 @@ a handful of issues with it that prevented it from being able to adapt to new Py The goal is for [cookiecutter-robust-python] to fill the gap that exists for a best practices template that is structured to be adaptable from the start. + + +--- -## Contributing +## Contributing[![](./docs/_static/pin.svg)](#table-of-contents) For more information on contributing to the [Robust Python Cookiecutter], please visit the [contributing] docs. From e7f4ddfc4d227093938c32cf07fe884d1b82587c Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Mon, 8 Dec 2025 00:35:14 -0500 Subject: [PATCH 51/52] docs: adjust links in pins to match section locations --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ccccf81..51c1229 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [Why does this project exist?](#why-does-this-project-exist) - [Contributing](#contributing) -## About[![](./docs/_static/pin.svg)](#table-of-contents) +## About[![](./docs/_static/pin.svg)](#about) ### Key Features - Uses [cruft] to allow for easily transitioning between: @@ -76,7 +76,7 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC --- -## Getting Started[![](./docs/_static/pin.svg)](#table-of-contents) +## Getting Started[![](./docs/_static/pin.svg)](#getting-started) ### Prerequisites The only requirement is installing [uv]. @@ -126,7 +126,7 @@ From there all that is left is setting up various integrations like Pypi publish --- -## Roadmap[![](./docs/_static/pin.svg)](#table-of-contents) +## Roadmap[![](./docs/_static/pin.svg)](#roadmap) This is a really brief/condensed idea of what is planned for this template, and where it stands currently:
@@ -164,7 +164,7 @@ This is a really brief/condensed idea of what is planned for this template, and --- -## Why does this project exist?[![](./docs/_static/pin.svg)](#table-of-contents) +## Why does this project exist?[![](./docs/_static/pin.svg)](#why-does-this-project-exist) Unfortunately, the [Hypermodern Python Cookiecutter] is no longer maintained nor modern. While it will always have a place in my heart, there have been far too many improvements in Python tooling to keep using it as is. @@ -181,7 +181,7 @@ The goal is for [cookiecutter-robust-python] to fill the gap that exists for a b --- -## Contributing[![](./docs/_static/pin.svg)](#table-of-contents) +## Contributing[![](./docs/_static/pin.svg)](#contributing) For more information on contributing to the [Robust Python Cookiecutter], please visit the [contributing] docs. From b8cafe6e4de42ba7accb0367222bdedcd9baf800 Mon Sep 17 00:00:00 2001 From: Kyle Oliver Date: Mon, 8 Dec 2025 01:16:52 -0500 Subject: [PATCH 52/52] feat: improve the initial description and add a small blurb linking to the tooling rationale --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 51c1229..4a97062 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ **[cookiecutter-robust-python]** is a template made with the understanding that **project needs change over time**. -The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. +The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CICD** within a structure designed for **future adaptability**. Meaning that important decisions like whether to use [maturin] may be delayed **without breaking the docs, CICD, etc.** during transition. --- @@ -71,6 +71,9 @@ The **[Robust Python Cookiecutter]** aims to provide **best practice tooling/CIC - [pip-audit] for security vulnerability checking - [maturin] (optional) for rust integration when needed +> 💡 For more information on tools evaluated and why choices were made, please visit our docs on [tooling decisions] + +