exosphere-cli 2.2.0__tar.gz → 2.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/PKG-INFO +3 -2
  2. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/README.md +1 -0
  3. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/pyproject.toml +3 -3
  4. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/objects.py +1 -0
  5. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/api.py +24 -2
  6. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/freebsd.py +19 -1
  7. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/schema/host-report.schema.json +5 -0
  8. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/LICENSE +0 -0
  9. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/__init__.py +0 -0
  10. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/cli.py +0 -0
  11. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/__init__.py +0 -0
  12. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/config.py +0 -0
  13. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/connections.py +0 -0
  14. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/host.py +0 -0
  15. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/inventory.py +0 -0
  16. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/report.py +0 -0
  17. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/sudo.py +0 -0
  18. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/ui.py +0 -0
  19. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/utils.py +0 -0
  20. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/commands/version.py +0 -0
  21. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/config.py +0 -0
  22. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/context.py +0 -0
  23. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/data.py +0 -0
  24. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/database.py +0 -0
  25. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/errors.py +0 -0
  26. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/fspaths.py +0 -0
  27. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/inventory.py +0 -0
  28. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/main.py +0 -0
  29. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/migrations.py +0 -0
  30. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/pipelining.py +0 -0
  31. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/__init__.py +0 -0
  32. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/debian.py +0 -0
  33. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/factory.py +0 -0
  34. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/openbsd.py +0 -0
  35. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/providers/redhat.py +0 -0
  36. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/repl.py +0 -0
  37. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/reporting.py +0 -0
  38. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/schema/__init__.py +0 -0
  39. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/security.py +0 -0
  40. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/setup/__init__.py +0 -0
  41. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/setup/detect.py +0 -0
  42. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.html.j2 +0 -0
  43. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.md.j2 +0 -0
  44. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/templates/report.txt.j2 +0 -0
  45. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/__init__.py +0 -0
  46. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/app.py +0 -0
  47. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/context.py +0 -0
  48. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/dashboard.py +0 -0
  49. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/elements.py +0 -0
  50. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/inventory.py +0 -0
  51. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/logs.py +0 -0
  52. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/messages.py +0 -0
  53. {exosphere_cli-2.2.0 → exosphere_cli-2.4.0}/src/exosphere/ui/style.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: exosphere-cli
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: CLI/TUI driven patch reporting for remote Unix-like systems.
5
5
  Author: Alexandre Gauthier
6
6
  Author-email: Alexandre Gauthier <alex@underwares.org>
@@ -30,8 +30,8 @@ Requires-Dist: rich>=14.1.0
30
30
  Requires-Dist: textual-serve>=1.1.3 ; extra == 'web'
31
31
  Requires-Python: >=3.13
32
32
  Project-URL: homepage, https://exosphere.readthedocs.io
33
- Project-URL: issues, https://github.com/mrdaemon/exosphere/issues
34
33
  Project-URL: repository, https://github.com/mrdaemon/exosphere
34
+ Project-URL: issues, https://github.com/mrdaemon/exosphere/issues
35
35
  Provides-Extra: web
36
36
  Description-Content-Type: text/markdown
37
37
 
