{"record":{"id":"c827edce3506ce78","repo":"stamparm/maltrail","slug":"version-r-is-not-major-minor-patch","errorCode":null,"errorMessage":"[!] version %r is not 'major.minor[.patch]'","messagePattern":"\\[!\\] version %r is not 'major\\.minor\\[\\.patch\\]'","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"sensor/tools/check_version.py","lineNumber":113,"sourceCode":"    \"\"\"\n\n    match = re.search(r'name = \"maltrail-sensor\"\\nversion = \"([^\"]+)\"', _read(CARGO_LOCK))\n    if not match:\n        raise SystemExit(\"[!] no maltrail-sensor package entry found in %s\" % CARGO_LOCK)\n    return match.group(1)\n\n\ndef series(version):\n    \"\"\"'3.0.1' and '3.0' both reduce to (3, 0) - Maltrail versions two components, Cargo three.\n\n    A SemVer pre-release / build suffix is stripped first, so 'v3.0-rc1' and '3.0.0+deb' are the\n    3.0 series like anything else. Release candidates are the whole point of having a release\n    pipeline that can be rehearsed, and refusing to name one would have made that impossible.\n    \"\"\"\n    version = re.split(r\"[-+]\", version, 1)[0]\n    parts = version.split('.')\n    if len(parts) < 2:\n        raise SystemExit(\"[!] version %r is not 'major.minor[.patch]'\" % version)\n    try:\n        return (int(parts[0]), int(parts[1]))\n    except ValueError:\n        raise SystemExit(\"[!] version %r has non-numeric components\" % version)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"check that the sensor, the server and CITATION.cff agree on the version\")\n    parser.add_argument(\"--tag\", help=\"also require both to match this release tag (e.g. '3.0' or 'v3.0')\")\n    args = parser.parse_args()\n\n    settings, cargo, citation = settings_version(), cargo_version(), citation_version()\n    generated, locked = settings_gen_version(), cargo_lock_version()\n    print(\"[i] core/settings.py VERSION   = %s\" % settings)\n    print(\"[i] sensor/Cargo.toml version  = %s\" % cargo)\n    print(\"[i] CITATION.cff version       = %s\" % citation)\n    print(\"[i] settings_gen.rs VERSION    = %s\" % generated)\n    print(\"[i] Cargo.lock version         = %s\" % locked)","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/tools/check_version.py#L95-L131","documentation":"series() reduces a version string like '3.0.1' or '3.0' to a (major, minor) tuple so Maltrail's two-component versions can be compared with Cargo's three-component ones. It strips any pre-release/build suffix (after '-' or '+') and then requires at least major.minor. A version with fewer than two dot-separated components cannot be reduced and triggers this exit.","triggerScenarios":"Passing --tag or configuring a version like '3', 'v3' (after prefix stripping), or an empty-ish value so that after re.split(r'[-+]') and split('.') there are fewer than 2 parts.","commonSituations":"Tagging a release as just '3' instead of '3.0'; passing a major-only version on the CLI; a version source containing only one numeric component.","solutions":["Use a version with at least major.minor, e.g. '3.0' or '3.0.1'","Re-run with --tag '3.0' (or 'v3.0') instead of a major-only tag","Check the version source (tag/CITATION.cff/Cargo.toml) for accidental truncation","Remember pre-release suffixes like '3.0.0-rc1' are allowed — the issue is missing the minor component"],"exampleFix":"// before\ncheck_version.py --tag 3\n// after\ncheck_version.py --tag 3.0","handlingStrategy":"validation","validationCode":"import re\ndef is_valid_series(v):\n    v = re.split(r'[-+]', v, 1)[0]\n    parts = v.split('.')\n    return len(parts) >= 2 and parts[0].isdigit() and parts[1].isdigit()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always tag releases as major.minor or major.minor.patch","Encode pre-releases as suffixes (-rc1) not alternate components","Validate tags in CI before invoking check_version.py"],"tags":["python","versioning","cli"],"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"}