{"record":{"id":"c8b70c964e57403a","repo":"GyulyVGC/sniffnet","slug":"cannot-parse-debian-changelog","errorCode":null,"errorMessage":"Cannot parse debian/changelog","messagePattern":"Cannot parse debian/changelog","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"resources/packaging/linux/deb-source/prepare.py","lineNumber":43,"sourceCode":"\ndef prepare(output):\n    if output.exists() and any(output.iterdir()):\n        raise SystemExit(f\"Refusing to overwrite an existing release in {output}; use --output with a fresh directory\")\n    # Stage outside the checkout so Cargo cannot inherit a newer parent workspace.\n    with tempfile.TemporaryDirectory(prefix=\"sniffnet-debian-\") as staging:\n        source = prepare_staged(Path(staging))\n        output.mkdir(parents=True, exist_ok=True)\n        for path in Path(staging).iterdir():\n            shutil.move(str(path), output / path.name)\n    print(f\"Release files: {output}\", flush=True)\n    return output / source.name\n\n\ndef prepare_staged(output):\n    changelog = (DEBIAN / \"changelog\").read_text()\n    match = re.match(r\"sniffnet \\(([^)]+)\\) ([\\w-]+);\", changelog)\n    if not match:\n        raise SystemExit(\"Cannot parse debian/changelog\")\n    version = match[1].rsplit(\"-\", 1)[0]\n    tag = f\"refs/tags/v{version}\"\n    commit = run(\"git\", \"rev-parse\", \"--verify\", f\"{tag}^{{commit}}\",\n                 capture_output=True, text=True).stdout.strip()\n    manifest = tomllib.loads(run(\"git\", \"show\", f\"{commit}:Cargo.toml\",\n                                capture_output=True, text=True).stdout)\n    if manifest[\"package\"][\"version\"] != version:\n        raise SystemExit(\"debian/changelog must match the tagged Cargo.toml version\")\n    source = output / f\"sniffnet-{version}\"\n    archive = output / f\"sniffnet_{version}.orig.tar.gz\"\n    if source.exists() or archive.exists():\n        raise SystemExit(f\"Refusing to overwrite an existing release in {output}; use --output with a fresh directory\")\n    epoch = int(run(\"git\", \"show\", \"-s\", \"--format=%ct\", commit,\n                    capture_output=True, text=True).stdout)\n    output.mkdir(parents=True, exist_ok=True)\n    source.mkdir()\n\n    # Extract only release-tag inputs. A branch with the same name cannot win","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/GyulyVGC/sniffnet/blob/592a62a5ad2fb2e9f8470a6174ab9f18dc86997a/resources/packaging/linux/deb-source/prepare.py#L25-L61","documentation":"prepare_staged() reads resources/packaging/linux/deb-source/debian/changelog and parses its first line with the regex ^sniffnet (<version>) <distribution>;. If the first line does not match this exact Debian changelog format, the version cannot be determined and the script aborts. The parser expects the standard Debian changelog header and does not tolerate deviating package names or missing fields.","triggerScenarios":"Running prepare.py when the first line of debian/changelog is malformed: package name other than 'sniffnet' at line start, missing parenthesized version, missing distribution codename, missing trailing semicolon, or a changelog starting with blank lines or a comment.","commonSituations":"Hand-editing the changelog and breaking the header; renaming the package; an automated tool reformatting the changelog; a changelog file accidentally emptied or replaced with a template; missing closing semicolon after the distribution name.","solutions":["Inspect the first line of resources/packaging/linux/deb-source/debian/changelog and fix it to match: sniffnet (<version>) <distro>; e.g. 'sniffnet (1.3.1-1) trixie;'","Restore the changelog from git (git checkout -- resources/packaging/linux/deb-source/debian/changelog)","If adding a new entry, use dch or copy an existing entry's header format exactly, keeping 'sniffnet', parentheses, and the trailing semicolon"],"exampleFix":"# before\nsniffnet 1.3.1 trixie;\n# after\nsniffnet (1.3.1) trixie;","handlingStrategy":"validation","validationCode":"import re\nfirst = open(\"resources/packaging/linux/deb-source/debian/changelog\").readline()\nif not re.match(r\"sniffnet \\(([^)]+)\\) ([\\w-]+);\", first):\n    raise SystemExit(\"debian/changelog header is malformed\")","typeGuard":"def changelog_header_is_valid(text: str) -> bool:\n    return re.match(r\"sniffnet \\(([^)]+)\\) ([\\w-]+);\", text) is not None","tryCatchPattern":"try:\n    prepare(output)\nexcept SystemExit as e:\n    if \"Cannot parse debian/changelog\" in str(e):\n        print(\"Fix debian/changelog first line to: sniffnet (<version>) <distro>;\", file=sys.stderr)\n    raise","preventionTips":["Edit debian/changelog with dch instead of hand-editing the header","Keep the exact header format: package name, parenthesized version, distribution, trailing semicolon","Validate the changelog in CI before running the packager","Restore the file from git if it was accidentally rewritten"],"tags":["parsing","debian","changelog","regex"],"backgroundTag":"invalid-argument-format","analyzedSha":"592a62a5ad2fb2e9f8470a6174ab9f18dc86997a","analyzedAt":"2026-09-18T11:20:14.290Z","contentChangedAt":"2026-09-18T11:20:14.290Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}