PyVCC 1.3.0__tar.gz → 1.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyvcc-1.3.2/PKG-INFO ADDED
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.1
2
+ Name: PyVCC
3
+ Version: 1.3.2
4
+ Summary: PyVCC python version controller using conventional commits
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: pygit2==1.19.2
7
+ Requires-Dist: structlog>=25.3.0
8
+ Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
9
+ Description-Content-Type: text/markdown
10
+
11
+ # PyVCC - Python Version Controller
12
+
13
+ **Automatic semantic versioning based on conventional commits**
14
+
15
+ PyVCC analyzes your Git repository's commit history and automatically determines the appropriate semantic version based on [Conventional Commits](https://www.conventionalcommits.org/) specification.
16
+
17
+ [![codecov](https://codecov.io/gh/lcavalcante/python-version-controller/graph/badge.svg?token=H65NZV7N3Z)](https://codecov.io/gh/lcavalcante/python-version-controller)
18
+ [![tests](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml/badge.svg)](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml)
19
+
20
+ ## Features
21
+
22
+ - ✅ Automatic semantic version calculation (MAJOR.MINOR.PATCH)
23
+ - ✅ Conventional Commits specification support
24
+ - ✅ Breaking change detection (BREAKING CHANGE: footer or ! suffix)
25
+ - ✅ Customizable starting version and commit
26
+ - ✅ Fast Git repository analysis using pygit2
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install pyvcc
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Basic Usage
37
+
38
+ ```bash
39
+ # Run from your Git repository root
40
+ pyvcc
41
+ ```
42
+
43
+ ### Options
44
+
45
+ ```bash
46
+ # Start from a specific version
47
+ pyvcc --initial-version 1.0.0
48
+
49
+ # Start analysis from a specific commit
50
+ pyvcc --initial-commit abc1234
51
+
52
+ # Verbose output
53
+ pyvcc --verbose
54
+
55
+ # Silent mode (errors only)
56
+ pyvcc --silent
57
+ ```
58
+
59
+ ### Environment Variables
60
+
61
+ ```bash
62
+ # Set repository root
63
+ PYVC_REPO_ROOT=/path/to/repo pyvcc
64
+
65
+ # Set initial version
66
+ PYVC_INITIAL_VERSION=2.0.0 pyvcc
67
+
68
+ # Set initial commit
69
+ PYVC_INITIAL_COMMIT=abc1234 pyvcc
70
+ ```
71
+
72
+ ## How It Works
73
+
74
+ PyVCC analyzes each commit message in your Git history and applies semantic versioning rules:
75
+
76
+ - **MAJOR**: Incremented for breaking changes (commit type with ! or BREAKING CHANGE: footer)
77
+ - **MINOR**: Incremented for new features (feat: commits)
78
+ - **PATCH**: Incremented for bug fixes (fix: commits)
79
+ - **NO BUMP**: Other commit types (chore, docs, style, refactor, test, ci, build, perf)
80
+
81
+ ### Commit Message Examples
82
+
83
+ ```markdown
84
+ # Major version bump (breaking change)
85
+ feat!: Redesign API endpoint structure
86
+
87
+ # OR
88
+ feat: Migrate to new database schema
89
+
90
+ BREAKING CHANGE: Database schema has changed and requires migration
91
+
92
+ # Minor version bump (new feature)
93
+ feat: Add new user authentication API
94
+
95
+ # Patch version bump (bug fix)
96
+ fix: Resolve login error on Safari
97
+
98
+ # No version bump
99
+ chore: Update dependencies
100
+ docs: Update README installation instructions
101
+ style: Format code according to new style guide
102
+ ```
103
+
104
+ ## Requirements
105
+
106
+ - Python 3.10+
107
+ - Git repository with commit history
108
+ - Commit messages following Conventional Commits specification
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ # Install development dependencies
114
+ pip install -e .[dev]
115
+
116
+ # Run tests
117
+ pytest
118
+
119
+ # Run with coverage
120
+ pytest --cov=pyvcc
121
+ ```
122
+
123
+ ## License
124
+
125
+ MIT License - See [LICENSE](LICENSE) for details.
pyvcc-1.3.2/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # PyVCC - Python Version Controller
2
+
3
+ **Automatic semantic versioning based on conventional commits**
4
+
5
+ PyVCC analyzes your Git repository's commit history and automatically determines the appropriate semantic version based on [Conventional Commits](https://www.conventionalcommits.org/) specification.
6
+
7
+ [![codecov](https://codecov.io/gh/lcavalcante/python-version-controller/graph/badge.svg?token=H65NZV7N3Z)](https://codecov.io/gh/lcavalcante/python-version-controller)
8
+ [![tests](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml/badge.svg)](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml)
9
+
10
+ ## Features
11
+
12
+ - ✅ Automatic semantic version calculation (MAJOR.MINOR.PATCH)
13
+ - ✅ Conventional Commits specification support
14
+ - ✅ Breaking change detection (BREAKING CHANGE: footer or ! suffix)
15
+ - ✅ Customizable starting version and commit
16
+ - ✅ Fast Git repository analysis using pygit2
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install pyvcc
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Basic Usage
27
+
28
+ ```bash
29
+ # Run from your Git repository root
30
+ pyvcc
31
+ ```
32
+
33
+ ### Options
34
+
35
+ ```bash
36
+ # Start from a specific version
37
+ pyvcc --initial-version 1.0.0
38
+
39
+ # Start analysis from a specific commit
40
+ pyvcc --initial-commit abc1234
41
+
42
+ # Verbose output
43
+ pyvcc --verbose
44
+
45
+ # Silent mode (errors only)
46
+ pyvcc --silent
47
+ ```
48
+
49
+ ### Environment Variables
50
+
51
+ ```bash
52
+ # Set repository root
53
+ PYVC_REPO_ROOT=/path/to/repo pyvcc
54
+
55
+ # Set initial version
56
+ PYVC_INITIAL_VERSION=2.0.0 pyvcc
57
+
58
+ # Set initial commit
59
+ PYVC_INITIAL_COMMIT=abc1234 pyvcc
60
+ ```
61
+
62
+ ## How It Works
63
+
64
+ PyVCC analyzes each commit message in your Git history and applies semantic versioning rules:
65
+
66
+ - **MAJOR**: Incremented for breaking changes (commit type with ! or BREAKING CHANGE: footer)
67
+ - **MINOR**: Incremented for new features (feat: commits)
68
+ - **PATCH**: Incremented for bug fixes (fix: commits)
69
+ - **NO BUMP**: Other commit types (chore, docs, style, refactor, test, ci, build, perf)
70
+
71
+ ### Commit Message Examples
72
+
73
+ ```markdown
74
+ # Major version bump (breaking change)
75
+ feat!: Redesign API endpoint structure
76
+
77
+ # OR
78
+ feat: Migrate to new database schema
79
+
80
+ BREAKING CHANGE: Database schema has changed and requires migration
81
+
82
+ # Minor version bump (new feature)
83
+ feat: Add new user authentication API
84
+
85
+ # Patch version bump (bug fix)
86
+ fix: Resolve login error on Safari
87
+
88
+ # No version bump
89
+ chore: Update dependencies
90
+ docs: Update README installation instructions
91
+ style: Format code according to new style guide
92
+ ```
93
+
94
+ ## Requirements
95
+
96
+ - Python 3.10+
97
+ - Git repository with commit history
98
+ - Commit messages following Conventional Commits specification
99
+
100
+ ## Development
101
+
102
+ ```bash
103
+ # Install development dependencies
104
+ pip install -e .[dev]
105
+
106
+ # Run tests
107
+ pytest
108
+
109
+ # Run with coverage
110
+ pytest --cov=pyvcc
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT License - See [LICENSE](LICENSE) for details.
@@ -1,15 +1,18 @@
1
1
  [project]
2
2
  name = "PyVCC"
3
- version = "1.3.0"
3
+ version = "1.3.2"
4
4
  description = "PyVCC python version controller using conventional commits"
5
5
  readme = "README.md"
6
- requires-python = ">=3.10"
6
+ requires-python = ">=3.11"
7
7
  dependencies = [
8
- "pygit2>=1.18.0",
8
+ "pygit2==1.19.2",
9
9
  "structlog>=25.3.0",
10
10
  "typing-extensions>=4.0.0; python_version < '3.11'",
11
11
  ]
12
12
 
13
+ [project.scripts]
14
+ pyvcc = "pyvcc.__main__:main"
15
+
13
16
  [dependency-groups]
14
17
  dev = [
15
18
  "coverage[toml]>=7.8.0",
@@ -1,4 +1,4 @@
1
1
  """PyVCC - Python Version Controller for SemVer manangment with conventional commits"""
2
2
 
3
- from .cli import main as main
3
+ from .cli import run as run
4
4
  from .semver import SemVer as SemVer
@@ -7,10 +7,11 @@ import os
7
7
  import structlog
8
8
  import sys
9
9
  import argparse
10
- from pyvcc.cli import main, validate_args
10
+ from pyvcc.cli import run
11
+ from pyvcc.cli import validate_args
11
12
 
12
13
 
13
- if __name__ == "__main__":
14
+ def main():
14
15
  parser = argparse.ArgumentParser(prog="PyVC", description=__doc__)
15
16
  parser.add_argument("--root", type=str, default=".", required=False)
16
17
  parser.add_argument("--initial-version", type=str, required=False, default="0.1.0")
@@ -42,5 +43,9 @@ if __name__ == "__main__":
42
43
  cache_logger_on_first_use=True,
43
44
  )
44
45
 
45
- version = main(root, version, commit)
46
+ version = run(root, version, commit)
46
47
  sys.stdout.write(version)
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()
@@ -31,7 +31,7 @@ def validate_args(root: str, version: str) -> bool:
31
31
  return is_valid
32
32
 
33
33
 
34
- def main(root: str, version: str, start_commit_id: str | None = None) -> str:
34
+ def run(root: str, version: str, start_commit_id: str | None = None) -> str:
35
35
  repo_path = Path(root) / PurePath(".git")
36
36
  semver = SemVer.semver_from_string(version)
37
37
 
@@ -49,13 +49,13 @@ class SemVer:
49
49
  def is_breaking_change(type: str, message: str) -> bool:
50
50
  """
51
51
  SemVer defines that a breaking change is a commit with a type ending in '!'
52
- OR that contains 'BREAKING CHANGE:' in the messsage body
52
+ OR that contains 'BREAKING CHANGE:' in the message body
53
53
 
54
54
  # Parameters:
55
55
  type (str): Commit message type (feat, fix, chore, etc)
56
56
  message (str): commit message content
57
57
  """
58
- return type[-1] == "!" or "BREAKING CHANGE:" in message
58
+ return (type and type[-1] == "!") or "BREAKING CHANGE:" in message
59
59
 
60
60
  @classmethod
61
61
  def semver_from_string(cls, str_version: str) -> Self:
@@ -96,17 +96,34 @@ class SemVer:
96
96
  message (str): commit message content
97
97
  """
98
98
 
99
- regex = re.compile(r"[.*]?([a-z]*!?)(\(.*\))?:\s(.*)$")
99
+ regex = re.compile(r"^(?:Merged?\s+)?(\w+!?)(?:\(([^)]*)\))?(!?)(?::\s+(.+))?$")
100
100
  bump = BumpEnum.NO_BUMP
101
101
  head = message.split("\n")[0]
102
102
 
103
103
  parsed_head = regex.search(head)
104
104
 
105
105
  if parsed_head is not None:
106
- commit_type = parsed_head.groups()[0].upper()
107
- log.debug("trying bump", type=commit_type, message=head)
108
-
109
- if cls.is_breaking_change(commit_type, message):
106
+ groups = parsed_head.groups()
107
+ type_with_breaking = groups[0].upper()
108
+ breaking_indicator = groups[2] # Captures '!' after scope, e.g. feat(api)!:
109
+
110
+ # Combine type+breaking indicator for is_breaking_change check
111
+ # e.g. "FEAT!" from type or "FEAT" + "!" from scope suffix
112
+ effective_type = (
113
+ type_with_breaking
114
+ if type_with_breaking.endswith("!")
115
+ else type_with_breaking + breaking_indicator
116
+ )
117
+ is_breaking = cls.is_breaking_change(effective_type, message)
118
+
119
+ # Clean the type by removing trailing !
120
+ commit_type = type_with_breaking.rstrip("!")
121
+
122
+ log.debug(
123
+ "trying bump", type=commit_type, message=head, breaking=is_breaking
124
+ )
125
+
126
+ if is_breaking:
110
127
  bump = BumpEnum.MAJOR
111
128
  elif commit_type == CommitEnum.FEAT.name:
112
129
  bump = BumpEnum.MINOR
@@ -2,7 +2,7 @@ from pygit2 import Commit
2
2
  import pytest
3
3
  from pygit2.repository import Repository
4
4
 
5
- from pyvcc.cli import main, validate_args
5
+ from pyvcc.cli import run, validate_args
6
6
  from pyvcc.semver import BumpEnum, SemVer
7
7
 
8
8
 
@@ -13,14 +13,14 @@ class MockCommit:
13
13
  self.short_id = short_id
14
14
 
15
15
 
16
- def test_main_invalid_path():
16
+ def test_run_invalid_path():
17
17
  with pytest.raises(Exception):
18
- main("/(7", "1.0.0")
18
+ run("/(7", "1.0.0")
19
19
 
20
20
 
21
- def test_main_invalid_semver():
21
+ def test_run_invalid_semver():
22
22
  with pytest.raises(Exception):
23
- main(".", "1.0.0.2")
23
+ run(".", "1.0.0.2")
24
24
 
25
25
 
26
26
  def test_validate_version():
@@ -41,7 +41,7 @@ def test_single_commit(monkeypatch):
41
41
 
42
42
  monkeypatch.setattr(Repository, "walk", mock_walk)
43
43
 
44
- assert main(".", "1.0.0") == "1.1.0"
44
+ assert run(".", "1.0.0") == "1.1.0"
45
45
 
46
46
 
47
47
  def test_two_commits(monkeypatch):
@@ -53,7 +53,7 @@ def test_two_commits(monkeypatch):
53
53
 
54
54
  monkeypatch.setattr(Repository, "walk", mock_walk)
55
55
 
56
- assert main(".", "1.0.0") == "1.2.0"
56
+ assert run(".", "1.0.0") == "1.2.0"
57
57
 
58
58
 
59
59
  def test_head_bump_commit():
@@ -64,10 +64,10 @@ def test_head_bump_commit():
64
64
  message = commit.message
65
65
  match SemVer.bump_type(message):
66
66
  case BumpEnum.MAJOR:
67
- assert main(".", "1.0.0", str(repo.head.target)) == "2.0.0"
67
+ assert run(".", "1.0.0", str(repo.head.target)) == "2.0.0"
68
68
  case BumpEnum.MINOR:
69
- assert main(".", "1.0.0", str(repo.head.target)) == "1.1.0"
69
+ assert run(".", "1.0.0", str(repo.head.target)) == "1.1.0"
70
70
  case BumpEnum.PATCH:
71
- assert main(".", "1.0.0", str(repo.head.target)) == "1.0.1"
71
+ assert run(".", "1.0.0", str(repo.head.target)) == "1.0.1"
72
72
  case BumpEnum.NO_BUMP:
73
- assert main(".", "1.0.0", str(repo.head.target)) == "1.0.0"
73
+ assert run(".", "1.0.0", str(repo.head.target)) == "1.0.0"
@@ -141,6 +141,88 @@ def test_bump_multiple5():
141
141
  assert str(version) == "1.0.0"
142
142
 
143
143
 
144
+ def test_bump_scope_with_slash():
145
+ """Test commit with scope containing slash character"""
146
+ version = SemVer(1, 0, 0)
147
+ message = "feat(api/v2): Add new endpoint"
148
+ version.bump_version(message)
149
+ assert str(version) == "1.1.0" # Should be MINOR bump
150
+
151
+
152
+ def test_bump_scope_with_hyphen():
153
+ """Test commit with scope containing hyphen"""
154
+ version = SemVer(1, 0, 0)
155
+ message = "fix(core-library): Resolve memory issue"
156
+ version.bump_version(message)
157
+ assert str(version) == "1.0.1" # Should be PATCH bump
158
+
159
+
160
+ def test_bump_breaking_with_scope():
161
+ """Test breaking change commit with scope"""
162
+ version = SemVer(1, 0, 0)
163
+ message = "feat(api)!: Complete API redesign"
164
+ version.bump_version(message)
165
+ assert str(version) == "2.0.0" # Should be MAJOR bump
166
+
167
+
168
+ def test_bump_extra_spaces():
169
+ """Test commit with extra spaces after colon"""
170
+ version = SemVer(1, 0, 0)
171
+ message = "feat: Add new feature with extra spaces"
172
+ version.bump_version(message)
173
+ assert str(version) == "1.1.0" # Should be MINOR bump
174
+
175
+
176
+ def test_bump_scope_with_underscores():
177
+ """Test commit with scope containing underscores"""
178
+ version = SemVer(1, 0, 0)
179
+ message = "fix(user_auth): Resolve login issue"
180
+ version.bump_version(message)
181
+ assert str(version) == "1.0.1" # Should be PATCH bump
182
+
183
+
184
+ def test_bump_uppercase_type():
185
+ """Test commit with uppercase type (should handle gracefully)"""
186
+ version = SemVer(1, 0, 0)
187
+ message = "FEAT: Uppercase type commit"
188
+ version.bump_version(message)
189
+ assert str(version) == "1.1.0" # Should be MINOR bump
190
+
191
+
192
+ def test_bump_trailing_exclamation_in_description():
193
+ """Trailing ! in description is NOT a breaking change indicator"""
194
+ version = SemVer(1, 0, 0)
195
+ message = "feat: Description with breaking change!"
196
+ version.bump_version(message)
197
+ assert str(version) == "1.1.0" # Should be MINOR bump, not MAJOR
198
+
199
+
200
+ def test_bump_trailing_exclamation_in_fix_description():
201
+ """Trailing ! in fix commit description should NOT trigger MAJOR bump"""
202
+ version = SemVer(1, 0, 0)
203
+ message = "fix: Critical bug fix!"
204
+ version.bump_version(message)
205
+ assert str(version) == "1.0.1" # Should be PATCH, not MAJOR
206
+
207
+
208
+ def test_bump_no_space_after_colon():
209
+ """Test commit without space after colon (should not match conventional commit spec)"""
210
+ version = SemVer(1, 0, 0)
211
+ message = "feat:Description without space"
212
+ version.bump_version(message)
213
+ assert str(version) == "1.0.0" # Should NOT bump (no conventional commit match)
214
+
215
+
216
+ def test_breaking_change_null_type():
217
+ """Test is_breaking_change with None type (null safety check)"""
218
+ version = SemVer(1, 0, 0)
219
+ # This tests the null safety check in is_breaking_change method
220
+ # where (type and type[-1] == "!") handles None type gracefully
221
+ message = "Some commit message without conventional format"
222
+ version.bump_version(message)
223
+ assert str(version) == "1.0.0" # Should NOT bump (no conventional commit match)
224
+
225
+
144
226
  def test_parse_semver_str():
145
227
  version = SemVer.semver_from_string("1.1.0")
146
228
  assert str(version) == "1.1.0"
pyvcc-1.3.0/PKG-INFO DELETED
@@ -1,15 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: PyVCC
3
- Version: 1.3.0
4
- Summary: PyVCC python version controller using conventional commits
5
- Requires-Python: >=3.10
6
- Requires-Dist: pygit2>=1.18.0
7
- Requires-Dist: structlog>=25.3.0
8
- Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
9
- Description-Content-Type: text/markdown
10
-
11
- # python-version-controller
12
- conventional version controller for python
13
-
14
- [![codecov](https://codecov.io/gh/lcavalcante/python-version-controller/graph/badge.svg?token=H65NZV7N3Z)](https://codecov.io/gh/lcavalcante/python-version-controller)
15
- [![tests](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml/badge.svg)](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml)
pyvcc-1.3.0/README.md DELETED
@@ -1,5 +0,0 @@
1
- # python-version-controller
2
- conventional version controller for python
3
-
4
- [![codecov](https://codecov.io/gh/lcavalcante/python-version-controller/graph/badge.svg?token=H65NZV7N3Z)](https://codecov.io/gh/lcavalcante/python-version-controller)
5
- [![tests](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml/badge.svg)](https://github.com/lcavalcante/python-version-controller/actions/workflows/code-quality.yml)
File without changes
File without changes
File without changes
File without changes
File without changes