Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mike version sorting #171

Merged
merged 7 commits into from
Nov 3, 2023
Merged
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
6 changes: 3 additions & 3 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

## Summary

This release adds support for `pylint` 3, so downstream projects can upgrade their `pylint` version.
This release fixes a bug in `mike` version sorting.

## Upgrading

If upgrading `pylint` you might get a few new check errors.
- `frequenz.repo.config.mkdocs.mike.`: The `sort_versions()` function now takes plain `str`s as arguments instead of `MikeVersionInfo` objects.

### Cookiecutter template

There is no need to regenerate any templates with this release.

## Bug Fixes

- `mkdocs`: The `conftest` module is now properly hidden from the documentation again.
- CI / `mkdocs`: `mike` version sorting now properly sort pre-releases as older than stable releases for the same major and minor version.
31 changes: 9 additions & 22 deletions src/frequenz/repo/config/cli/version/mike/sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,35 @@
"""Sort `mike`'s `version.json` file with a custom order."""


import dataclasses
import json
import sys
from typing import TextIO
from typing import Any, TextIO

from .... import github
from ....mkdocs.mike import MikeVersionInfo, sort_mike_versions
from ....mkdocs.mike import sort_mike_versions


def _sort(stream_in: TextIO, stream_out: TextIO) -> None:
"""Sort the versions in the given in stream to the given out stream.

Args:
stream_in: The stream to read the versions from.
stream_out: The stream to write the sorted versions to.
"""
versions = json.load(stream_in)
sorted_versions = sort_mike_versions([MikeVersionInfo(**v) for v in versions])
json.dump(sorted_versions, stream_out, separators=(",", ":"))


def _load_and_sort_versions_from(stream: TextIO) -> list[MikeVersionInfo]:
"""Load the versions from the given stream.
def _load_and_sort_versions_from(stream: TextIO) -> dict[str, dict[str, Any]]:
"""Load the versions from the given stream and sort them.

Args:
stream: The stream to read the versions from.

Returns:
The loaded versions.
The sorted loaded versions.
"""
versions = [MikeVersionInfo(**v) for v in json.load(stream)]
return sort_mike_versions(versions)
versions = {v["version"]: v for v in json.load(stream)}
return {v: versions[v] for v in sort_mike_versions(list(versions.keys()))}


def _dump_versions_to(versions: list[MikeVersionInfo], stream: TextIO) -> None:
def _dump_versions_to(versions: dict[str, dict[str, Any]], stream: TextIO) -> None:
"""Dump the versions to the given stream.

Args:
versions: The versions to dump.
stream: The stream to write the versions to.
"""
json.dump([dataclasses.asdict(v) for v in versions], stream, separators=(",", ":"))
json.dump(list(versions.values()), stream, separators=(",", ":"))


def main() -> None:
Expand Down
29 changes: 12 additions & 17 deletions src/frequenz/repo/config/mkdocs/mike.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ def _to_fake_sortable_semver(version: str) -> str:

The following transformations are applied:

- `vX.Y-pre` -> `X.Y.0-pre`
- `vX.Y` -> `X.Y.0`
- `vX.Y-pre` -> `X.Y.99999`
- `vX.Y-dev` -> `X.Y.999999`

The idea is to convert the version string to a semver string that can be sorted
Expand All @@ -161,7 +161,7 @@ def _to_fake_sortable_semver(version: str) -> str:
The converted version string.
"""
version = _stable_to_semver_re.sub(r"\1.\2.0", version)
version = _pre_to_semver_re.sub(r"\1.\2.99999", version)
version = _pre_to_semver_re.sub(r"\1.\2.0-pre", version)
version = _dev_to_semver_re.sub(r"\1.\2.999999", version)
if version.startswith("v"):
version = version[1:]
Expand All @@ -175,17 +175,17 @@ def compare_mike_version(version1: str, version2: str) -> int:

- Versions are first compared by major version (`X`).
- If they have the same major, then they are compared by minor version (`Y`).
- If they have the same major and minor, then pre-releases (`vX.Y-pre`) are
considered bigger than stable versions (`vX.Y`) and development versions
- If they have the same major and minor, then stable versions (`vX.Y`) are
considered bigger than pre-releases (`vX.Y-pre`) and development versions
(`vX.Y-dev`) are considered bigger than pre-releases.
- Any other version not matching `vX.Y(-pre|-dev)?` is considered to be bigger than
the matching versions.
- Not matching versions are compared alphabetically.

Example:

`v1.0` < `v1.0-pre` < `v1.0-dev` < `v1.1` < `v2.0` < `v2.0-pre` < `v2.0-dev`
< `whatever` < `x`.
`v1.0-pre` < `v1.0` < `v1.0-dev` < `v1.1` < `v2.0-pre` < `v2.0` < `v2.0-dev`
< `whatever` < `x`.

Args:
version1: The first version to compare.
Expand All @@ -210,9 +210,7 @@ def compare_mike_version(version1: str, version2: str) -> int:
return -1 if version1 < version2 else 1


def sort_mike_versions(
versions: list[MikeVersionInfo], *, reverse: bool = True
) -> list[MikeVersionInfo]:
def sort_mike_versions(versions: list[str], *, reverse: bool = True) -> list[str]:
"""Sort `mike`'s `version.json` file with a custom order.

The `version` keys are expected as follows:
Expand All @@ -227,16 +225,17 @@ def sort_mike_versions(
- Versions are first sorted by major version (`X`).
- Inside a major version group, versions are sorted by minor version (`Y`).
- For the same major and minor version, development versions (`-dev`) considered
the latest for that major version group, then pre-release versions (`-pre`), and
finally stable versions.
the latest for that major version group, then stable versions, and finally
pre-release versions (`-pre`).
- Other versions appear first and are sorted alphabetically.

The versions are sorted in-place using
[`compare_mike_version()`][frequenz.repo.config.mkdocs.mike.compare_mike_version].

Example:

`z`, `whatever`, `v2.1-dev`, `v2.1-pre`, `v2.0`, `v1.1-dev`, `v1.0-dev`, `v1.0`
`z`, `whatever`, `v2.1-dev`, `v2.1`, `v2.1-pre`, `v2.0`, `v1.1-dev`, `v1.0-dev`,
`v1.0`

Args:
versions: The list of versions to sort.
Expand All @@ -245,9 +244,5 @@ def sort_mike_versions(
Returns:
The sorted list of versions.
"""

def compare(ver1: MikeVersionInfo, ver2: MikeVersionInfo) -> int:
return compare_mike_version(ver1.version, ver2.version)

versions.sort(key=functools.cmp_to_key(compare), reverse=reverse)
versions.sort(key=functools.cmp_to_key(compare_mike_version), reverse=reverse)
return versions
Loading