| import os |
| import re |
| from pathlib import Path |
|
|
| try: |
| import tomllib |
| except ImportError: |
| import tomli as tomllib |
|
|
| from packaging.version import Version |
| from setuptools import setup |
| from setuptools_git_versioning import count_since, get_branch, get_sha, get_tags |
|
|
| REPO_ROOT = Path(__file__).parent |
| LAST_RELEASE_VERSION = Version("0.7.0") |
| TAG_VERSION_PATTERN = re.compile(r"^v(\d+\.\d+\.\d+)$") |
|
|
|
|
| def get_last_version_diff() -> tuple[Version, str | None, int | None]: |
| """ |
| Get the last version, last tag, and the number of commits since the last tag. |
| If no tags are found, return the last release version and None for the tag/commits. |
| |
| :returns: A tuple containing the last version, last tag, and number of commits since |
| the last tag. |
| """ |
| tagged_versions = [ |
| (Version(match.group(1)), tag) |
| for tag in get_tags(root=REPO_ROOT) |
| if (match := TAG_VERSION_PATTERN.match(tag)) |
| ] |
| tagged_versions.sort(key=lambda tv: tv[0]) |
| last_version, last_tag = ( |
| tagged_versions[-1] if tagged_versions else (LAST_RELEASE_VERSION, None) |
| ) |
| commits_since_last = ( |
| count_since(last_tag + "^{commit}", root=REPO_ROOT) if last_tag else None |
| ) |
|
|
| return last_version, last_tag, commits_since_last |
|
|
|
|
| def get_next_version( |
| build_type: str, build_iteration: str | int | None |
| ) -> tuple[Version, str | None, int]: |
| """ |
| Get the next version based on the build type and iteration. |
| - build_type == release: take the last version and add a post if build iteration |
| - build_type == nightly: increment to next minor, add 'a' with build iteration |
| - build_type == alpha: increment to next minor, add 'a' with build iteration |
| - build_type == dev: increment to next minor, add 'dev' with build iteration |
| |
| :param build_type: The type of build (release, candidate, nightly, alpha, dev). |
| :param build_iteration: The build iteration number. If None, defaults to the number |
| of commits since the last tag or 0 if no commits since the last tag. |
| :returns: A tuple containing the next version, the last tag the version is based |
| off of (if any), and the final build iteration used. |
| """ |
| version, tag, commits_since_last = get_last_version_diff() |
|
|
| if not build_iteration and build_iteration != 0: |
| build_iteration = commits_since_last or 0 |
| elif isinstance(build_iteration, str): |
| build_iteration = int(build_iteration) |
|
|
| |
| version = max(version, LAST_RELEASE_VERSION) |
|
|
| if build_type == "release": |
| |
| |
| |
| |
| |
| |
| return version, tag, 0 |
|
|
| |
| version = Version(f"{version.major}.{version.minor + 1}.0") |
|
|
| if build_type in ["nightly", "alpha"]: |
| |
| version = Version(f"{version}.a{build_iteration}") |
| else: |
| |
| version = Version(f"{version}.dev{build_iteration}") |
|
|
| return version, tag, build_iteration |
|
|
|
|
| def read_existing_version(version_py: Path) -> tuple[Version, str | None, int]: |
| if version_py.exists(): |
| text = version_py.read_text() |
|
|
| match_version = re.search(r'^version\s*=\s*["\']([^"\']+)["\']', text, re.M) |
| match_tag = re.search(r'^git_last_tag\s*=\s*["\']([^"\']*)["\']', text, re.M) |
| match_iteration = re.search( |
| r'^build_iteration\s*=\s*["\']([^"\']+)["\']', text, re.M |
| ) |
|
|
| version = Version(match_version.group(1)) if match_version else None |
| tag = match_tag.group(1) if match_tag and match_tag.group(1) else None |
| build_iteration = int(match_iteration.group(1)) if match_iteration else 0 |
|
|
| return version, tag, build_iteration |
|
|
|
|
| def building_from_sdist() -> bool: |
| |
| return REPO_ROOT.name.startswith("speculators-") |
|
|
|
|
| def write_version_files() -> tuple[Path, Path]: |
| """ |
| Write the version information to version.txt and version.py files. |
| version.txt contains the version string. |
| version.py contains the version plus additional metadata. |
| |
| :returns: A tuple containing the paths to the version.txt and version.py files. |
| """ |
| build_type = os.getenv("SPECULATORS_BUILD_TYPE", "dev").lower() |
| module_path = REPO_ROOT / "src" / "speculators" |
| version_txt_path = module_path / "version.txt" |
| version_py_path = module_path / "version.py" |
|
|
| if building_from_sdist() and version_py_path.exists(): |
| version, tag, build_iteration = read_existing_version(version_py_path) |
| else: |
| version, tag, build_iteration = get_next_version( |
| build_type=build_type, |
| build_iteration=os.getenv("SPECULATORS_BUILD_ITERATION"), |
| ) |
|
|
| git_commit = get_sha(root=REPO_ROOT) if (not building_from_sdist()) else "" |
| git_branch = get_branch(root=REPO_ROOT) if (not building_from_sdist()) else "" |
|
|
| with version_txt_path.open("w") as file: |
| file.write(str(version)) |
|
|
| with version_py_path.open("w") as file: |
| file.writelines( |
| [ |
| f'version = "{version}"\n', |
| f'build_type = "{build_type}"\n', |
| f'build_iteration = "{build_iteration}"\n', |
| f'git_commit = "{git_commit}"\n', |
| f'git_branch = "{git_branch}"\n', |
| f'git_last_tag = "{tag or ""}"\n', |
| ] |
| ) |
|
|
| return version_txt_path, version_py_path |
|
|
|
|
| def get_hs_connectors_requirement() -> str: |
| build_type = os.getenv("SPECULATORS_BUILD_TYPE", "dev").lower() |
| version_py_path = REPO_ROOT / "src" / "speculators" / "version.py" |
|
|
| if building_from_sdist() and version_py_path.exists(): |
| version, tag, build_iteration = read_existing_version(version_py_path) |
| else: |
| version, tag, build_iteration = get_next_version( |
| build_type=build_type, |
| build_iteration=os.getenv("SPECULATORS_BUILD_ITERATION"), |
| ) |
|
|
| if build_type == "dev": |
| |
| local = (REPO_ROOT / "hs_connectors").resolve() |
| return f"hs-connectors @ file://{local.as_posix()}" |
| elif build_type == "release": |
| |
| return f"hs-connectors=={version}" |
| else: |
| |
| return f"hs-connectors>{LAST_RELEASE_VERSION},<={version}" |
|
|
|
|
| def get_base_dependencies() -> list[str]: |
| """ |
| Read the static base dependency list from pyproject.toml's |
| [tool.speculators.dependencies].base table, so it stays hand-edited TOML |
| rather than a Python literal duplicated here. |
| """ |
| with (REPO_ROOT / "pyproject.toml").open("rb") as file: |
| data = tomllib.load(file) |
|
|
| return data["tool"]["speculators"]["dependencies"]["base"] |
|
|
|
|
| setup( |
| |
| install_requires=get_base_dependencies() + [get_hs_connectors_requirement()], |
| setuptools_git_versioning={ |
| "enabled": True, |
| "version_file": str(write_version_files()[0]), |
| }, |
| ) |
|
|