Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ jobs:

- name: Configure git
run: |
git config --global user.name "GH-actions-bot"
git config --global user.email "gh-actions-bot@noreply.github.com"
git config --global user.name "Tester"
git config --global user.email "test@example.com"

- uses: astral-sh/setup-uv@v7
- run: uvx --with tox-uv tox -e py,docs,style
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:

- name: Configure git
run: |
git config --global user.name "GH-actions-bot"
git config --global user.email "gh-actions-bot@noreply.github.com"
git config --global user.name "Tester"
git config --global user.email "test@example.com"

- uses: astral-sh/setup-uv@v7
- run: uvx --with tox-uv tox -e py
Expand All @@ -56,8 +56,8 @@ jobs:

- name: Configure git
run: |
git config --global user.name "GH-actions-bot"
git config --global user.email "gh-actions-bot@noreply.github.com"
git config --global user.name "Tester"
git config --global user.email "test@example.com"

- uses: astral-sh/setup-uv@v7
- run: uvx --with tox-uv tox -e py
Expand Down
99 changes: 99 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# AGENTS.md

## Purpose

This file defines repo-specific guidance for coding agents working on `setupmeta`.
Use it as the first source of truth for how to make changes safely and consistently.

## Project Summary

- `setupmeta` helps keep `setup.py` files minimal by auto-filling metadata and requirements.
- It provides custom setup commands: `explain`, `version`, and `check`.
- It supports git-tag-based versioning and automated version bumps.
- The project is self-hosted: `setup.py` uses `setupmeta` itself and has bootstrap behavior.

## Core Principles

- Preserve backward compatibility for users relying on legacy `setup.py` workflows.
- Keep runtime dependencies minimal; prefer stdlib for runtime code.
- Favor explicit, test-covered behavior over cleverness.
- Keep documentation aligned with actual behavior and test scenarios.

## Repository Layout

- `setupmeta/`: main library code (`commands`, `model`, `scm`, `versioning`, etc.)
- `tests/`: unit tests + scenario replay tests
- `tests/scenarios/` and `examples/`: behavior fixtures with `expected.txt` snapshots
- `docs/`: user and contributor documentation
- `setup.py`: self-bootstrapping package definition
- `tox.ini`: test/lint/docs/coverage orchestration

## Environment and Commands

Use these commands from repo root:

- Quick tests: `.venv/bin/pytest -q`
- Full test/lint/docs run: `tox`
- Single tox env: `tox -e py314`
- Fast compatibility matrix (old+new + coverage): `tox -e py39,py314,coverage`
- Style only: `tox -e style`
- Docs checks: `tox -e docs`
- Refresh scenario snapshots: `tox -e refreshscenarios`
- Manual command checks:
- `.venv/bin/python setup.py explain`
- `.venv/bin/python setup.py version`
- `.venv/bin/python setup.py check -q`

Notes:

- `tox.ini` pins `UV_CACHE_DIR` to `.tox/.uv-cache`, so no command-line prefix is needed.
- `py37` exists because it is the oldest Python still supported by this library.
- If `py37` is unavailable locally (common on macOS arm64), substitute the oldest available interpreter and pair it with the newest one.
Example: system Python `3.9` + `3.14` via `tox -e py39,py314,coverage`.
- Intent: keep local runs fast while still exercising one older runtime and one modern runtime, with `coverage combine` validating cross-env coverage data.

## Code Change Expectations

- Keep changes narrow and focused.
- Maintain Python compatibility targeted by this repo (including older supported versions).
- When changing behavior, update both tests and docs in the same change.
- Do not silently alter CLI output formats used by scenario snapshots unless intentional.
- If you touch versioning logic, verify:
- `tests/test_versioning.py`
- `tests/test_setup_py.py`
- scenario outputs that include `version`/`explain`.

## Scenario Snapshot Rules

- Scenario tests compare command output against `expected.txt`.
- If behavior changes are intentional:
1. Regenerate snapshots (`tox -e refreshscenarios`).
2. Review diffs in `tests/scenarios/*/expected.txt` and `examples/*/expected.txt`.
3. Ensure docs and release notes explain user-visible changes.
- If behavior changes are not intentional, fix code/tests instead of accepting snapshot churn.

