Skip to content
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
8 changes: 3 additions & 5 deletions cfbs/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@
import copy

from cfbs.internal_file_management import fetch_archive
from cfbs.masterfiles.analyze import (
highest_version,
sort_versions,
version_as_comparable_list,
)
from cfbs.utils import (
CFBSNetworkError,
cfbs_dir,
deduplicate_list,
fetch_url,
file_sha256,
get_json,
highest_version,
immediate_subdirectories,
mkdir,
read_json,
sort_versions,
version_as_comparable_list,
CFBSExitError,
)

Expand Down
2 changes: 1 addition & 1 deletion cfbs/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def search_command(terms: List[str]):

from cfbs.cfbs_json import CFBSJson
from cfbs.cfbs_types import CFBSCommandExitCode, CFBSCommandGitResult
from cfbs.masterfiles.analyze import most_relevant_version
from cfbs.masterfiles.download import download_single_version
from cfbs.updates import ModuleUpdates, update_module
from cfbs.utils import (
Expand All @@ -72,6 +71,7 @@ def search_command(terms: List[str]):
file_diff_text,
is_cfbs_repo,
mkdir,
most_relevant_version,
read_json,
CFBSExitError,
save_file,
Expand Down
99 changes: 1 addition & 98 deletions cfbs/masterfiles/analyze.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from collections import OrderedDict
import os
from typing import Iterable, List

from cfbs.utils import dict_sorted_by_key, file_sha256
from cfbs.utils import dict_sorted_by_key, file_sha256, version_as_comparable_list

Version = str

Expand Down Expand Up @@ -92,99 +91,3 @@ def finalize_vcf(versions_dict, checksums_dict, files_dict):
)

return versions_dict, checksums_dict, files_dict


def version_as_comparable_list(version: str):
"""Also supports versions containing exactly one of `b` or `-`.

Example of the version ordering: `3.24.0b1 < 3.24.0 < 3.24.0-1`.

Examples:
* `version_as_comparable_list("3.24.0b1")` is `[[3, 24, 0], [-1, 1]]`
* `version_as_comparable_list("3.24.0-2")` is `[[3, 24, 0], [1, 2]]`
* `version_as_comparable_list("3.24.x")` is `[[3, 24, 99999], [0, 0]]`"""
if version == "master":
version = "x"

if "b" not in version:
if "-" not in version:
version += "|0.0"
version = version.replace("x", "99999").replace("-", "|1.").replace("b", "|-1.")
versionpair = version.split("|")
versions_str = [versionpair[0].split("."), versionpair[1].split(".")]

versions_int = [
[int(s) for s in versions_str[0]],
[int(s) for s in versions_str[1]],
]

return versions_int


def version_as_comparable_list_negated(version):
vcl = version_as_comparable_list(version)

vcl[0] = [-x for x in vcl[0]]
vcl[1] = [-x for x in vcl[1]]

return vcl


def version_is_at_least(version, min_version):
return min_version is None or (
version_as_comparable_list(version) >= version_as_comparable_list(min_version)
)


def sort_versions(versions: list, reverse: bool = True):
"""Sorts a list of versions, in descending order by default."""
versions.sort(
key=version_as_comparable_list,
reverse=reverse,
)


def highest_version(versions: Iterable[Version]):
return max(versions, key=version_as_comparable_list, default=None)


def lowest_version(versions: Iterable[Version]):
return min(versions, key=version_as_comparable_list, default=None)


def is_lower_version(version_a: Version, version_b: Version):
"""Returns `True` if and only if `version_a` is lower than `version_b`."""

return version_as_comparable_list(version_a) < version_as_comparable_list(version_b)


def most_relevant_version(
other_versions: List[Version], reference_version: Version
) -> Version:
"""
The most relevant version is the highest version among older other versions,
or if there are no older other versions, the lowest version among other versions.

`other_versions` is assumed to be non-empty and not contain `reference_version`."""
assert len(other_versions) > 0
assert reference_version not in other_versions

highest_other_version = highest_version(other_versions)
lowest_other_version = lowest_version(other_versions)
# Unfortunately, Pyright can not infer that these can't be `None` when `other_versions` is non-empty.
assert highest_other_version is not None
assert lowest_other_version is not None

if is_lower_version(highest_other_version, reference_version):
# all other versions are older
return highest_other_version
if is_lower_version(reference_version, lowest_other_version):
# all other versions are newer
return lowest_other_version
# there are both older and newer versions
lower_other_versions = [
o_v for o_v in other_versions if is_lower_version(o_v, reference_version)
]
highest_lower = highest_version(lower_other_versions)
assert highest_lower is not None
return highest_lower
9 changes: 7 additions & 2 deletions cfbs/masterfiles/check_download_matches_git.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import os
from collections import OrderedDict

