Metadata-Version: 2.4
Name: plone.versioncheck
Version: 2.0.0
Summary: Checks pinned versions with overrides in a cascaded buildout
Author-email: "Jens W. Klein" <jens@bluedynamics.com>
License: GPL version 2
Project-URL: Source, https://github.com/plone/plone.versioncheck
Project-URL: Issues, https://github.com/plone/plone.versioncheck/issues
Keywords: plone,buildout,version
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Buildout :: Extension
Classifier: Framework :: Buildout :: Recipe
Classifier: Framework :: Buildout
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: colorama
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: jinja2
Requires-Dist: packaging
Requires-Dist: zc.buildout
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pytest-mock; extra == "test"
Requires-Dist: pytest-remove-stale-bytecode; extra == "test"
Requires-Dist: pytest-html; extra == "test"
Requires-Dist: respx; extra == "test"
Provides-Extra: typecheck
Requires-Dist: pyright; extra == "typecheck"
Provides-Extra: develop
Requires-Dist: pdbpp; extra == "develop"
Requires-Dist: isort; extra == "develop"
Provides-Extra: docs
Requires-Dist: Sphinx; extra == "docs"

# plone.versioncheck

[![Tests](https://github.com/plone/plone.versioncheck/actions/workflows/tests.yaml/badge.svg)](https://github.com/plone/plone.versioncheck/actions/workflows/tests.yaml)
[![Coverage](https://codecov.io/gh/plone/plone.versioncheck/branch/master/graph/badge.svg)](https://codecov.io/gh/plone/plone.versioncheck)
[![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/)

Checks pinned versions with overrides in a cascaded buildout

**Requirements:** Python 3.10 or later

## Features

### 1. Checks buildouts `[versions]` sections while stepping through the cascaded `extends`

- Command line script collects the inherited version pins, remembers where a version pin comes from
- It displays the result in order to enable a human to check that pins and overrides are OK
- Output is colored; this helps to identify packages which have newer versions available
- Machine readable output as JSON on demand

### 2. Checks Python Package Index (PyPI) for newer versions

- Detects if a newer major, minor or bugfix (or a prerelease) is available
- Uses async HTTP requests with concurrent processing for **10-50x faster** PyPI checks
- Configurable concurrency limit (default: 20 concurrent requests)

### 3. Buildout extension records the current versions state and requirements

- Versions state and requirements are written to a file
- Versions from the file will be consumed by the command line tool
  - Orphaned version pins are detected
  - It shows which package pulled in another package as dependency

It works best with [semantically](http://semver.org/) and only with [syntactically](https://setuptools.readthedocs.io/en/latest/setuptools.html#specifying-your-project-s-version) correct version numbers!

## Usage

### Install with your buildout

Add a section to install it as a script and add it as an extension to your buildout:

```ini
[buildout]
...
extensions =
    plone.versioncheck

parts =
    ...
    ploneversioncheck
    ...

...

[ploneversioncheck]
recipe = zc.recipe.egg
eggs = plone.versioncheck

...
```

Run buildout as usual.

Now a file `.plone.versioncheck.tracked.json` was generated in the buildout-directory.

This file will be used by `bin/versioncheck` to figure out which packages were finally used.

Run buildout again to regenerate this file.

### Command line

```
usage: versioncheck [-h] [-p] [-n] [-N] [-r] [-d] [-i] [-e EXCLUDE_CFG] [-m]
                    [--no-cache] [-b] [-o [OUTPUT]] [--no-colors]
                    [--debug-limit DEBUG_LIMIT]
                    [buildout]

Fetch information about pinned versions and its overrides in simple and complex/cascaded buildouts.

positional arguments:
  buildout              path to buildout.cfg or other *.cfg file

optional arguments:
  -h, --help            show this help message and exit
  -p, --pypi            check PyPI for newer versions
  -n, --newer           display only packages with newer version than active
  -N, --newer-orphaned  display orphaned packages only when newer versions
                        available
  -r, --required-by     show information about requirements (only if tracking
                        file is available)
  -d, --show-release-dates
                        show information about release dates (only for package
                        lookup from PyPI)
  -i, --ignore-tracking
                        ignore tracking file (if present)
  -e EXCLUDE_CFG, --exclude-cfg EXCLUDE_CFG
                        exclude in listing when cfg-filename pattern matches
                        (fnmatch) the given expression
  -m, --machine         show as machine readable output (json)
  --no-cache            do not use a cache for PyPI
  -b, --browser         show as html for webbrowser
  -o [OUTPUT], --output [OUTPUT]
                        safe output to output-file
  --no-colors           do not show colors
  --debug-limit DEBUG_LIMIT
                        Limit the number of PyPI versions fetched for
                        debugging

States and color codes:
  [A]ctive (white)
  [D]evelop (green)
  [O]rphaned (magenta)
  [I]nherited (older or same versions are gray, newer are yellow)
  [U]pdate of final release on PyPI available (cyan)
  [P]rerelease update on PyPI available (blue)
  [X] unpinned (red)
  [r] Requirement (gray)
  [a] Annotation (gray)

Color of package name helps to indicate overall state of a package.
```

### Files created

If the extension was used, a file `.plone.versioncheck.tracked.json` will be created.
It contains the information from last buildout run, including version states and dependency tree.

**Note:** HTTP caching is currently disabled. The performance gains from async concurrent requests (10-50x speedup) far outweigh the caching benefits. Caching may be re-enabled in a future release.

## Output explained

### Legend of states and colors

**[D]evelopment Egg**
- A development egg is usually active
- Description shows location
- Color: Green

**[A]ctive Pin**
- Pinned version. Package is used and recent, all seems fine
- Color: White

**[I]nherited Pin**
- Unused pin. If older than active, the pin color is gray; if newer, it is yellow

**[O]rphaned**
- If tracked, it shows whether the package in the given configuration was used at all
- Be careful with this information!
- I.e. in a development buildout file, other packages are used than in a live or continuous integration buildout!
- Color: Magenta

**[X] Unpinned**
- Tracked, but no pin in `[versions]` sections were found
- Color: Red

**[U]pdate final release**
- At PyPI there is a newer final version available (major, minor or bugfix)
- Descriptions shows on which level
- Color: Cyan

**[P]rerelease update**
- At PyPI there is a newer prerelease version available (major, minor or bugfix)
- Descriptions shows on which level
- Only if there is no final release update available
- Color: Blue

**[r] Required by**
- If tracked and option `--required-by` was given, show packages this package is required by
- Valid for current active/used version
- Keep in mind this is based on the declared requirements, missing or implicit requirements are not covered

**[a] Annotation**
- It is possible to annotate the reason why a version was chosen
- The information is parsed out of a section `[versionannotations]`
- In this section key is the name of the package and value the text annotations to be displayed
- The value can span more than one line using indent (as usual)

### Order of versions

Order of versions is the buildout resolution order (how they are resolved by buildout in the extends chain/tree).
After that, the PyPI releases are shown (major, minor, pre, then the prereleases)

Example, given in each a version of `my.pkg` was declared:

1. `buildout.cfg` with `my.pkg=3.0.3`
   1. `buildout.cfg` extends `foo.cfg` with `my.pkg=3.0.1`
   2. `buildout.cfg` extends `bar.cfg` with `my.pkg=2.0`
      - `foo.cfg` extends `baz.cfg` with `my.pkg=3.1`

2. found a newer versions at pypi
   1. major `my.pkg=4.0`
   2. minor `my.pkg=3.2`
   3. major prerelease `my.pkg=5.1b2`

Output looks like so:

```
my.pkg
    3.0.3............... A buildout.cfg
    2.0 ................ I bar.cfg
    3.0.1 .............. I foo.cfg
    3.1 ................ I baz.cfg
    4.0 ................ U Major
    3.2 ................ U Minor
    5.1b2............... P Majorpre
```

### Example

Here w/o colors, run on `buildout.coredev`:

```
$ ./bin/versioncheck -p buildout.cfg

accesscontrol
    3.0.12 .... A versions.cfg
    2.13.13 ... I http://dist.plone.org/versions/zope-2-13-23-versions.cfg
acquisition
    4.2.2 ..... A versions.cfg
    2.13.9 .... I http://dist.plone.org/versions/zope-2-13-23-versions.cfg
alabaster
    0.7.7 ..... X unpinned
archetypes.multilingual
    3.0.1 ..... A versions.cfg
archetypes.referencebrowserwidget
    2.5.6 ..... A versions.cfg
archetypes.schemaextender
    2.1.5 ..... A versions.cfg
argcomplete
    1.0.0 ..... A tests.cfg
argh
    0.26.1 .... A tests.cfg
argparse
    (unset) ... A versions.cfg
    1.1 ....... I http://dist.plone.org/versions/zopetoolkit-1-0-8-ztk-versions.cfg
    Can not check legacy version number.  U Error
autopep8
    1.2.1 ..... A tests.cfg

[... skipped a bunch ...]

coverage
    3.7.1 ..... A tests.cfg
    3.5.2 ..... I http://dist.plone.org/versions/zopetoolkit-1-0-8-ztk-versions.cfg
    4.0.3 ..... U Major
    4.1b1 ..... P Majorpre
cssmin
    0.2.0 ..... A versions.cfg
cssselect
    0.9.1 ..... A versions.cfg
datetime
    3.0.3 ..... A versions.cfg
    2.12.8 .... I http://dist.plone.org/versions/zope-2-13-23-versions.cfg
    4.0.1 ..... U Major
decorator
    4.0.6 ..... A versions.cfg

[... skipped a bunch ...]

plone.app.textfield
    1.2.6 ..... A versions.cfg
plone.app.theming
    1.2.17.dev0  D /home/workspacejensens/coredev5/src/plone.app.theming/src
    1.2.16 .... I versions.cfg
plone.app.tiles
    2.1.0 ..... A versions.cfg
    2.2.0 ..... U Minor

[... skipped a bunch ...]
```

## Source Code and Contributions

If you want to help with the development (improvement, update, bug-fixing, ...) of `plone.versioncheck` this is a great idea!

Please follow the [contribution guidelines](http://docs.plone.org/develop/coredev/docs/guidelines.html).

- [Source code at Github](https://github.com/plone/plone.versioncheck)
- [Issue tracker at Github](https://github.com/plone/plone.versioncheck/issues)

Maintainer of `plone.versioncheck` is Jens Klein and the Plone contributors.
We appreciate any contribution and if a release is needed to be done on PyPI, please just contact one of us (best by opening an issue).

## Development

### Requirements

- Python 3.10 or later
- [uv](https://github.com/astral-sh/uv) package manager

### Setup

Clone the project and set up the development environment:

```bash
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh

# Create virtual environment
uv venv --python 3.10

# Activate virtual environment
source .venv/bin/activate  # Linux/macOS
# or: .venv\Scripts\activate  # Windows

# Install in development mode with all extras
uv pip install -e ".[test,typecheck,develop]"

# Install pre-commit hooks
pre-commit install
```

### Running Tests

```bash
# Run all tests with coverage
pytest

# Run tests for specific module
pytest tests/test_pypi.py

# Run linting and type checking
pre-commit run --all-files
```

### Code Quality

The project uses:
- **ruff** for linting and formatting
- **isort** for import sorting (Plone profile)
- **pyright** for type checking
- **pre-commit** for automated quality checks
- **[pre-commit.ci](https://pre-commit.ci)** for automated PR checks

Test coverage requirement: 77% minimum (currently ~78%)

Pre-commit hooks will automatically fix issues when possible. On pull requests, pre-commit.ci will run all checks and auto-commit fixes.

## License

The project is licensed under the GPLv2.