## Documentation Rules

- Keep `README.rst`, `docs/*.rst`, and example READMEs consistent with current behavior.
- Avoid documenting deprecated/removed commands as active features.
- Keep versioning docs aligned with current strategy defaults and command examples.
- Record user-visible changes in `HISTORY.rst`.

## Safety and Review Checklist

Before finalizing a change, verify:

1. Tests pass for affected areas.
2. Formatting/lint checks pass.
3. Scenario snapshots are unchanged unless intentionally updated.
4. Docs are updated if behavior or commands changed.
5. No unrelated files were modified.

## When to Ask for Clarification

Ask the maintainer before proceeding if:

- A change could break backward compatibility.
- Expected output changes are broad/noisy and intent is unclear.
- A docs update conflicts with tested behavior.
- A refactor touches bootstrap or version bump internals in `setup.py`, `setupmeta/hook.py`, `setupmeta/versioning.py`, or `setupmeta/scm.py`.
14 changes: 12 additions & 2 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@
Release notes
=============

3.9.0 (2026-02-09)
3.9.0 (2026-02-11)
------------------

* Test with py3.14, publish sdist with py3.13

* Removed commands: ``entrypoints``, ``cleanall``

* Removed post-version-bump hook support (unused, unnecessary, and was not well documented)

* Internal project modernizations (use ``uv``, ``ruff``, enabled more linter rules, etc)
* Removed old ``register`` hook used with setuptools v50

* Removed support for pygradle-style versioning

* Internal project modernizations:

* Refactored usage of ``subprocess.run()``, removed last left overs from the py2 days

* use ``uv``, ``ruff``, enabled more linter rules, etc

* Use coveralls_ for test coverage reporting

Expand Down
18 changes: 6 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ See examples_ for more.

git describe --dirty --tags --long --first-parent --match 'v*.*'

# Then, if above yields nothing, we try the more vague '*.*'

git describe --dirty --tags --long --first-parent --match '*.*'

you will need **git version >= 1.8.4** if you wish to use ``setupmeta``'s versioning capabilities.


Expand All @@ -84,7 +80,7 @@ The goal of this project is to:
* Support tag-based versioning_ (like setuptools_scm_, but with super simple configuration/defaults and automated ``bump`` capability)

* Provide useful Commands_ to see the metadata (**explain**), **version** (including support for bumping versions),
**cleanall**, etc
and **check**


How it works?
Expand All @@ -102,12 +98,10 @@ How it works?

* ``entry_points`` is auto-filled from file ``entry_points.ini`` (bonus: tools like PyCharm have a nice syntax highlighter for those)

* ``install_requires`` is auto-filled if you have a ``requirements.txt`` (or ``pinned.txt``) file,
* ``install_requires`` is auto-filled from ``requirements.in`` (preferred), then ``requirements.txt``
(or ``pinned.txt`` for older projects),
pinning is abstracted away by default as per `community recommendation`_, see requirements_ for more info.

* ``tests_require`` is auto-filled if you have a ``tests/requirements.txt``, or ``requirements-dev.txt``,
or ``dev-requirements.txt``, or ``test-requirements.txt`` file

