Metadata-Version: 2.5
Name: manageprojects
Version: 0.29.0
Summary: Manage Python / Django projects
Project-URL: Documentation, https://codeberg.org/jedie/manageprojects
Project-URL: Source, https://codeberg.org/jedie/manageprojects
Author-email: Jens Diemer <mamageprojects@jensdiemer.de>
License: GPL-3.0-or-later
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: cli-base-utilities>=0.27.1
Requires-Dist: codespell
Requires-Dist: cookiecutter>=2.4.0
Requires-Dist: editorconfig
Requires-Dist: rich
Requires-Dist: ruff
Requires-Dist: tomlkit
Requires-Dist: ty
Requires-Dist: tyro
Description-Content-Type: text/markdown

# manageprojects - Manage Python / Django projects

[![manageprojects @ PyPi](https://img.shields.io/pypi/v/manageprojects?label=manageprojects%20%40%20PyPi)](https://pypi.org/project/manageprojects/)
[![Python Versions](https://img.shields.io/pypi/pyversions/manageprojects)](https://codeberg.org/jedie/manageprojects/src/branch/main/pyproject.toml)
[![License GPL-3.0-or-later](https://img.shields.io/pypi/l/manageprojects)](https://codeberg.org/jedie/manageprojects/src/branch/main/LICENSE)

Mix the idea of Ansible with CookieCutter Templates and Django Migrations to manage and update your Python Packages and Django Projects...

The main idea it to transfer changes of a CookieCutter template back to the created project.
Manageprojects used git to create a patch of the template changes and applies it to the created project.

Besides this, `manageprojects` also includes other generic helper for Python packages:

 * `publish_package()` - Build and upload a new release to PyPi, but with many pre-checks.
 * `format-file` - Format/Check a Python source file with Darker & Co., useful as IDE action.
 * `install_python.py` - [One file and no dependencies to install Python, if needed, from official Python FTP server, verified.](https://codeberg.org/jedie/manageprojects/src/branch/main/docs/install_python.md)
 * `setup_python.py` - [One file and no dependencies to download and setup redistributable Python, if needed.](https://codeberg.org/jedie/manageprojects/src/branch/main/docs/setup_python.md)

Read below the `Helper` section.

Documentation: [docs/](https://codeberg.org/jedie/manageprojects/src/branch/main/docs/) (WIP)


## GitHub suspended me!

**Important notice:**

----

GitHub _suspended_ [my account](https://github.com/jedie) sometime in late August 2026 !
There was **no** warning beforehand, nor **any explanation** of the reasons afterwards.
Since then, I have had **no access** or control over my data on GitHub.

All my projects and everything about me have completely disappeared.
**Everything** just results in a **404 "Not Found"** error page.

A support request still hasn't been answered, even after several weeks (apart from a confirmation that the request was received).

For that reason, I have been looking for a new home for all my OpenSource projects.
(Or rather, I am in the process of republishing all my projects elsewhere.)

Now you can find my on these place**s**:

* [**Codeberg**.org/jedie](https://codeberg.org/jedie)
* [**OpenCommit**.eu/jedie](https://opencommit.eu/jedie)
* [**GitLab**.com/users/jedie](https://gitlab.com/users/jedie/projects)
* [**Radicle**](https://radicle.network/install): [did:key:z6MkfvNUuBCwRtNZwANEAGd4vaTzyuZXMKfiSyjoBALga934](https://radicle.network/nodes/iris.radicle.network/users/did%3Akey%3Az6MkfvNUuBCwRtNZwANEAGd4vaTzyuZXMKfiSyjoBALga934) | [jedie.radicle.garden](https://radicle.network/nodes/jedie.radicle.garden)

That is why there will probably be a lot of broken links pointing to my GitHub account for quite some time!

----

## install

You can use [pipx](https://pipx.pypa.io/stable/installation/) to install and use PyHardLinkBackup, e.g.:

```bash
sudo apt install pipx

pipx install manageprojects
```
After this you can call the CLI via `manageprojects` command.

To upgrade an existing installation, use:
```bash
pipx upgrade PyHardLinkBackup
```
see: https://pipx.pypa.io/stable/docs/#pipx-upgrade



The output of `manageprojects --help` looks like:

[comment]: <> (✂✂✂ auto generated main help start ✂✂✂)
```
usage: manageprojects [-h] {clone-project,format-file,reverse,shell-completion,start-project,update-project,version,wiggle}



╭─ options ──────────────────────────────────────────────────────────────────────────────╮
│ -h, --help            show this help message and exit                                  │
╰────────────────────────────────────────────────────────────────────────────────────────╯
╭─ subcommands ──────────────────────────────────────────────────────────────────────────╮
│ (required)                                                                             │
│   • clone-project     Clone existing project by replay the cookiecutter template in a  │
│                       new directory.                                                   │
│                                                                                        │
│                       e.g.:                                                            │
│                                                                                        │
│                       manageprojects clone-project ~/foo/bar ~/cloned/                 │
│   • format-file       Format and check the given python source code file with ruff,    │
│                       codespell and mypy.                                              │
│                                                                                        │
│                       If the given file is a directory, all python files that are      │
│                       tracked as changed by git will be formatted.                     │
│                                                                                        │
│                       The optional fallback values will be only used, if we can't get  │
│                       them from the project meta files like ".editorconfig" and        │
│                       "pyproject.toml"                                                 │
│   • reverse           Create a cookiecutter template from a managed project.           │
│                                                                                        │
│                       e.g.:                                                            │
│                                                                                        │
│                       manageprojects reverse ~/my_managed_project/                     │
│                       ~/my_new_cookiecutter_template/                                  │
│   • shell-completion  Setup shell completion for this CLI (Currently only for bash     │
│                       shell)                                                           │
│   • start-project     Start a new "managed" project via a CookieCutter Template. Note: │
│                       The CookieCutter Template *must* be use git!                     │
│                                                                                        │
│                       e.g.:                                                            │
│                                                                                        │
│                       manageprojects start-project                                     │
│                       git+https://codeberg.org/jedie/cookiecutter_templates/           │
│                       --directory uv-python ~/foobar/                                  │
│   • update-project    Update a existing project.                                       │
│                                                                                        │
│                       e.g. update by overwrite (and merge changes manually via git):   │
│                                                                                        │
│                       manageprojects update-project ~/foo/bar/                         │
│   • version           Print version and exit                                           │
│   • wiggle            Run wiggle to merge *.rej in given directory.                    │
│                       https://github.com/neilbrown/wiggle                              │
│                                                                                        │
│                       e.g.:                                                            │
│                                                                                        │
│                       manageprojects wiggle ~/my_managed_project/                      │
╰────────────────────────────────────────────────────────────────────────────────────────╯
```
[comment]: <> (✂✂✂ auto generated main help end ✂✂✂)

### most important commands

#### start-project

Help from `manageprojects start-project --help` Looks like:

[comment]: <> (✂✂✂ auto generated start-project help start ✂✂✂)
```
usage: manageprojects start-project [-h] [START-PROJECT OPTIONS] STR PATH

Start a new "managed" project via a CookieCutter Template. Note: The CookieCutter Template *must* be use git!

e.g.:

manageprojects start-project git+https://codeberg.org/jedie/cookiecutter_templates/ --directory uv-python ~/foobar/

╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
│ STR              The name of the CookieCutter Template. (required)                     │
│ PATH             Target path for the new project. Must not exist yet! (required)       │
╰────────────────────────────────────────────────────────────────────────────────────────╯
╭─ options ──────────────────────────────────────────────────────────────────────────────╮
│ -h, --help       show this help message and exit                                       │
│ -v, --verbosity  Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable)               │
│ --directory {None}|STR                                                                 │
│                  Cookiecutter Option: Directory within repo that holds                 │
│                  cookiecutter.json file for advanced repositories with multi templates │
│                  in it (default: None)                                                 │
│ --replay, --no-replay                                                                  │
│                  Cookiecutter Option: Do not prompt for parameters and only use        │
│                  information entered previously (default: False)                       │
│ --input, --no-input                                                                    │
│                  Cookiecutter Option: Do not prompt for parameters and only use        │
│                  cookiecutter.json file content (default: False)                       │
│ --checkout {None}|STR                                                                  │
│                  Cookiecutter Option: Optional branch, tag or commit ID to checkout    │
│                  after clone (default: None)                                           │
│ --password {None}|STR                                                                  │
│                  Cookiecutter Option: Password to use when extracting the repository   │
│                  (default: None)                                                       │
│ --config-file {None}|PATH                                                              │
│                  Cookiecutter Option: Optional path to "cookiecutter_config.yaml"      │
│                  (default: None)                                                       │
╰────────────────────────────────────────────────────────────────────────────────────────╯
```
[comment]: <> (✂✂✂ auto generated start-project help end ✂✂✂)


#### update-project

Help from `manageprojects update-project --help` Looks like:

[comment]: <> (✂✂✂ auto generated update-project help start ✂✂✂)
```
usage: manageprojects update-project [-h] [UPDATE-PROJECT OPTIONS] PATH

Update a existing project.

e.g. update by overwrite (and merge changes manually via git):

manageprojects update-project ~/foo/bar/

╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
│ PATH             project-path (required)                                               │
╰────────────────────────────────────────────────────────────────────────────────────────╯
╭─ options ──────────────────────────────────────────────────────────────────────────────╮
│ -h, --help       show this help message and exit                                       │
│ -v, --verbosity  Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable)               │
│ --overwrite, --no-overwrite                                                            │
│                  Overwrite all Cookiecutter template files to the last template state  │
│                  and do not apply the changes via git patches. The developer is        │
│                  supposed to apply the differences manually via git. Will be aborted   │
│                  if the project git repro is not in a clean state. (default: True)     │
│ --cleanup, --no-cleanup                                                                │
│                  Cleanup created temporary files (default: True)                       │
│ --input, --no-input                                                                    │
│                  Cookiecutter Option: Do not prompt for parameters and only use        │
│                  cookiecutter.json file content (default: False)                       │
│ --password {None}|STR                                                                  │
│                  Cookiecutter Option: Password to use when extracting the repository   │
│                  (default: None)                                                       │
│ --config-file {None}|PATH                                                              │
│                  Cookiecutter Option: Optional path to "cookiecutter_config.yaml"      │
│                  (default: None)                                                       │
╰────────────────────────────────────────────────────────────────────────────────────────╯
```
[comment]: <> (✂✂✂ auto generated update-project help end ✂✂✂)



## workflow

### 1. Create a new project

Use `start-project` command and a github url as Cookiecutter template, e.g.:

```bash
~/manageprojects$ manageprojects start-project https://codeberg.org/jedie/cookiecutter_templates/ --directory piptools-python ~/my_new_project/
~/manageprojects$ cd ~/my_new_project/your_cool_package/
~/my_new_project/your_cool_package/$ git init
~/my_new_project/your_cool_package/$ git add .
~/my_new_project/your_cool_package/$ git commit --message "my cool new project"
```

Note: https://codeberg.org/jedie/cookiecutter_templates is a multi template repository, the `piptools-python` template is here: https://codeberg.org/jedie/cookiecutter_templates/tree/main/piptools-python

After running the `start-project` command, look into the created files.
Manage projects stores all needed meta information about the used Cookiecutter template into `pyproject.toml`, e.g.:
```bash
~/manageprojects$ cat ~/my_new_project/your_cool_package/pyproject.toml
...
[manageprojects] # https://codeberg.org/jedie/manageprojects
initial_revision = "6e4c875"
initial_date = 2022-11-10T12:37:20+01:00
cookiecutter_template = "https://codeberg.org/jedie/cookiecutter_templates/"
cookiecutter_directory = "piptools-python"

[manageprojects.cookiecutter_context.cookiecutter]
...
```

### 2. Update existing project

If the source Cookiecutter changed, then you can apply these changes to your created project, e.g.:

```bash
~/manageprojects$ manageprojects update-project ~/my_new_project/your_cool_package/
```

After this, manageproject will update the own meta information in `pyproject.toml` by add `applied_migrations` with the information about the current Cookiecutter version, e.g.:
```bash
~/manageprojects$ cat ~/my_new_project/your_cool_package/pyproject.toml
...
[manageprojects] # https://codeberg.org/jedie/manageprojects
initial_revision = "6e4c875"
initial_date = 2022-11-10T12:37:20+01:00
cookiecutter_template = "https://codeberg.org/jedie/cookiecutter_templates/"
cookiecutter_directory = "piptools-python"
applied_migrations = [
    "dd69dcf", # 2022-11-22T19:48:28+01:00
]
...
```

## How?

Everything is based on git ;)

* manageprojects knows the git hash of the used Cookiecutter Template at creation time and the current git hash.
* It builds a git patch between these two commits.
* This patch will be applied to the created project sources.

So theoretically the changes in the template are applied to the project.

However, this does not work in every case, because git can't match the changes.

See below:

## drawbacks

One problem is that git can't apply all changes.

But `git apply` is used with `--reject`.
It applies the parts of the patch that are applicable,
and leave the rejected hunks in corresponding `*.rej` files.

There is a cool tool, called `wiggle`: https://github.com/neilbrown/wiggle

It tries to apply rejected patches by perform word-wise diffs.

Just run `wiggle` via manageproject CLI, e.g.:

```bash
~/manageprojects$ manageprojects wiggle ~/my_new_project/your_cool_package/
```


#### Update by overwrite

A alternative way to update a project:

1. Just overwrite all files with the current Cookiecutter template output
2. Merge changes manually via `git`

So you doesn't have trouble with not applicable git patches ;)

Just add `--overwrite`, e.g.:
```bash
~/manageprojects$ manageprojects update-project --overwrite ~/my_new_project/your_cool_package/
```


## Helper

Below are some generic tools helpful for Python packages.


### install_python.py

A script to download, build and install a Python interpreter, but only if the system Python is not the required major version.
It's a standalone script with no dependencies. Works with Python 3.9+.

More information: [docs/install_python.md](https://codeberg.org/jedie/manageprojects/src/branch/main/docs/install_python.md)


### "reverse" - Reverse a project into a Cookiecutter template

A existing managed project can be converted back to a Cookiecutter template, e.g.:
```bash
~/manageprojects$ manageprojects reverse ~/my_new_project/ ~/cookiecutter_template/
```


### "format-file" - Format and check the given python source code file

You can use `format-file` as "Action on save" or manual action in your IDE to fix code style ;)

[comment]: <> (✂✂✂ auto generated format-file help start ✂✂✂)
```
usage: manageprojects format-file [-h] PATH [-v] [--py-version STR] [--max-line-length INT] [--max-distance INT]

Format and check the given python source code file with ruff, codespell and mypy.

If the given file is a directory, all python files that are tracked as changed by git will be formatted.

The optional fallback values will be only used, if we can't get them from the project meta files like ".editorconfig" and "pyproject.toml"

╭─ positional arguments ─────────────────────────────────────────────────────────────────╮
│ PATH                   file-path (required)                                            │
╰────────────────────────────────────────────────────────────────────────────────────────╯
╭─ options ──────────────────────────────────────────────────────────────────────────────╮
│ -h, --help             show this help message and exit                                 │
│ -v, --verbosity        Verbosity level; e.g.: -v, -vv, -vvv, etc. (repeatable)         │
│ --py-version STR       Fallback Python version for darker/pyupgrade, if version is not │
│                        defined in pyproject.toml (default: 3.10)                       │
│ --max-line-length INT  Fallback max. line length for darker/isort etc., if not defined │
│                        in .editorconfig (default: 119)                                 │
│ --max-distance INT     If we only format the changed lines: The maximum number of      │
│                        lines between two chunks that can be merged. (default: 1)       │
╰────────────────────────────────────────────────────────────────────────────────────────╯
```
[comment]: <> (✂✂✂ auto generated format-file help end ✂✂✂)


### publish

The `manageprojects.utilities.publish.publish_package()` is designed for external packages, too.

Build and upload (with twine) a project to PyPi with many pre-checks:

 * Has correct version number?
 * Is on main branch and up-to-date with origin?
 * Check if current version already published
 * Build a git tag based on current package version
 * Adds change messages since last release to git tag message

Some checks result in a hard exit, but some can be manually confirmed from the user to continue publishing.


## start development

At least `uv` is needed. Install e.g.: via pipx:
```bash
apt-get install pipx
pipx install uv
```

Clone the project and just start the CLI help commands.
A virtual environment will be created/updated automatically.

```bash
~$ git clone https://codeberg.org/jedie/manageprojects.git
~$ cd manageprojects
~/manageprojects$ manageprojects --help
```

For developing manageprojects, there is the `dev-cli.py` with all development related commands.

The output of `./dev-cli.py --help` looks like:

[comment]: <> (✂✂✂ auto generated dev help start ✂✂✂)
```
usage: ./dev-cli.py [-h] {coverage,git-hooks,install,lint,mypy,nox,pip-audit,publish,run-git-hooks,shell-completion,test,update,update-readme-history,update-test-snapshot-files,version}



╭─ options ──────────────────────────────────────────────────────────────────────────────╮
│ -h, --help     show this help message and exit                                         │
╰────────────────────────────────────────────────────────────────────────────────────────╯
╭─ subcommands ──────────────────────────────────────────────────────────────────────────╮
│ (required)                                                                             │
│   • coverage   Run tests and show coverage report.                                     │
│   • git-hooks  Setup our "pre-commit" git hooks                                        │
│   • install    Install requirements and 'manageprojects' via pip as editable.          │
│   • lint       Check/fix code style by run: "ruff check --fix"                         │
│   • mypy       Run Mypy (configured in pyproject.toml)                                 │
│   • nox        Run nox                                                                 │
│   • pip-audit  Run pip-audit check against current requirements files                  │
│   • publish    Build and upload this project to PyPi                                   │
│   • run-git-hooks                                                                      │
│                Run the installed "pre-commit" git hooks                                │
│   • shell-completion                                                                   │
│                Setup shell completion for this CLI (Currently only for bash shell)     │
│   • test       Run unittests                                                           │
│   • update     Update dependencies (uv.lock) and git pre-commit hooks                  │
│   • update-readme-history                                                              │
│                Update project history base on git commits/tags in README.md            │
│                                                                                        │
│                Will be exited with 1 if the README.md was updated otherwise with 0.    │
│                                                                                        │
│                Also, callable via e.g.:                                                │
│                    python -m cli_base update-readme-history -v                         │
│   • update-test-snapshot-files                                                         │
│                Update all test snapshot files (by remove and recreate all snapshot     │
│                files)                                                                  │
│   • version    Print version and exit                                                  │
╰────────────────────────────────────────────────────────────────────────────────────────╯
```
[comment]: <> (✂✂✂ auto generated dev help end ✂✂✂)

## development git hooks

To install the git hooks, run:

```bash
~/manageprojects$ ./dev-cli.py git-hooks
```


## History

See also git tags: https://codeberg.org/jedie/manageprojects/tags

[comment]: <> (✂✂✂ auto generated history start ✂✂✂)



[comment]: <> (✂✂✂ auto generated history end ✂✂✂)

## Links

* Own Cookiecutter Templates: https://codeberg.org/jedie/cookiecutter_templates
* https://github.com/cookiecutter/cookiecutter
* Available Cookiecutters template on GitHub: https://github.com/search?q=cookiecutter&type=Repositories
* Packaging Python Projects: https://packaging.python.org/en/latest/tutorials/packaging-projects/
