From c271143626cf4994c17d13046f0a768a3db95d26 Mon Sep 17 00:00:00 2001 From: arielev Date: Fri, 2 Jan 2026 13:13:34 +0200 Subject: [PATCH 1/3] fix version regex to prevent false matches on compound keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add negative lookbehind to prevent matching 'target-version', 'myversion', etc. - Add detailed inline comments explaining each regex component - Use pybump set --auto for unique test.pypi.org versions (commit SHA as release) Fixes #58 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/pythonpackage.yml | 4 +++- src/pybump.py | 12 +++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 60336a2..afc7904 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -110,7 +110,9 @@ jobs: id: app_version_bump run: | pip install pybump - echo "app_version=$(pybump bump --level patch --file pyproject.toml)" >> $GITHUB_OUTPUT + # Bump patch, then set release to git commit SHA for unique test.pypi.org versions + pybump bump --level patch --file pyproject.toml + echo "app_version=$(pybump set --auto --file pyproject.toml)" >> $GITHUB_OUTPUT - name: Install uv uses: astral-sh/setup-uv@v5 with: diff --git a/src/pybump.py b/src/pybump.py index c06809c..030bb17 100644 --- a/src/pybump.py +++ b/src/pybump.py @@ -10,7 +10,17 @@ except ImportError: from pybump_version import PybumpVersion -regex_version_pattern = re.compile(r"((?:__)?version(?:__)? ?= ?[\"'])(.+?)([\"'])") +# Regex to match version strings like: version = "1.0.0" or __version__ = '1.0.0' +# (? Date: Fri, 2 Jan 2026 13:30:01 +0200 Subject: [PATCH 2/3] add --metadata flag to set command for PEP 440 compatible versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --metadata flag to 'set' command that works with --auto - When used, sets commit SHA as metadata (+sha) instead of release (-sha) - Fix version regex to prevent false matches on compound keys like 'target-version' - Update CI to use --auto --metadata for unique test.pypi.org versions Fixes #58 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/pythonpackage.yml | 5 +++-- pyproject.toml | 2 +- src/pybump.py | 16 +++++++++++---- test/test_simulate_pybump.py | 31 ++++++++++++++++++++++------- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index afc7904..e65ac01 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -110,9 +110,10 @@ jobs: id: app_version_bump run: | pip install pybump - # Bump patch, then set release to git commit SHA for unique test.pypi.org versions + # Bump patch, then set commit SHA as metadata (+sha) for unique test.pypi.org versions + # Using --metadata flag for PEP 440 compatibility (+ instead of -) pybump bump --level patch --file pyproject.toml - echo "app_version=$(pybump set --auto --file pyproject.toml)" >> $GITHUB_OUTPUT + echo "app_version=$(pybump set --auto --metadata --file pyproject.toml)" >> $GITHUB_OUTPUT - name: Install uv uses: astral-sh/setup-uv@v5 with: diff --git a/pyproject.toml b/pyproject.toml index f81bab0..2b10125 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ dependencies = {file = ["requirements.txt"]} [project] name = "pybump" -version = "1.13.1" +version = "1.14.0" dynamic = ["dependencies"] authors = [ diff --git a/src/pybump.py b/src/pybump.py index 030bb17..4cc4d39 100644 --- a/src/pybump.py +++ b/src/pybump.py @@ -186,6 +186,9 @@ def main(): # pragma: no cover help='Set automatic release / metadata from current git branch for current version') set_group.add_argument('--set-version', help='Semantic version to set as a combination of \'vX.Y.Z-release+metadata\'') + parser_set.add_argument('--metadata', action='store_true', + help='With --auto, set commit SHA as metadata (+sha) instead of release (-sha)', + required=False) parser_set.add_argument('--quiet', action='store_true', help='Do not print new version', required=False) # Sub-parser for get version command @@ -256,18 +259,23 @@ def main(): # pragma: no cover # Case set-version argument passed, just set the new version with its value if args['set_version']: new_version = PybumpVersion(args['set_version']) - # Case the 'auto' flag was set, set release with current git branch name and metadata with hash + # Case the 'auto' flag was set, set release or metadata with git commit SHA elif args['auto']: from git import Repo, InvalidGitRepositoryError # get the directory path of current working file file_dirname_path = os.path.dirname(args['file']) try: repo = Repo(path=file_dirname_path, search_parent_directories=True) - # update current version release and metadata with relevant git values + # get commit SHA (try active branch first, fall back to HEAD) try: - version_object.release = str(repo.active_branch.commit) + commit_sha = str(repo.active_branch.commit) except TypeError: - version_object.release = str(repo.head.object.hexsha) + commit_sha = str(repo.head.object.hexsha) + # set metadata (+sha) if --metadata flag is set, otherwise set release (-sha) + if args.get('metadata'): + version_object.metadata = commit_sha + else: + version_object.release = commit_sha new_version = version_object except InvalidGitRepositoryError: print("{} is not a valid git repo".format(file_dirname_path), file=stderr) diff --git a/test/test_simulate_pybump.py b/test/test_simulate_pybump.py index 66a5f78..8ae4c01 100644 --- a/test/test_simulate_pybump.py +++ b/test/test_simulate_pybump.py @@ -26,25 +26,27 @@ def simulate_get_version(file, app_version=False, sem_ver=False, release=False, return run(["python", "src/pybump.py", "get", "--file", file], stdout=PIPE, stderr=PIPE) -def simulate_set_version(file, version='', app_version=False, auto=False): +def simulate_set_version(file, version='', app_version=False, auto=False, metadata=False): """ execute sub process to simulate real app execution, set new version to a file if auto is True, auto add git branch / hash + if metadata is True (with auto), set SHA as metadata (+sha) instead of release (-sha) if app_version is True, then add the --app-version flag to execution :param file: string :param version: string :param app_version: boolean :param auto: boolean + :param metadata: boolean :return: CompletedProcess object """ if auto: + cmd = ["python", "src/pybump.py", "set", "--file", file, "--auto"] + if metadata: + cmd.append("--metadata") if app_version: - return run(["python", "src/pybump.py", "set", "--file", file, "--auto", "--app-version"], - stdout=PIPE, stderr=PIPE) - else: - return run(["python", "src/pybump.py", "set", "--file", file, "--auto"], - stdout=PIPE, stderr=PIPE) + cmd.append("--app-version") + return run(cmd, stdout=PIPE, stderr=PIPE) else: if app_version: return run(["python", "src/pybump.py", "set", "--file", file, "--set-version", version, "--app-version"], @@ -239,16 +241,31 @@ def test_get_flags(self): def test_set_flags(self): ################################################ - # simulate the 'set' command with version prefix + # simulate the 'set' command with --auto flag ################################################ # first set test_valid_setup.py a simple version simulate_set_version("test/test_content_files/test_valid_setup.py", version="1.0.1") + # test --auto sets release (-sha) test_set_auto = simulate_set_version("test/test_content_files/test_valid_setup.py", auto=True) self.assertRegex(test_set_auto.stdout.decode('utf-8').strip(), r'\b1.0.1-[0-9a-f]{40}\b', msg="test that 'test_set_auto' contains an hexadecimal string with exactly 40 characters") + ######################################################## + # simulate the 'set' command with --auto --metadata flag + ######################################################## + # reset to simple version + simulate_set_version("test/test_content_files/test_valid_setup.py", version="2.0.0") + + # test --auto --metadata sets metadata (+sha) instead of release (-sha) + test_set_auto_metadata = simulate_set_version("test/test_content_files/test_valid_setup.py", + auto=True, metadata=True) + self.assertRegex(test_set_auto_metadata.stdout.decode('utf-8').strip(), + r'\b2.0.0\+[0-9a-f]{40}\b', + msg="test that '--auto --metadata' sets SHA as metadata (+sha), " + "output should match 2.0.0+<40-char-hex>") + # test invalid version set test_set_auto = simulate_set_version("test/test_content_files/test_valid_setup.py", version='V123.x.4') self.assertEqual('Invalid semantic version format: V123.x.4\nMake sure to comply with https://semver.org/ ' From 665edab724923f306c6d9489417d7b32d04ab823 Mon Sep 17 00:00:00 2001 From: arielev Date: Fri, 2 Jan 2026 13:38:24 +0200 Subject: [PATCH 3/3] add test coverage for compound key regex fix (issue #58) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add test data for pyproject.toml with compound keys (target-version, myversion) - Add test case verifying only 'version' matches, not 'target-version' or 'myversion' - Add test case verifying inline/indented version still works 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- test/__init__.py | 25 +++++++++++++++++++++++++ test/test_pybump.py | 12 +++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/test/__init__.py b/test/__init__.py index d1ed896..f5850c4 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -99,6 +99,31 @@ dependencies = [] """ +# pyproject.toml with compound keys containing 'version' substring (issue #58) +# Should only match 'version = "2.0.0"' and NOT 'target-version' or other compound keys +valid_pyproject_toml_with_compound_keys = """ +[project] +name = "test-project" +version = "2.0.0" +description = "A test project" + +[tool.ruff] +target-version = "py312" + +[tool.some] +this-is-a-version-containing-key = "1.1.7" +myversion = "should-not-match" +""" + +# Content with version NOT at line start - should still match (inline in setup.py style) +valid_setup_py_inline_version = """ + setuptools.setup( + name="pybump", + version="3.2.1", + author="Test", + ) + """ + valid_version_file_1 = """0.12.4""" valid_version_file_2 = """1.5.0-alpha+meta""" invalid_version_file_1 = """ diff --git a/test/test_pybump.py b/test/test_pybump.py index 5127eed..6f195eb 100644 --- a/test/test_pybump.py +++ b/test/test_pybump.py @@ -6,7 +6,7 @@ from . import valid_helm_chart, invalid_helm_chart, empty_helm_chart, \ valid_setup_py, invalid_setup_py_1, invalid_setup_py_multiple_ver, \ valid_version_file_1, valid_version_file_2, invalid_version_file_1, invalid_version_file_2, \ - valid_pyproject_toml + valid_pyproject_toml, valid_pyproject_toml_with_compound_keys, valid_setup_py_inline_version class PyBumpTest(unittest.TestCase): @@ -178,6 +178,16 @@ def test_get_version_from_file(self): self.assertEqual(get_version_from_file(valid_pyproject_toml), '0.1.0') + # Test for issue #58: compound keys containing 'version' substring should not match + # File contains: version="2.0.0", target-version="py312", myversion="should-not-match" + # Only 'version' should be matched, not 'target-version' or 'myversion' + self.assertEqual(get_version_from_file(valid_pyproject_toml_with_compound_keys), '2.0.0', + msg="Should only match 'version', not compound keys like 'target-version'") + + # Test that inline version (not at line start) still works + self.assertEqual(get_version_from_file(valid_setup_py_inline_version), '3.2.1', + msg="Should match version even when indented (setup.py style)") + def test_set_version_in_file(self): # test the version replacement string, in a content content_pre = 'some text before version="3.17.5", and some text after'