Skip to content

Commit

Permalink
Fixed major-minor and major-only versions.
Browse files Browse the repository at this point in the history
  • Loading branch information
vd2org committed Mar 3, 2024
1 parent 620b889 commit f9e0b3f
Showing 1 changed file with 52 additions and 21 deletions.
73 changes: 52 additions & 21 deletions src/ubump/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import sys
from argparse import ArgumentParser
from contextlib import suppress
from dataclasses import dataclass, asdict, replace
from enum import StrEnum, auto
from string import Template
from typing import Optional, NamedTuple, Self
from typing import Optional, Self

import tomlkit
from packaging.version import parse
Expand All @@ -20,6 +21,8 @@
NAME = "ubump"
VERSION = "v0.1.10"

DEFAULT_TEMPLATE = "${major}.${minor}.${patch}"

logging.basicConfig(level=logging.INFO, format="%(levelname)s - %(message)s")
logger = logging.getLogger(NAME)
logger.setLevel(logging.DEBUG if sys.flags.debug else logging.INFO)
Expand Down Expand Up @@ -50,27 +53,41 @@ class Action(StrEnum):
set = auto()


class Version(NamedTuple):
@dataclass(frozen=True)
class Version:
major: int
minor: int
patch: int

@classmethod
def from_str(cls, version: str):
v = cls(*parse(version).release)
if v.to_str() != version:
raise ValueError(f"Invalid version: {version}")
if v.to_raw_str() != version:
raise ConfigError(f"Invalid version: {version} != {v.to_raw_str()}!")
return v

def to_str(self, template: str = "${major}.${minor}.${patch}"):
return Template(template).substitute(self._asdict())
def to_str(self, template: str = DEFAULT_TEMPLATE):
return Template(template).substitute(asdict(self))

def to_raw_str(self):
match (self.major, self.minor, self.patch):
case (major, None, None):
template = f"{major}"
case (major, minor, None):
template = f"{major}.{minor}"
case (major, minor, patch):
template = f"{major}.{minor}.{patch}"
case _:
raise RuntimeError(f"Unknown version configuration: {self}")

return Template(template).substitute(asdict(self))


class Config:
def __init__(
self,
version: Version,
template: Optional[str],
template: str,
message: Optional[str] = None,
tag: Optional[bool] = None,
files: Optional[list[str]] = None
Expand All @@ -80,6 +97,14 @@ def __init__(
self._files = files or []
self._tag = tag or True
self._message = message or "Bump to ${version}"
self._validate()

def _validate(self, new_version: Optional[Version] = None):
version = new_version or self.version
template_ids = set(Template(self.template).get_identifiers())
version_keys = set(k for k, v in asdict(version).items() if v is not None)
if template_ids != version_keys:
raise ConfigError(f"Version {version.to_raw_str()} isn't suitable for template {self.template}!")

@property
def version(self) -> Version:
Expand All @@ -91,6 +116,7 @@ def str_version(self) -> str:

@version.setter
def version(self, value: Version):
self._validate(value)
self._version = value

@property
Expand Down Expand Up @@ -121,16 +147,16 @@ def save(self, mode: ConfigMode):
"files": self.files
}

with open(mode, "r+") as file:
with open(mode, "a+") as file:
content = tomlkit.load(file)

if mode is ConfigMode.ubump:
ubump["version"] = self.version.to_str()
ubump["version"] = self.version.to_raw_str()
content["ubump"] = ubump
else:
if "project" not in content:
content["project"] = {}
content["project"]["version"] = self.version.to_str()
content["project"]["version"] = self.version.to_raw_str()
if "tool" not in content:
content["tool"] = {}
content["tool"]["ubump"] = ubump
Expand Down Expand Up @@ -271,7 +297,7 @@ def tag(cls, tag: str, message: str) -> bool:

class Actions:
@staticmethod
def init(version: str, template: Optional[str], no_pyproject: bool = False, dry: bool = False):
def init(version: str, template: str, no_pyproject: bool = False, dry: bool = False):
logger.info(f"Initializing ubump config...")

with suppress(ConfigNotFoundError):
Expand All @@ -289,13 +315,10 @@ def init(version: str, template: Optional[str], no_pyproject: bool = False, dry:
if not mode:
mode = ConfigMode.ubump

config = Config(
version=Version.from_str(version),
template=template or "v${major}.${minor}.${patch}"
)

logger.info(f"Using {mode} as config file...")

config = Config(version=Version.from_str(version), template=template)

logger.info(f"Searching for files contains current version {config.str_version}...")
config.files = Tools.walk(config)

Expand All @@ -320,14 +343,22 @@ def bump(action: Action, version: Optional[str] = None, dry: bool = False):

match action:
case Action.set:
logger.info(f"Setting version to {version} from {old_str_version}...")
ver = Version.from_str(version)
logger.info(f"Setting version to {ver.to_str(config.template)} from {old_str_version}...")
config.version = Version.from_str(version)
case Action.major:
config.version = config.version._replace(major=config.version.major + 1, minor=0, patch=0)
config.version = replace(config.version, major=config.version.major + 1,
minor=0 if config.version.minor is not None else None,
patch=0 if config.version.patch is not None else None)
case Action.minor:
config.version = config.version._replace(minor=config.version.minor + 1, patch=0)
if config.version.minor is None:
raise ConfigError(f"Can't bump minor version for {config.version.to_raw_str()}!")
config.version = replace(config.version, minor=config.version.minor + 1,
patch=0 if config.version.patch is not None else None)
case Action.patch:
config.version = config.version._replace(patch=config.version.patch + 1)
if config.version.patch is None:
raise ConfigError(f"Can't bump patch version for {config.version.to_raw_str()}!")
config.version = replace(config.version, patch=config.version.patch + 1)
case _:
raise RuntimeError(f"Unknown action: {action}")

Expand Down Expand Up @@ -365,7 +396,7 @@ def main():
init.add_argument("--no-pyproject", default=False, action="store_true",
help=f"Don't use {ConfigMode.pyproject}, use {ConfigMode.ubump} instead.")
init.add_argument("version", help="Current version.")
init.add_argument("-t", "--template", default="v${major}.${minor}.${patch}",
init.add_argument("-t", "--template", default=f"v{DEFAULT_TEMPLATE}",
help="The version template.")

major = subs.add_parser(Action.major, help="Bump major version.")
Expand Down

0 comments on commit f9e0b3f

Please sign in to comment.