Coverage for .tox/coverage/lib/python3.13/site-packages/wuttatell/cli/audit.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-23 19:04 -0500

1# -*- coding: utf-8; -*- 

2################################################################################ 

3# 

4# WuttaTell -- Telemetry submission for Wutta Framework 

5# Copyright © 2025-2026 Lance Edgar 

6# 

7# This file is part of Wutta Framework. 

8# 

9# Wutta Framework is free software: you can redistribute it and/or modify it 

10# under the terms of the GNU General Public License as published by the Free 

11# Software Foundation, either version 3 of the License, or (at your option) any 

12# later version. 

13# 

14# Wutta Framework is distributed in the hope that it will be useful, but 

15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 

16# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 

17# more details. 

18# 

19# You should have received a copy of the GNU General Public License along with 

20# Wutta Framework. If not, see <http://www.gnu.org/licenses/>. 

21# 

22################################################################################ 

23""" 

24See also: :ref:`wutta-audit` 

25""" 

26 

27import logging 

28import os 

29import subprocess 

30import sys 

31import tempfile 

32 

33try: 

34 from typing import Annotated 

35except ImportError: # pragma: no cover 

36 from typing_extensions import Annotated 

37 

38import typer 

39 

40from wuttjamaican.cli import wutta_typer 

41 

42log = logging.getLogger(__name__) 

43 

44 

45@wutta_typer.command() 

46def audit( 

47 ctx: typer.Context, 

48 path: Annotated[ 

49 str, 

50 typer.Option("--path", help="Restrict audit to the given path."), 

51 ] = None, 

52 quiet: Annotated[ 

53 bool, 

54 typer.Option("--quiet", "-q", help="Suppress output unless audit fails."), 

55 ] = False, 

56): 

57 """ 

58 Run the 'pip-audit' command and report findings 

59 """ 

60 config = ctx.parent.wutta_config 

61 

62 # TODO: how should we be doing this 

63 pip_audit = os.path.join(sys.prefix, "bin", "pip-audit") 

64 if not os.path.exists(pip_audit): 

65 raise FileNotFoundError(f"pip-audit not found: {pip_audit}") 

66 

67 cmd = [pip_audit] 

68 if path: 

69 cmd.extend(["--path", path]) 

70 

71 if ignored := config.get_list(f"{config.appname}.pip_audit.ignored_vulns"): 

72 for vuln in ignored: 

73 cmd.extend(["--ignore-vuln", vuln]) 

74 

75 log.debug("will run pip-audit command: %s", cmd) 

76 

77 if quiet: 

78 fd, temp_out = tempfile.mkstemp() 

79 os.close(fd) 

80 fd, temp_err = tempfile.mkstemp() 

81 os.close(fd) 

82 

83 cmd.extend(["--progress-spinner=off", "--output", temp_out]) 

84 try: 

85 with open(temp_err, "wb") as ferr: 

86 subprocess.run(cmd, check=True, stderr=ferr) 

87 

88 except subprocess.CalledProcessError as err: 

89 with open(temp_err, "rt", encoding="utf_8") as f: 

90 sys.stderr.write(f.read()) 

91 with open(temp_out, "rt", encoding="utf_8") as f: 

92 sys.stderr.write(f.read()) 

93 sys.stderr.write("\n") 

94 sys.exit(err.returncode) 

95 

96 finally: 

97 os.remove(temp_out) 

98 os.remove(temp_err) 

99 

100 sys.exit(0) 

101 

102 else: # not quiet, so just run and exit w/ same code 

103 result = subprocess.run(cmd, check=False) 

104 sys.exit(result.returncode)