{"record":{"id":"77aea75c94eef42b","repo":"stamparm/maltrail","slug":"no-version-constant-found-in-s","errorCode":null,"errorMessage":"[!] no VERSION constant found in %s","messagePattern":"\\[!\\] no VERSION constant found in (.+?)","errorType":"console","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"sensor/tools/check_version.py","lineNumber":86,"sourceCode":"    match = re.search(r\"^version\\s*:\\s*[\\\"']([^\\\"']+)[\\\"']\", _read(CITATION), re.M)\n    if not match:\n        raise SystemExit(\"[!] no version key found in %s\" % CITATION)\n    return match.group(1)\n\n\ndef settings_gen_version():\n    \"\"\"pub const VERSION: &str = \"3.2\"; from the generated mirror of core/settings.py.\n\n    This is a FOURTH place the version lives, and it was the one nobody checked here. The Rust\n    parity test covers it, but that test needs a Rust toolchain - so the automated monthly bump,\n    which is a text edit on a machine that has none, could not tell it had gone stale. It found\n    out from a failed release: the tag was already pushed, the gate refused it, and nothing was\n    built. A version bump is text; verifying it should be text too.\n    \"\"\"\n\n    match = re.search(r'^pub const VERSION: &str = \"([^\"]+)\";', _read(SETTINGS_GEN), re.M)\n    if not match:\n        raise SystemExit(\"[!] no VERSION constant found in %s\" % SETTINGS_GEN)\n    return match.group(1)\n\n\ndef cargo_lock_version():\n    \"\"\"The maltrail-sensor entry in sensor/Cargo.lock.\n\n    A FIFTH place, and the release builds with --locked: a lock that still names the old version\n    does not warn, it refuses to build.\n    \"\"\"\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.","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/stamparm/maltrail/blob/77cfb06d7606506d101bbcec0786c77166c4255e/sensor/tools/check_version.py#L68-L104","documentation":"settings_gen_version parses the generated Rust mirror of core/settings.py for `pub const VERSION: &str = \"...\"`. If the generated file is missing that constant (stale, regenerated from a modified template, or truncated), the tool exits with this message instead of reporting a bogus version. This is the text-based gate that keeps a failed release from being tagged.","triggerScenarios":"Running check_version.py when the generated settings mirror (SETTINGS_GEN) lacks a `pub const VERSION: &str = \"...\";` line — e.g. the generator template changed, the file was regenerated from a settings.py that removed VERSION, or the file is truncated/partially generated.","commonSituations":"A refactor renamed or removed the VERSION constant from settings.py before regenerating; the build script wrote a partial file mid-failure; someone hand-edited the generated file and broke the constant declaration syntax.","solutions":["Regenerate the settings mirror from core/settings.py with the project's generator so VERSION is emitted","Verify settings.py still defines VERSION and the generator template still contains the `pub const VERSION` line","Check the file for truncation or hand edits and restore `pub const VERSION: &str = \"3.2\";`","Re-run check_version.py to confirm the gate passes"],"exampleFix":"// before (generated settings mirror missing constant)\n// no VERSION constant\n// after\npub const VERSION: &str = \"3.2\";","handlingStrategy":"validation","validationCode":"import re, pathlib\ntext = pathlib.Path(SETTINGS_GEN).read_text()\nassert re.search(r'^pub const VERSION: &str = \"[^\"]+\";', text, re.M), \"generated settings missing VERSION\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always regenerate the mirror via the project generator, never hand-edit","Add a CI check that regenerates and diffs the mirror","Keep VERSION in settings.py so the template always emits it"],"tags":["python","rust","code-generation","release-tooling"],"backgroundTag":"missing-required-config-field","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"}