From 36a7d52dc6626dcfbb19168446619f831f8dc4c8 Mon Sep 17 00:00:00 2001 From: Danny Burrow Date: Sat, 28 Mar 2026 18:30:09 +0100 Subject: [PATCH] fix: address code quality issues and repo hygiene gaps, bump 0.7.1 - Use LOG.debug instead of logging.debug in _detect_hubble_cmd - Replace assert with early-return guards in reader threads - Guard cursor_flow_idx when ordered_keys is empty in watch mode - Close capture file handle on early exit paths to prevent leak - Add missing py.typed marker (referenced in CHANGELOG since v0.4.0) - Add types-PyYAML to dev dependencies for local mypy - Add .mypy_cache, .pytest_cache, .ruff_cache to .gitignore - Bump version to 0.7.1, update changelog --- .gitignore | 5 ++++- CHANGELOG.md | 12 ++++++++++++ hubble_audit2policy.py | 20 ++++++++++++++------ py.typed | 0 pyproject.toml | 4 ++-- 5 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 py.typed diff --git a/.gitignore b/.gitignore index 9f6eccf..3ca49bd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ __pycache__/ dist/ build/ .venv/ -.vscode/ \ No newline at end of file +.vscode/ +.mypy_cache/ +.pytest_cache/ +.ruff_cache/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b62adf..27aa7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.1] - 2026-03-28 + +### Fixed + +- Use module logger (`LOG.debug`) instead of root `logging.debug` in `_detect_hubble_cmd`. +- Replace `assert` guards in reader threads with early-return checks (assertions are stripped under `python -O`). +- Guard `cursor_flow_idx` when `ordered_keys` is empty in watch select mode. +- Close capture file handle on early exit paths to prevent resource leak. +- Add missing `py.typed` marker file (referenced in CHANGELOG since v0.4.0 but never created). +- Add `types-PyYAML` to dev dependencies so local `mypy` works without manual installs. +- Add `.mypy_cache/`, `.pytest_cache/`, `.ruff_cache/` to `.gitignore`. + ## [0.7.0] - 2026-03-27 ### Added diff --git a/hubble_audit2policy.py b/hubble_audit2policy.py index 74f46ba..75fac24 100755 --- a/hubble_audit2policy.py +++ b/hubble_audit2policy.py @@ -8,7 +8,7 @@ from __future__ import annotations -__version__ = "0.7.0" +__version__ = "0.7.1" __author__ = "noexecstack" __license__ = "Apache-2.0" @@ -1013,7 +1013,7 @@ def _detect_hubble_cmd() -> tuple[list[str], bool]: only when appropriate. """ if shutil.which("hubble"): - logging.debug("hubble: using PATH binary with -P (auto port-forward)") + LOG.debug("hubble: using PATH binary with -P (auto port-forward)") return ["hubble", "observe"], True # Fall back to kubectl exec into a Cilium pod. if shutil.which("kubectl"): @@ -1021,7 +1021,7 @@ def _detect_hubble_cmd() -> tuple[list[str], bool]: cilium_node_map = _build_cilium_node_map() if cilium_node_map: cilium_pod = next(iter(sorted(cilium_node_map.values()))) - logging.debug("hubble: using kubectl exec into %s", cilium_pod) + LOG.debug("hubble: using kubectl exec into %s", cilium_pod) return [ "kubectl", "exec", @@ -1075,7 +1075,8 @@ def _hubble_reader_thread( stop_event: threading.Event, ) -> None: """Read JSON flow lines from hubble stdout and feed them into *store*.""" - assert proc.stdout is not None + if proc.stdout is None: + return for line in proc.stdout: if stop_event.is_set(): break @@ -1100,7 +1101,8 @@ def _hubble_stderr_thread( stop_event: threading.Event, ) -> None: """Drain hubble stderr and record the last meaningful line in *store*.""" - assert proc.stderr is not None + if proc.stderr is None: + return for line in proc.stderr: if stop_event.is_set(): break @@ -1218,6 +1220,8 @@ def _watch_mode(args: argparse.Namespace) -> None: try: hubble_cmd = _build_hubble_observe_cmd(args) except RuntimeError as exc: + if capture_fh: + capture_fh.close() LOG.error("%s", exc) sys.exit(EXIT_ERROR) @@ -1230,6 +1234,8 @@ def _watch_mode(args: argparse.Namespace) -> None: try: proc_holder = [_launch_hubble(hubble_cmd, store, stop_event)] except FileNotFoundError: + if capture_fh: + capture_fh.close() LOG.error("Command not found: %s", hubble_cmd[0]) sys.exit(EXIT_ERROR) @@ -1297,9 +1303,11 @@ def _run(stdscr: curses.window) -> None: # type: ignore[name-defined] is_paused = not is_paused elif key in (ord("s"), ord("S")): # toggle select mode + if not is_selecting and not ordered_keys: + continue # nothing to select is_selecting = not is_selecting if is_selecting: - cursor_flow_idx = min(cursor_flow_idx, max(0, len(ordered_keys) - 1)) + cursor_flow_idx = min(cursor_flow_idx, len(ordered_keys) - 1) elif key == 27: # Esc – exit select + clear is_selecting = False diff --git a/py.typed b/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 91f7457..7315788 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "hubble-audit2policy" -version = "0.7.0" +version = "0.7.1" description = "Generate least-privilege CiliumNetworkPolicy YAML from Hubble flow logs." readme = "README.md" license = "Apache-2.0" @@ -26,7 +26,7 @@ classifiers = [ dependencies = ["pyyaml>=6.0"] [project.optional-dependencies] -dev = ["pytest>=7.0", "ruff>=0.4", "mypy>=1.10"] +dev = ["pytest>=7.0", "ruff>=0.4", "mypy>=1.10", "types-PyYAML>=6.0"] [project.scripts] hubble-audit2policy = "hubble_audit2policy:main"