247 lines
8.2 KiB
Python
Executable File
247 lines
8.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Helper script to bump the current version."""
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
|
|
from packaging.version import Version
|
|
|
|
from homeassistant import const
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
|
|
def _bump_release(release, bump_type):
|
|
"""Bump a release tuple consisting of 3 numbers."""
|
|
major, minor, patch = release
|
|
|
|
if bump_type == "patch":
|
|
patch += 1
|
|
elif bump_type == "minor":
|
|
minor += 1
|
|
patch = 0
|
|
|
|
return major, minor, patch
|
|
|
|
|
|
def bump_version(
|
|
version: Version, bump_type: str, *, nightly_version: str | None = None
|
|
) -> Version:
|
|
"""Return a new version given a current version and action."""
|
|
to_change = {}
|
|
|
|
if bump_type == "minor":
|
|
# Convert 0.67.3 to 0.68.0
|
|
# Convert 0.67.3.b5 to 0.68.0
|
|
# Convert 0.67.3.dev0 to 0.68.0
|
|
# Convert 0.67.0.b5 to 0.67.0
|
|
# Convert 0.67.0.dev0 to 0.67.0
|
|
to_change["dev"] = None
|
|
to_change["pre"] = None
|
|
|
|
if not version.is_prerelease or version.release[2] != 0:
|
|
to_change["release"] = _bump_release(version.release, "minor")
|
|
|
|
elif bump_type == "patch":
|
|
# Convert 0.67.3 to 0.67.4
|
|
# Convert 0.67.3.b5 to 0.67.3
|
|
# Convert 0.67.3.dev0 to 0.67.3
|
|
to_change["dev"] = None
|
|
to_change["pre"] = None
|
|
|
|
if not version.is_prerelease:
|
|
to_change["release"] = _bump_release(version.release, "patch")
|
|
|
|
elif bump_type == "dev":
|
|
# Convert 0.67.3 to 0.67.4.dev0
|
|
# Convert 0.67.3.b5 to 0.67.4.dev0
|
|
# Convert 0.67.3.dev0 to 0.67.3.dev1
|
|
if version.is_devrelease:
|
|
to_change["dev"] = ("dev", version.dev + 1)
|
|
else:
|
|
to_change["pre"] = ("dev", 0)
|
|
to_change["release"] = _bump_release(version.release, "minor")
|
|
|
|
elif bump_type == "beta":
|
|
# Convert 0.67.5 to 0.67.6b0
|
|
# Convert 0.67.0.dev0 to 0.67.0b0
|
|
# Convert 0.67.5.b4 to 0.67.5b5
|
|
|
|
if version.is_devrelease:
|
|
to_change["dev"] = None
|
|
to_change["pre"] = ("b", 0)
|
|
|
|
elif version.is_prerelease:
|
|
if version.pre[0] == "a":
|
|
to_change["pre"] = ("b", 0)
|
|
if version.pre[0] == "b":
|
|
to_change["pre"] = ("b", version.pre[1] + 1)
|
|
else:
|
|
to_change["pre"] = ("b", 0)
|
|
to_change["release"] = _bump_release(version.release, "patch")
|
|
|
|
else:
|
|
to_change["release"] = _bump_release(version.release, "patch")
|
|
to_change["pre"] = ("b", 0)
|
|
|
|
elif bump_type == "nightly":
|
|
# Convert 0.70.0d0 to 0.70.0d201904241254, fails when run on non dev release
|
|
if not version.is_devrelease:
|
|
raise ValueError("Can only be run on dev release")
|
|
|
|
new_dev = dt_util.utcnow().strftime("%Y%m%d%H%M")
|
|
if nightly_version:
|
|
new_version = Version(nightly_version)
|
|
if new_version.release != version.release:
|
|
raise ValueError("Nightly version must have the same release version")
|
|
if not new_version.is_devrelease:
|
|
raise ValueError("Nightly version must be a dev version")
|
|
new_dev = new_version.dev
|
|
|
|
to_change["dev"] = ("dev", new_dev)
|
|
|
|
else:
|
|
raise ValueError(f"Unsupported type: {bump_type}")
|
|
|
|
temp = Version("0")
|
|
temp._version = version._version._replace(**to_change) # noqa: SLF001
|
|
return Version(str(temp))
|
|
|
|
|
|
def write_version(version):
|
|
"""Update Home Assistant constant file with new version."""
|
|
content = Path("homeassistant/const.py").read_text()
|
|
|
|
major, minor, patch = str(version).split(".", 2)
|
|
|
|
content = re.sub(
|
|
"MAJOR_VERSION: Final = .*\n", f"MAJOR_VERSION: Final = {major}\n", content
|
|
)
|
|
content = re.sub(
|
|
"MINOR_VERSION: Final = .*\n", f"MINOR_VERSION: Final = {minor}\n", content
|
|
)
|
|
content = re.sub(
|
|
"PATCH_VERSION: Final = .*\n", f'PATCH_VERSION: Final = "{patch}"\n', content
|
|
)
|
|
|
|
Path("homeassistant/const.py").write_text(content)
|
|
|
|
|
|
def write_version_metadata(version: Version) -> None:
|
|
"""Update pyproject.toml file with new version."""
|
|
content = Path("pyproject.toml").read_text(encoding="utf8")
|
|
|
|
content = re.sub(r"(version\W+=\W).+\n", f'\\g<1>"{version}"\n', content, count=1)
|
|
|
|
Path("pyproject.toml").write_text(content, encoding="utf8")
|
|
|
|
|
|
def write_ci_workflow(version: Version) -> None:
|
|
"""Update ci workflow with new version."""
|
|
content = Path(".github/workflows/ci.yaml").read_text()
|
|
|
|
short_version = ".".join(str(version).split(".", maxsplit=2)[:2])
|
|
content = re.sub(
|
|
r"(\n\W+HA_SHORT_VERSION: )\"\d{4}\.\d{1,2}\"\n",
|
|
f'\\g<1>"{short_version}"\n',
|
|
content,
|
|
count=1,
|
|
)
|
|
|
|
Path(".github/workflows/ci.yaml").write_text(content)
|
|
|
|
|
|
def main() -> None:
|
|
"""Execute script."""
|
|
parser = argparse.ArgumentParser(description="Bump version of Home Assistant")
|
|
parser.add_argument(
|
|
"type",
|
|
help="The type of the bump the version to.",
|
|
choices=["beta", "dev", "patch", "minor", "nightly"],
|
|
)
|
|
parser.add_argument(
|
|
"--commit", action="store_true", help="Create a version bump commit."
|
|
)
|
|
parser.add_argument(
|
|
"--set-nightly-version", help="Set the nightly version to", type=str
|
|
)
|
|
|
|
arguments = parser.parse_args()
|
|
|
|
if arguments.set_nightly_version and arguments.type != "nightly":
|
|
parser.error("--set-nightly-version requires type set to nightly.")
|
|
|
|
if (
|
|
arguments.commit
|
|
and subprocess.run(["git", "diff", "--quiet"], check=False).returncode == 1
|
|
):
|
|
print("Cannot use --commit because git is dirty.")
|
|
return
|
|
|
|
current = Version(const.__version__)
|
|
bumped = bump_version(
|
|
current, arguments.type, nightly_version=arguments.set_nightly_version
|
|
)
|
|
assert bumped > current, "BUG! New version is not newer than old version"
|
|
|
|
write_version(bumped)
|
|
write_version_metadata(bumped)
|
|
write_ci_workflow(bumped)
|
|
print(bumped)
|
|
|
|
if not arguments.commit:
|
|
return
|
|
|
|
subprocess.run(["git", "commit", "-nam", f"Bump version to {bumped}"], check=True)
|
|
|
|
|
|
def test_bump_version() -> None:
|
|
"""Make sure it all works."""
|
|
import pytest
|
|
|
|
assert bump_version(Version("0.56.0"), "beta") == Version("0.56.1b0")
|
|
assert bump_version(Version("0.56.0b3"), "beta") == Version("0.56.0b4")
|
|
assert bump_version(Version("0.56.0.dev0"), "beta") == Version("0.56.0b0")
|
|
|
|
assert bump_version(Version("0.56.3"), "dev") == Version("0.57.0.dev0")
|
|
assert bump_version(Version("0.56.0b3"), "dev") == Version("0.57.0.dev0")
|
|
assert bump_version(Version("0.56.0.dev0"), "dev") == Version("0.56.0.dev1")
|
|
|
|
assert bump_version(Version("0.56.3"), "patch") == Version("0.56.4")
|
|
assert bump_version(Version("0.56.3.b3"), "patch") == Version("0.56.3")
|
|
assert bump_version(Version("0.56.0.dev0"), "patch") == Version("0.56.0")
|
|
|
|
assert bump_version(Version("0.56.0"), "minor") == Version("0.57.0")
|
|
assert bump_version(Version("0.56.3"), "minor") == Version("0.57.0")
|
|
assert bump_version(Version("0.56.0.b3"), "minor") == Version("0.56.0")
|
|
assert bump_version(Version("0.56.3.b3"), "minor") == Version("0.57.0")
|
|
assert bump_version(Version("0.56.0.dev0"), "minor") == Version("0.56.0")
|
|
assert bump_version(Version("0.56.2.dev0"), "minor") == Version("0.57.0")
|
|
|
|
now = dt_util.utcnow().strftime("%Y%m%d%H%M")
|
|
assert bump_version(Version("0.56.0.dev0"), "nightly") == Version(
|
|
f"0.56.0.dev{now}"
|
|
)
|
|
assert bump_version(
|
|
Version("2024.4.0.dev20240327"),
|
|
"nightly",
|
|
nightly_version="2024.4.0.dev202403271315",
|
|
) == Version("2024.4.0.dev202403271315")
|
|
with pytest.raises(ValueError, match="Can only be run on dev release"):
|
|
bump_version(Version("0.56.0"), "nightly")
|
|
with pytest.raises(
|
|
ValueError, match="Nightly version must have the same release version"
|
|
):
|
|
bump_version(
|
|
Version("0.56.0.dev0"),
|
|
"nightly",
|
|
nightly_version="2024.4.0.dev202403271315",
|
|
)
|
|
with pytest.raises(ValueError, match="Nightly version must be a dev version"):
|
|
bump_version(Version("0.56.0.dev0"), "nightly", nightly_version="0.56.0")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|