{"record":{"id":"7ca34957e773bdf2","repo":"stamparm/maltrail","slug":"cannot-bump-r-expected-major-minor","errorCode":null,"errorMessage":"[!] cannot bump %r - expected 'major.minor'","messagePattern":"\\[!\\] cannot bump %r - expected 'major\\.minor'","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"sensor/tools/bump_version.py","lineNumber":79,"sourceCode":"        (os.path.join(ROOT, \"CITATION.cff\"),\n         r'^(version\\s*:\\s*\")[^\"]*(\")', r'\\g<1>%s\\g<2>' % version),\n        (os.path.join(ROOT, \"sensor\", \"src\", \"settings_gen.rs\"),\n         r'^(pub const VERSION: &str = \")[^\"]*(\";)', r'\\g<1>%s\\g<2>' % version),\n    ]\n\n\ndef current():\n    match = re.search(r'^VERSION\\s*=\\s*\"([^\"]+)\"',\n                      _read(os.path.join(ROOT, \"core\", \"settings.py\")), re.M)\n    if not match:\n        raise SystemExit(\"[!] no VERSION assignment in core/settings.py\")\n    return match.group(1)\n\n\ndef next_minor(version):\n    parts = version.split(\".\")\n    if len(parts) < 2 or not all(p.isdigit() for p in parts[:2]):\n        raise SystemExit(\"[!] cannot bump %r - expected 'major.minor'\" % version)\n    return \"%s.%d\" % (parts[0], int(parts[1]) + 1)\n\n\ndef apply(version, date_released=None):\n    \"\"\"Rewrite every file, or none of them.\"\"\"\n\n    planned = []\n    for path, pattern, repl in _edits(version):\n        text = _read(path)\n        new, n = re.subn(pattern, repl, text, flags=re.M)\n        # Exactly once. Zero means the file moved on and this script is now lying about\n        # covering it; more than once means the pattern is catching something it should not\n        # (a dependency's version key, say) and would corrupt the file.\n        if n != 1:\n            raise SystemExit(\"[!] %s: expected exactly 1 version line, matched %d\\n\"\n                             \"    Refusing to write ANY file - a half-applied bump is the \"\n                             \"failure this script exists to prevent.\" % (path, n))\n        planned.append((path, new, text != new))","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/tools/bump_version.py#L61-L97","documentation":"next_minor() parses the current version string expecting at least two dot-separated numeric components ('major.minor'). If the version read from core/settings.py does not have two leading digit-only components, it refuses to bump and exits. This guards against silently producing a malformed version.","triggerScenarios":"Calling next_minor(version) (via --next in main) where version.split('.') yields fewer than 2 parts, or parts[0]/parts[1] are non-numeric (e.g. VERSION = \"0\" or VERSION = \"0.beta\").","commonSituations":"core/settings.py holds a single-component version (0), a word (dev), a suffix (0.5-rc1 where '5-rc1' isn't digits), or someone manually edited the version string.","solutions":["Fix the VERSION value in core/settings.py to the exact 'major.minor' form, e.g. VERSION = \"0.53\".","If you intend a different scheme, bypass --next and pass the full target explicitly: python bump_version.py --version 0.54 (it still must match \\d+\\.\\d+).","Strip suffixes like -rc1 or build metadata from VERSION before running the bump script."],"exampleFix":"// before (core/settings.py)\nVERSION = \"0.5-rc1\"\n// after\nVERSION = \"0.5\"","handlingStrategy":"validation","validationCode":"import re\nv = open('core/settings.py').read()\nm = re.search(r'^VERSION\\s*=\\s*\"([^\"]+)\"', v, re.M)\nif not m or not re.match(r'^\\d+\\.\\d+$', m.group(1)):\n    raise RuntimeError('VERSION must be major.minor, e.g. \"0.53\"')\n","typeGuard":"import re\ndef is_major_minor(version: str) -> bool:\n    return bool(re.match(r'^\\d+\\.\\d+$', version))\n","tryCatchPattern":"try:\n    bump_version.main(['--next'])\nexcept SystemExit as e:\n    if 'cannot bump' in str(e):\n        normalize_version_string()\n","preventionTips":["Store VERSION strictly as digits.digits - no suffixes or single numbers","Validate VERSION format in CI before release steps","Keep pre-release metadata out of core/settings.py; track it elsewhere"],"tags":["versioning","invalid-argument-format"],"backgroundTag":"invalid-argument-format","analyzedSha":"77cfb06d7606506d101bbcec0786c77166c4255e","analyzedAt":"2026-09-13T03:50:16.010Z","contentChangedAt":"2026-09-13T03:50:16.010Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}