1
1
#!/usr/bin/env python
2
2
3
3
from hatchling .version .scheme .plugin .interface import VersionSchemeInterface
4
+ from typing import Mapping
5
+ from semver import VersionInfo
6
+ from operator import ge , gt
7
+ from copy import deepcopy
8
+ from .bump_instruction import BumpInstruction
4
9
5
10
6
11
class SemverScheme (VersionSchemeInterface ):
@@ -12,33 +17,55 @@ class SemverScheme(VersionSchemeInterface):
12
17
"""
13
18
14
19
PLUGIN_NAME = "semver"
20
+ INSTRUCTION_SEPARATOR = ","
15
21
16
- def update (self , desired_version , original_version , version_data ) -> str :
17
- from semver import VersionInfo
18
-
19
- original = VersionInfo .parse (original_version )
20
- parts = desired_version .replace ("micro" , "patch" ).replace ("fix" , "patch" ).split ("," )
21
-
22
- for part in parts :
23
- if part in ("major" , "minor" , "patch" ):
24
- next_version = getattr (original , "bump_" + part )()
25
- original = next_version
26
- elif part in ("post" , "rev" , "r" ):
27
- raise ValeError (f"Semver has no concept of a post-release. Use 'build' instead" )
28
- elif part == "dev" :
29
- raise ValeError (f"Semver has no concept of a dev-release. Use 'build' instead" )
22
+ def update (self , desired_version : str , original_version : str , version_data : Mapping ) -> str :
23
+ if not desired_version :
24
+ return original_version
25
+ original_version = VersionInfo .parse (original_version )
26
+ current_version = deepcopy (original_version )
27
+ instructions : str = desired_version
28
+ validate = self .config .get ("validate-bump" , True )
29
+ for instruction in instructions .split (self .INSTRUCTION_SEPARATOR ):
30
+ bi = BumpInstruction (instruction )
31
+ last_bump_was_build = False
32
+ if bi .version_part == "build" :
33
+ current_version = current_version .bump_build (token = bi .token )
34
+ last_bump_was_build = True
35
+ elif bi .version_part == "release" :
36
+ current_version = current_version .finalize_version ()
37
+ elif bi .is_specific :
38
+ current_version = VersionInfo .parse (bi .version_part )
30
39
else :
31
- if len (parts ) > 1 :
32
- raise ValueError (
33
- "Cannot specify multiple update operations with an explicit version"
34
- )
40
+ current_version = current_version .next_version (
41
+ bi .version_part , prerelease_token = bi .token
42
+ )
43
+ if validate :
44
+ self .validate_bump (current_version , original_version , bumped_build = last_bump_was_build )
45
+ return current_version
35
46
36
- next_version = VersionInfo .parse (part )
37
- if self .config .get ("validate-bump" , True ) and next_version <= original :
38
- raise ValueError (
39
- f"Version `{ part } ` is not higher than the original version `{ original_version } `"
47
+ def validate_bump (
48
+ self , current_version : VersionInfo , original_version : VersionInfo , bumped_build : bool
49
+ ) -> None :
50
+ """
51
+ In Semver spec, all builds are equally ranked.
52
+ So for build bumps we validate only whether the current version is equal to the original one.
53
+ For other bumps we validate if the current version is higher than the original.
54
+ """
55
+ if bumped_build :
56
+ comparator = ge
57
+ relation = "at least as high as"
58
+ else :
59
+ comparator = gt
60
+ relation = "higher than"
61
+ if comparator (current_version , original_version ):
62
+ return
63
+ else :
64
+ raise ValueError (
65
+ " " .join (
66
+ (
67
+ f"Version `{ current_version } ` is not { relation } " ,
68
+ f"the original version `{ original_version } `" ,
40
69
)
41
- else :
42
- return str (next_version )
43
-
44
- return str (original )
70
+ )
71
+ )
0 commit comments