* ``description`` will be the 1st line of your README (unless that 1st line is too short, or is just the project's name),
or the 1st line of the first docstring found in the scanned files (see list below)

Expand All @@ -130,7 +124,7 @@ How it works?

* tag "v1.0.0", 5 commits since tag -> version is "1.0.5"

* if checkout is dirty, a marker is added -> version would be "1.0.5.post5.dirty"
* if checkout is dirty, a marker is added -> version would be "1.0.5+dirty"

* With ``versioning="post"``, your git tags will be of the form ``v{major}.{minor}.{patch}``,
a "post" addendum will be present if there are commits since latest version tag:
Expand All @@ -139,7 +133,7 @@ How it works?

* tag "v1.0.0", 5 commits since tag -> version is "1.0.0.post5"

* if checkout is dirty, a marker is added -> version would be "1.0.0.post5.dirty"
* if checkout is dirty, a marker is added -> version would be "1.0.0.post5+dirty"

* With ``versioning="build-id"``, your git tags will be of the form ``v{major}.{minor}.0``,
the number of commits since latest version tag will be used to auto-fill the "patch" part of the version:
Expand All @@ -154,7 +148,7 @@ How it works?

* if checkout is dirty, a marker is added -> version would be "1.0.5+hlocal.g456.dirty"

* Use the **bump** command (see commands_) to easily bump (ie: increment major, minor or patch + apply git tag)
* Use the **version** command (see commands_) to easily bump (ie: increment major, minor or patch + apply git tag)

* Version format can be customized, see versioning_ for more info

Expand Down
79 changes: 16 additions & 63 deletions docs/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,23 @@ For example, this is what setupmeta says about itself (it's self-using)::
author: (auto-adjust ) Zoran Simic
\_: (setupmeta/__init__.py:6) Zoran Simic zoran@simicweb.com
author_email: (auto-adjust ) zoran@simicweb.com
classifiers: (classifiers.txt ) 21 items: ["Development Status :: 5 - Production/Stable", "Intend...
bugtrack_url: (auto-fill ) https://github.com/codrsquad/setupmeta/issues
classifiers: (explicit ) 23 items: ["Development Status :: 5 - Production/Stable", "Intend...
description: (setupmeta/__init__.py:2) Simplify your setup.py
download_url: (auto-fill ) https://github.com/codrsquad/setupmeta/archive/v2.1.1.tar.gz
download_url: (auto-fill ) https://github.com/codrsquad/setupmeta/archive/v3.9.0.tar.gz
\_: (setupmeta/__init__.py:5) archive/v{version}.tar.gz
entry_points: (explicit ) [distutils.commands] check = setupmeta.commands:CheckCommand clea...
keywords: (setup.py:4 ) ["simple", "DRY", "setup.py"]
entry_points: (explicit ) [distutils.commands] check = setupmeta.commands:CheckCommand expla...
include_package_data: (MANIFEST.in ) True
install_requires: (explicit ) ["setuptools>=67"]
license: (auto-fill ) MIT
long_description: (README.rst ) Simplify your setup.py ====================== .. image:: https://...
long_description_content_type: (README.rst ) text/x-rst
name: (setup.py:16 ) setupmeta
packages: (auto-fill ) ["setupmeta"]
name: (explicit ) setupmeta
packages: (explicit ) ["setupmeta"]
python_requires: (explicit ) >=3.7
setup_requires: (explicit ) ["setupmeta"]
title*: (setup.py:16 ) setupmeta
url: (setupmeta/__init__.py:4) https://github.com/codrsquad/setupmeta
version: (git ) 2.1.1
version: (git ) 3.9.0
versioning: (explicit ) dev
zip_safe: (explicit ) True

Expand All @@ -48,9 +50,9 @@ In the above output:
had a value that came from 2 different sources, final value showing at top,
while all the other values seen showing below with the ``\_`` indicator.

* ``classifiers`` came from file ``classifiers.txt``
* ``classifiers`` came from explicit settings in ``setup.py``

* ``description`` came from ``setup.py`` line 2
* ``description`` came from ``setupmeta/__init__.py`` line 2

* ``download_url`` was defined in ``setupmeta/__init__.py`` line 5, since it was mentioning
``{version}`` (and was a relative path), it got auto-expanded and filled in properly
Expand All @@ -59,25 +61,12 @@ In the above output:

* ``long_description`` came from ``README.rst``

* ``name`` came from line 16 of setup.py, note that ``title`` also came from that line -
this simply means the constant ``__title__`` was used as ``name``
* ``name`` came from an explicit setting in setup.py

* Note that ``title*`` is shown with an asterisk, the asterisk means that setupmeta sees
the value and can use it, but doesn't transfer it to setuptools
* ``packages`` came from explicit settings in setup.py

* ``packages`` was auto-filled to ``["setupmeta"]``

* ``version`` was determined from git tag (due to ``versioning="post"`` in setup.py),
in this case ``1.1.2.post1+g816252c`` means:

* latest tag was 1.1.2

* there was 1 commit since that tag (``.post1`` means 1 change since tag,
``".post"`` denotes this would be a "post-release" version,
and should play nicely with PEP-440_)

* the ``+g816252c`` suffix means that the checkout wasn't clean when ``explain`` command
was ran, local checkout was dirty at short git commit id "816252c"
* ``version`` was determined from git (due to ``versioning="dev"`` in setup.py),
in this case ``3.9.0`` means current commit is exactly on a version tag


If you'd like to see what your ``setup.py`` would look like without setupmeta
Expand Down Expand Up @@ -106,40 +95,4 @@ Typical usage::
python setup.py version --b minor --commit # Effectively bump


cleanall
========

Handily clean build artifacts. Cleans the usual suspects:
``.cache/ .tox/ build/ dist/ venv/ __pycache__/ *.egg-info *.py[cod]``.

Example::

🦎 3.9 ~/dev/github/setupmeta: ./setup.py cleanall
running cleanall
deleted .tox
deleted setupmeta.egg-info
deleted examples/direct/__pycache__
deleted examples/hierarchical/__pycache__
deleted examples/single/__pycache__
deleted setupmeta/__pycache__
deleted tests/__pycache__
deleted tests/scenarios/complex/tests/__pycache__
deleted tests/scenarios/readmes/__pycache__
deleted 14 .pyc files


entrypoints
===========

This will simply show you your ``entry_points/console_scripts``.
Can be handy for pygradle_ users.

Example::

🦎 3.9 ~/github/pickley: python setup.py entrypoints

pickley = pickley.cli:protected_main

.. _PEP-440: https://www.python.org/dev/peps/pep-0440/

.. _pygradle: https://github.com/linkedin/pygradle/
2 changes: 1 addition & 1 deletion docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ A coverage report is generated on all ``tox`` runs

Run this to see the generated html report::

open .tox/coverage/index.html
open .tox/test-reports/htmlcov/index.html


Refreshing the test scenarios
Expand Down
12 changes: 5 additions & 7 deletions docs/versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ How does it work?

**Note**: ``setupmeta``'s versioning is based on (by default)::

git describe --dirty --tags --long --match *.* --first-parent
git describe --dirty --tags --long --first-parent --match 'v*.*'

you will need **git version >= 1.8.4** if you wish to use ``setupmeta``'s versioning capabilities.

Expand Down Expand Up @@ -144,7 +144,7 @@ Example:
Commit Tag Version Note (command ran to add tag)
======= ====== ================= ==============================================================
no .git 0.0.0 Version defaults to 0.0.0 (when no tag yet)
none 0.0.0.dirty No commit yet (but ``git init`` was ran)
none 0.0.0+dirty No commit yet (but ``git init`` was ran)
g1 0.0.0.post1 Initial commit
g1 0.0.0.post1+dirty Same as above, only checkout was not clean anymore
g2 0.0.0.post2
Expand Down Expand Up @@ -242,7 +242,7 @@ distance
This is well suited if you want to publish a new version at every commit (but don't want to keep
bumping version in code for every commit).

``distance`` corresponds to this format: ``branch(main,master):{major}.{minor}.{distance}{dirty}``
``distance`` corresponds to this format: ``branch(main,master):{major}.{minor}.{distance}+{dirty}``

State this in your ``setup.py``::

Expand Down Expand Up @@ -387,15 +387,13 @@ This is what ``versioning="post"`` is a shortcut for::
"main": "{major}.{minor}.{patch}{post}",
"extra": "{dirty}",
"branches": ["main"],
"version_tag": "*.*",
"version_tag": "v*.*",
},
...
)

``version_tag`` is the glob pattern of git tags to consider as version tags.
Unfortunately (for historical reasons), the default form is ``*.*`` (ie: any git tag
with a dot in it), and arguably should have been ``v*.*`` (ie: git tags that start with ``v``
and have dot in them...)
The default form is ``v*.*`` (ie: git tags that start with ``v`` and have a dot in them).

Ideally, git would allow to state a full regex, as only tags that match this regex
would ideally be considered as version tags: ``^v?\d+\.\d+(\.\d+)?$``, however this is not
Expand Down
Loading