from cfbs.masterfiles.analyze import version_as_comparable_list
from cfbs.utils import dict_diff, read_json, CFBSExitError, write_json
from cfbs.utils import (
dict_diff,
read_json,
CFBSExitError,
write_json,
version_as_comparable_list,
)


def check_download_matches_git(versions):
Expand Down
10 changes: 8 additions & 2 deletions cfbs/masterfiles/download.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import os
import shutil

from cfbs.masterfiles.analyze import version_is_at_least
from cfbs.utils import CFBSNetworkError, fetch_url, get_json, mkdir, CFBSExitError
from cfbs.utils import (
CFBSNetworkError,
fetch_url,
get_json,
mkdir,
CFBSExitError,
version_is_at_least,
)

ENTERPRISE_RELEASES_URL = "https://cfengine.com/release-data/enterprise/releases.json"

Expand Down
3 changes: 1 addition & 2 deletions cfbs/masterfiles/generate_release_information.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from cfbs.masterfiles.analyze import version_is_at_least
from cfbs.masterfiles.download import download_all_versions
from cfbs.masterfiles.generate_vcf_download import generate_vcf_download
from cfbs.masterfiles.generate_vcf_git_checkout import generate_vcf_git_checkout
from cfbs.masterfiles.check_download_matches_git import check_download_matches_git
from cfbs.utils import immediate_subdirectories
from cfbs.utils import immediate_subdirectories, version_is_at_least

DOWNLOAD_PATH = "downloaded_masterfiles"

Expand Down
99 changes: 99 additions & 0 deletions cfbs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,102 @@ def loads_bundlenames(policy: str):

regex = r"(?<=^bundle agent )[a-zA-Z0-9_\200-\377]+"
return re.findall(regex, policy, re.MULTILINE)


Version = str


def version_as_comparable_list(version: str):
"""Also supports versions containing exactly one of `b` or `-`.

Example of the version ordering: `3.24.0b1 < 3.24.0 < 3.24.0-1`.

Examples:
* `version_as_comparable_list("3.24.0b1")` is `[[3, 24, 0], [-1, 1]]`
* `version_as_comparable_list("3.24.0-2")` is `[[3, 24, 0], [1, 2]]`
* `version_as_comparable_list("3.24.x")` is `[[3, 24, 99999], [0, 0]]`"""
if version == "master":
version = "x"

if "b" not in version:
if "-" not in version:
version += "|0.0"
version = version.replace("x", "99999").replace("-", "|1.").replace("b", "|-1.")
versionpair = version.split("|")
versions_str = [versionpair[0].split("."), versionpair[1].split(".")]

versions_int = [
[int(s) for s in versions_str[0]],
[int(s) for s in versions_str[1]],
]

return versions_int


def version_as_comparable_list_negated(version):
vcl = version_as_comparable_list(version)

vcl[0] = [-x for x in vcl[0]]
vcl[1] = [-x for x in vcl[1]]

return vcl


def version_is_at_least(version, min_version):
return min_version is None or (
version_as_comparable_list(version) >= version_as_comparable_list(min_version)
)


def sort_versions(versions: list, reverse: bool = True):
"""Sorts a list of versions, in descending order by default."""
versions.sort(
key=version_as_comparable_list,
reverse=reverse,
)


def highest_version(versions: Iterable[Version]):
return max(versions, key=version_as_comparable_list, default=None)


def lowest_version(versions: Iterable[Version]):
return min(versions, key=version_as_comparable_list, default=None)


def is_lower_version(version_a: Version, version_b: Version):
"""Returns `True` if and only if `version_a` is lower than `version_b`."""

return version_as_comparable_list(version_a) < version_as_comparable_list(version_b)


def most_relevant_version(
other_versions: List[Version], reference_version: Version
) -> Version:
"""
The most relevant version is the highest version among older other versions,
or if there are no older other versions, the lowest version among other versions.

`other_versions` is assumed to be non-empty and not contain `reference_version`."""
assert len(other_versions) > 0
assert reference_version not in other_versions

highest_other_version = highest_version(other_versions)
lowest_other_version = lowest_version(other_versions)
# Unfortunately, Pyright can not infer that these can't be `None` when `other_versions` is non-empty.
assert highest_other_version is not None
assert lowest_other_version is not None

if is_lower_version(highest_other_version, reference_version):
# all other versions are older
return highest_other_version
if is_lower_version(reference_version, lowest_other_version):
# all other versions are newer
return lowest_other_version
# there are both older and newer versions
lower_other_versions = [
o_v for o_v in other_versions if is_lower_version(o_v, reference_version)
]
highest_lower = highest_version(lower_other_versions)
assert highest_lower is not None
return highest_lower