@@ -70,6 +70,7 @@ or see [the documentation](https://exosphere.readthedocs.io/en/stable/) to get s
70
70
  - See everything in one spot, at a glance, without complex automation or enterprise
71
71
  solutions
72
72
  - Does not require Python (or anything else) to be installed on remote systems
73
+ - Parallel operations across hosts with optional SSH pipelining
73
74
  - Document based reporting in HTML, text or markdown format
74
75
  - JSON output for integration with other tools
75
76
 
@@ -33,6 +33,7 @@ or see [the documentation](https://exosphere.readthedocs.io/en/stable/) to get s
33
33
  - See everything in one spot, at a glance, without complex automation or enterprise
34
34
  solutions
35
35
  - Does not require Python (or anything else) to be installed on remote systems
36
+ - Parallel operations across hosts with optional SSH pipelining
36
37
  - Document based reporting in HTML, text or markdown format
37
38
  - JSON output for integration with other tools
38
39
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "exosphere-cli"
3
- version = "2.2.0"
3
+ version = "2.4.0"
4
4
  description = "CLI/TUI driven patch reporting for remote Unix-like systems."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -50,7 +50,7 @@ dev = [
50
50
  "pytest-json-ctrf>=0.3.5",
51
51
  "pytest-mock>=3.14.1",
52
52
  "renku-sphinx-theme>=0.5.0",
53
- "ruff>=0.11.5",
53
+ "ruff>=0.15.0",
54
54
  "sphinx>=8.2.3,<9.0",
55
55
  "sphinx-autobuild>=2024.10.3",
56
56
  "sphinx-lint>=1.0.0",
@@ -74,7 +74,7 @@ issues = "https://github.com/mrdaemon/exosphere/issues"
74
74
  exosphere = "exosphere.main:main"
75
75
 
76
76
  [build-system]
77
- requires = ["uv_build>=0.7.19,<0.10.0"]
77
+ requires = ["uv_build>=0.7.19,<0.11.0"]
78
78
  build-backend = "uv_build"
79
79
 
80
80
  [tool.uv.build-backend]
@@ -205,6 +205,7 @@ class Host:
205
205
  "flavor": self.flavor,
206
206
  "version": self.version,
207
207
  "supported": self.supported,
208
+ "stale": self.is_stale,
208
209
  "online": self.online,
209
210
  "package_manager": self.package_manager,
210
211
  "updates": [update.__dict__.copy() for update in self.updates],
@@ -6,13 +6,23 @@ well as helper functions and decorators to be used by package manager
6
6
  provider implementations.
7
7
  """
8
8
 
9
+ import functools
9
10
  import logging
10
11
  from abc import ABC, abstractmethod
11
12
  from collections.abc import Callable
12
13
 
13
14
  from fabric import Connection
15
+ from invoke.exceptions import AuthFailure
14
16
 
15
17
  from exosphere.data import Update
18
+ from exosphere.errors import DataRefreshError
19
+
20
+ _SUDO_AUTH_FAILURE_MESSAGE = (
21
+ "Sudo failed: "
22
+ "Ensure the user is configured with passwordless sudo. "
23
+ "You can use 'exosphere sudo generate' to produce a sudoers snippet for this host. "
24
+ "See: https://exosphere.readthedocs.io/en/stable/connections.html#id1"
25
+ )
16
26
 
17
27
 
18
28
  def requires_sudo(func: Callable) -> Callable:
@@ -23,9 +33,21 @@ def requires_sudo(func: Callable) -> Callable:
23
33
  it requires sudo privileges to execute. You should add it to any
24
34
  method that requires elevated privileges, i.e. whenever you are
25
35
  using 'cx.sudo()' instead of 'cx.run()'.
36
+
37
+ Additionally, the decorator provides enhanced error handling for
38
+ sudo related failures, presenting a clear message about sudo policies
39
+ and sudoers configuration when an AuthFailure or related exception occurs.
26
40
  """
27
- setattr(func, "__requires_sudo", True)
28
- return func
41
+
42
+ @functools.wraps(func)
43
+ def wrapper(*args: object, **kwargs: object) -> object:
44
+ try:
45
+ return func(*args, **kwargs)
46
+ except AuthFailure as e:
47
+ raise DataRefreshError(_SUDO_AUTH_FAILURE_MESSAGE) from e
48
+
49
+ setattr(wrapper, "__requires_sudo", True)
50
+ return wrapper
29
51
 
30
52
 
31
53
  class PkgManager(ABC):
@@ -26,6 +26,7 @@ class Pkg(PkgManager):
26
26
 
27
27
  SUDOERS_COMMANDS: list[str] | None = [
28
28
  "/usr/sbin/pkg update -q",
29
+ "/usr/sbin/pkg audit -qF",
29
30
  ]
30
31
 
31
32
  def __init__(self) -> None:
@@ -41,7 +42,11 @@ class Pkg(PkgManager):
41
42
  """
42
43
  Synchronize the package repository.
43
44
 
44
- This method is equivalent to running 'pkg update -q'.
45
+ It will also download the latest version of the vuln.xml
46
+ database to check for security updates.
47
+
48
+ This method is equivalent to running 'pkg update -q', followed
49
+ by 'pkg audit -qF'.
45
50
 
46
51
  :param cx: Fabric Connection object
47
52
  :return: True if synchronization is successful, False otherwise.
@@ -56,6 +61,19 @@ class Pkg(PkgManager):
56
61
  )
57
62
  return False
58
63
 
64
+ # Sync vulnerability data via pkg audit -qF, which forces a download
65
+ # of the vuln.xml database. We do not store or parse the output, as
66
+ # the cost of running it a second time in get_updates() is negligible,
67
+ # and will be a local operation anyways.
68
+ self.logger.debug("Synchronizing vuln.xml via pkg audit")
69
+ audit_result = cx.sudo("/usr/sbin/pkg audit -qF", hide=True, warn=True)
70
+
71
+ if audit_result.failed and audit_result.stderr:
72
+ self.logger.error(
73
+ f"Failed to synchronize vuln.xml via pkg audit: {audit_result.stderr}"
74
+ )
75
+ return False
76
+
59
77
  self.logger.debug("FreeBSD pkg repositories synchronized successfully")
60
78
 
61
79
  return True
@@ -19,6 +19,7 @@
19
19
  "flavor",
20
20
  "version",
21
21
  "supported",
22
+ "stale",
22
23
  "online",
23
24
  "package_manager",
24
25
  "updates",
@@ -59,6 +60,10 @@
59
60
  "type": "boolean",
60
61
  "description": "Whether this host type is supported by Exosphere"
61
62
  },
63
+ "stale": {
64
+ "type": "boolean",
65
+ "description": "Whether the host data is stale and needs refreshed"
66
+ },
62
67
  "online": {
63
68
  "type": "boolean",
64
69
  "description": "Whether the host was last reachable"
File without changes