fix: properly handle invalid and dev versions in version parser, fixes #9014

- `src/borg/__init__.py`: The `setuptools_scm` fallback version `0.1.dev1`
  was incorrectly bypassing the assertion check (as `0` and `1` are valid
  integers), which hid the intended helpful error message when building
  from source without tags. Added an explicit check for the `0.1.dev` prefix.

- `src/borg/version.py`: `parse_version` and `format_version` have been
  updated to correctly understand `setuptools_scm`'s `.dev` or `dev` prefixes.
  These dev releases are now properly encoded in the version tuple as `-9`
  (which logically makes them older than alphas `-4`, betas `-3`, rcs `-2`,
  and final `-1`), and correctly reformatted to `.dev` strings.
This commit is contained in:
Thomas Waldmann 2026-05-12 22:22:33 +02:00
parent bc7814a677
commit 232ccabfa3
No known key found for this signature in database
GPG key ID: 243ACFA951F78E01
2 changed files with 4 additions and 4 deletions

View file

@ -10,7 +10,7 @@ __version_tuple__ = parse_version(__version__).release
# assert that all semver components are integers
# this is mainly to show errors when people repackage poorly
# and setuptools_scm determines a 0.1.dev... version
assert all(isinstance(v, int) for v in __version_tuple__), \
assert not __version__.startswith("0.1.dev") and all(isinstance(v, int) for v in __version_tuple__), \
"""\
broken borgbackup version metadata: %r

View file

@ -17,7 +17,7 @@ def parse_version(version):
"""
version_re = r"""
(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+) # version, e.g. 1.2.33
(?P<prerelease>(?P<ptype>a|b|rc)(?P<pnum>\d+))? # optional prerelease, e.g. a1 or b2 or rc33
(?P<prerelease>\.?(?P<ptype>a|b|rc|dev)(?P<pnum>\d+))? # optional prerelease, e.g. a1 or b2 or rc33 or .dev1
"""
m = re.match(version_re, version, re.VERBOSE)
if m is None:
@ -25,7 +25,7 @@ def parse_version(version):
gd = m.groupdict()
version = [int(gd['major']), int(gd['minor']), int(gd['patch'])]
if m.lastgroup == 'prerelease':
p_type = {'a': -4, 'b': -3, 'rc': -2}[gd['ptype']]
p_type = {'a': -4, 'b': -3, 'rc': -2, 'dev': -9}[gd['ptype']]
p_num = int(gd['pnum'])
version += [p_type, p_num]
else:
@ -44,6 +44,6 @@ def format_version(version):
elif part == -1:
break
else:
f[-1] = f[-1] + {-2: 'rc', -3: 'b', -4: 'a'}[part] + str(next(it))
f[-1] = f[-1] + {-2: 'rc', -3: 'b', -4: 'a', -9: '.dev'}[part] + str(next(it))
break
return '.'.join(f)