exosphere-cli 3.0.0__tar.gz → 3.0.1__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.
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/PKG-INFO +9 -5
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/README.md +8 -4
- exosphere_cli-3.0.1/pyproject.toml +131 -0
- exosphere_cli-3.0.0/pyproject.toml → exosphere_cli-3.0.1/pyproject.toml.orig +2 -2
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/config.py +4 -4
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/host.py +11 -10
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/inventory.py +31 -28
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/report.py +7 -7
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/utils.py +4 -3
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/version.py +4 -1
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/config.py +33 -10
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/data.py +4 -4
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/editing.py +4 -1
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/fspaths.py +1 -1
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/inventory.py +12 -6
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/main.py +14 -7
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/migrations.py +12 -9
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/objects.py +97 -21
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/pipelining.py +3 -3
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/api.py +2 -2
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/debian.py +2 -1
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/factory.py +5 -3
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/freebsd.py +9 -9
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/redhat.py +11 -12
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/repl.py +14 -3
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/reporting.py +2 -2
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/app.py +18 -11
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/dashboard.py +15 -10
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/elements.py +5 -3
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/inventory.py +15 -10
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/logs.py +11 -9
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/COPYRIGHT +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/LICENSE +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/LICENSES/Apache-2.0.txt +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/LICENSES/CC-BY-4.0.txt +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/LICENSES/MIT.txt +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/LICENSES/OFL-1.1.txt +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/cli.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/connections.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/sudo.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/commands/ui.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/context.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/database.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/errors.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/providers/openbsd.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/runners.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/schema/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/schema/host-report.schema.json +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/security.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/setup/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/setup/detect.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/templates/report.html.j2 +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/templates/report.md.j2 +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/templates/report.txt.j2 +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/__init__.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/context.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/messages.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/palette.py +0 -0
- {exosphere_cli-3.0.0 → exosphere_cli-3.0.1}/src/exosphere/ui/style.tcss +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: exosphere-cli
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.1
|
|
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>
|
|
@@ -149,7 +149,6 @@ uv run textual run --dev -c exosphere ui
|
|
|
149
149
|
Congratulations! Editing any of the `.tcss` files in the `ui/` directory will
|
|
150
150
|
reflect changes immediately.
|
|
151
151
|
|
|
152
|
-
|
|
153
152
|
### Documentation Editing Quick Start
|
|
154
153
|
|
|
155
154
|
To edit the documentation, you can use the following commands:
|
|
@@ -188,8 +187,10 @@ tasks are defined in the `pyproject.toml` file under the `[tool.poe.tasks]` tabl
|
|
|
188
187
|
|
|
189
188
|
| path | description |
|
|
190
189
|
| ---- | ----------- |
|
|
190
|
+
| `LICENSES/` | Full text of every license used by the project and its assets |
|
|
191
191
|
| `docs/` | Sphinx documentation source tree |
|
|
192
192
|
| `docs/source/_ext/` | Custom Sphinx extensions for the project |
|
|
193
|
+
| `docs/source/changelog/` | Per-release changelog entries (markdown) and their assets |
|
|
193
194
|
| `examples/` | Example configuration files and reports |
|
|
194
195
|
| `scripts/` | Utilitarian scripts for dev and maintenance |
|
|
195
196
|
| `src/` | Main source code for the application |
|
|
@@ -224,13 +225,16 @@ Paths below are relative to `src/exosphere/` unless otherwise noted.
|
|
|
224
225
|
| `context.py` | Context management for shared state across commands and UI |
|
|
225
226
|
| `data.py` | Data models and structures for serialization and exchange |
|
|
226
227
|
| `database.py` | Cache system for serialization |
|
|
228
|
+
| `editing.py` | External editor helper module, shared between CLI and UI |
|
|
227
229
|
| `errors.py` | Exception classes and general error messages |
|
|
230
|
+
| `fspaths.py` | Platform-appropriate filesystem paths module, for state |
|
|
228
231
|
| `inventory.py` | Inventory management subsystem |
|
|
229
232
|
| `migrations.py` | Cache format migration processes |
|
|
230
233
|
| `objects.py` | Main objects for representing Hosts, and most of the relevant logic |
|
|
231
234
|
| `pipelining.py` | SSH pipelining implementation, including reaper thread |
|
|
232
235
|
| `repl.py` | REPL module for interactive CLI usage |
|
|
233
236
|
| `reporting.py` | Reporting subsystem, including templates and formatters |
|
|
237
|
+
| `runners.py` | SSH runners module, handles setting up remote POSIX environment |
|
|
234
238
|
| `security.py` | Sudo management subsystem, including policy and utilities |
|
|
235
239
|
|
|
236
240
|
Generally, most of the things Exosphere does to hosts (including connection management
|
|
@@ -245,9 +249,9 @@ Paths below are relative to `src/exosphere/` unless otherwise noted.
|
|
|
245
249
|
| `ui/app.py` | Main Textual application class and entry point for the UI |
|
|
246
250
|
| `ui/context.py` | UI Context management for shared state across UI components |
|
|
247
251
|
| `ui/elements.py` | Shared UI elements, including task runners |
|
|
248
|
-
| `ui/dashboard.py` | Dashboard
|
|
249
|
-
| `ui/inventory.py` | Inventory
|
|
250
|
-
| `ui/logs.py` | Logs
|
|
252
|
+
| `ui/dashboard.py` | Dashboard screen implementation |
|
|
253
|
+
| `ui/inventory.py` | Inventory screen implementation |
|
|
254
|
+
| `ui/logs.py` | Logs screen implementation |
|
|
251
255
|
| `ui/messages.py` | Screen refresh and message passing system |
|
|
252
256
|
| `ui/palette.py` | Command palette providers and implementations |
|
|
253
257
|
|
|
@@ -108,7 +108,6 @@ uv run textual run --dev -c exosphere ui
|
|
|
108
108
|
Congratulations! Editing any of the `.tcss` files in the `ui/` directory will
|
|
109
109
|
reflect changes immediately.
|
|
110
110
|
|
|
111
|
-
|
|
112
111
|
### Documentation Editing Quick Start
|
|
113
112
|
|
|
114
113
|
To edit the documentation, you can use the following commands:
|
|
@@ -147,8 +146,10 @@ tasks are defined in the `pyproject.toml` file under the `[tool.poe.tasks]` tabl
|
|
|
147
146
|
|
|
148
147
|
| path | description |
|
|
149
148
|
| ---- | ----------- |
|
|
149
|
+
| `LICENSES/` | Full text of every license used by the project and its assets |
|
|
150
150
|
| `docs/` | Sphinx documentation source tree |
|
|
151
151
|
| `docs/source/_ext/` | Custom Sphinx extensions for the project |
|
|
152
|
+
| `docs/source/changelog/` | Per-release changelog entries (markdown) and their assets |
|
|
152
153
|
| `examples/` | Example configuration files and reports |
|
|
153
154
|
| `scripts/` | Utilitarian scripts for dev and maintenance |
|
|
154
155
|
| `src/` | Main source code for the application |
|
|
@@ -183,13 +184,16 @@ Paths below are relative to `src/exosphere/` unless otherwise noted.
|
|
|
183
184
|
| `context.py` | Context management for shared state across commands and UI |
|
|
184
185
|
| `data.py` | Data models and structures for serialization and exchange |
|
|
185
186
|
| `database.py` | Cache system for serialization |
|
|
187
|
+
| `editing.py` | External editor helper module, shared between CLI and UI |
|
|
186
188
|
| `errors.py` | Exception classes and general error messages |
|
|
189
|
+
| `fspaths.py` | Platform-appropriate filesystem paths module, for state |
|
|
187
190
|
| `inventory.py` | Inventory management subsystem |
|
|
188
191
|
| `migrations.py` | Cache format migration processes |
|
|
189
192
|
| `objects.py` | Main objects for representing Hosts, and most of the relevant logic |
|
|
190
193
|
| `pipelining.py` | SSH pipelining implementation, including reaper thread |
|
|
191
194
|
| `repl.py` | REPL module for interactive CLI usage |
|
|
192
195
|
| `reporting.py` | Reporting subsystem, including templates and formatters |
|
|
196
|
+
| `runners.py` | SSH runners module, handles setting up remote POSIX environment |
|
|
193
197
|
| `security.py` | Sudo management subsystem, including policy and utilities |
|
|
194
198
|
|
|
195
199
|
Generally, most of the things Exosphere does to hosts (including connection management
|
|
@@ -204,9 +208,9 @@ Paths below are relative to `src/exosphere/` unless otherwise noted.
|
|
|
204
208
|
| `ui/app.py` | Main Textual application class and entry point for the UI |
|
|
205
209
|
| `ui/context.py` | UI Context management for shared state across UI components |
|
|
206
210
|
| `ui/elements.py` | Shared UI elements, including task runners |
|
|
207
|
-
| `ui/dashboard.py` | Dashboard
|
|
208
|
-
| `ui/inventory.py` | Inventory
|
|
209
|
-
| `ui/logs.py` | Logs
|
|
211
|
+
| `ui/dashboard.py` | Dashboard screen implementation |
|
|
212
|
+
| `ui/inventory.py` | Inventory screen implementation |
|
|
213
|
+
| `ui/logs.py` | Logs screen implementation |
|
|
210
214
|
| `ui/messages.py` | Screen refresh and message passing system |
|
|
211
215
|
| `ui/palette.py` | Command palette providers and implementations |
|
|
212
216
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "exosphere-cli"
|
|
3
|
+
version = "3.0.1"
|
|
4
|
+
description = "CLI/TUI driven patch reporting for remote Unix-like systems."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
classifiers = [
|
|
8
|
+
"Development Status :: 5 - Production/Stable",
|
|
9
|
+
"Environment :: Console",
|
|
10
|
+
"Intended Audience :: System Administrators",
|
|
11
|
+
"Natural Language :: English",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Programming Language :: Python :: 3.13",
|
|
14
|
+
"Programming Language :: Python :: 3.14",
|
|
15
|
+
"Topic :: System :: Monitoring",
|
|
16
|
+
"Topic :: System :: Systems Administration",
|
|
17
|
+
"Topic :: Utilities",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
license = "MIT"
|
|
21
|
+
license-files = [
|
|
22
|
+
"LICENSE",
|
|
23
|
+
"LICENSES/*",
|
|
24
|
+
"COPYRIGHT",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"fabric>=3.2.2",
|
|
28
|
+
"textual>=6.7.0",
|
|
29
|
+
"pyyaml>=6.0.3",
|
|
30
|
+
"platformdirs>=4.3.8",
|
|
31
|
+
"prompt-toolkit>=3.0.51",
|
|
32
|
+
"jinja2>=3.1.6",
|
|
33
|
+
"packaging>=24.0",
|
|
34
|
+
"rich>=14.1.0",
|
|
35
|
+
"cyclopts>=4.16.1",
|
|
36
|
+
"filelock>=3.29.4",
|
|
37
|
+
"pydantic>=2.12",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[[project.authors]]
|
|
41
|
+
name = "Alexandre Gauthier"
|
|
42
|
+
email = "alex@underwares.org"
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
homepage = "https://exosphere.readthedocs.io"
|
|
46
|
+
repository = "https://github.com/mrdaemon/exosphere"
|
|
47
|
+
issues = "https://github.com/mrdaemon/exosphere/issues"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
exosphere = "exosphere.main:main"
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"fabric[pytest]>=3.2.2",
|
|
55
|
+
"jsonschema>=4.25.1",
|
|
56
|
+
"poethepoet>=0.33.1",
|
|
57
|
+
"pyright>=1.1.400",
|
|
58
|
+
"pytest>=8.4.2",
|
|
59
|
+
"pytest-asyncio>=1.2.0",
|
|
60
|
+
"pytest-cov>=6.1.1",
|
|
61
|
+
"pytest-json-ctrf>=0.3.5",
|
|
62
|
+
"pytest-mock>=3.14.1",
|
|
63
|
+
"sphinx-rtd-theme>=3.0.2",
|
|
64
|
+
"ruff>=0.15.0",
|
|
65
|
+
"sphinx>=8.2.3",
|
|
66
|
+
"sphinx-autobuild>=2024.10.3",
|
|
67
|
+
"sphinx-lint>=1.0.0",
|
|
68
|
+
"sphinx-tabs>=3.4.7",
|
|
69
|
+
"sphinxcontrib-spelling>=8.0.2",
|
|
70
|
+
"textual-dev>=1.7.0",
|
|
71
|
+
"codespell>=2.4.0",
|
|
72
|
+
"myst-parser[linkify]>=4.0.0",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[build-system]
|
|
76
|
+
requires = ["uv_build>=0.7.19,<0.13.0"]
|
|
77
|
+
build-backend = "uv_build"
|
|
78
|
+
|
|
79
|
+
[tool.uv.build-backend]
|
|
80
|
+
module-name = "exosphere"
|
|
81
|
+
|
|
82
|
+
[tool.pytest.ini_options]
|
|
83
|
+
testpaths = ["tests"]
|
|
84
|
+
|
|
85
|
+
[tool.codespell]
|
|
86
|
+
skip = "*.lock,*.woff,*.woff2,*.ttf,*.ttx,*.svg,*.png,*.gif,*.html,*.htm,docs/source/_static"
|
|
87
|
+
|
|
88
|
+
[tool.poe.tasks]
|
|
89
|
+
lint = "ruff check src tests scripts"
|
|
90
|
+
isort = "ruff check --select I --fix src tests scripts"
|
|
91
|
+
isort-check = "ruff check --select I src tests scripts --diff"
|
|
92
|
+
typecheck = "pyright src tests scripts"
|
|
93
|
+
ruff-format = "ruff format src tests scripts"
|
|
94
|
+
ruff-format-check = "ruff format --check src tests scripts --diff"
|
|
95
|
+
spellcheck = "codespell src tests scripts docs/source README.md"
|
|
96
|
+
spellcheck-fix = "codespell --write-changes src tests scripts docs/source README.md"
|
|
97
|
+
format = [
|
|
98
|
+
"ruff-format",
|
|
99
|
+
"isort",
|
|
100
|
+
]
|
|
101
|
+
format-check = [
|
|
102
|
+
"ruff-format-check",
|
|
103
|
+
"isort-check",
|
|
104
|
+
]
|
|
105
|
+
check = [
|
|
106
|
+
"format-check",
|
|
107
|
+
"lint",
|
|
108
|
+
"typecheck",
|
|
109
|
+
"spellcheck",
|
|
110
|
+
]
|
|
111
|
+
test = "pytest -v --ctrf .tests_report.json"
|
|
112
|
+
coverage = "pytest --cov-report term-missing --cov-report html --cov=src tests/"
|
|
113
|
+
docs-build = "sphinx-build -b html -W --keep-going docs/source docs/build/html"
|
|
114
|
+
docs-lint = "sphinx-lint docs/source"
|
|
115
|
+
docs-spellcheck = "sphinx-build -b spelling -W --keep-going docs/source docs/build"
|
|
116
|
+
docs-linkcheck = "sphinx-build -b linkcheck docs/source docs/build/linkcheck"
|
|
117
|
+
docs = [
|
|
118
|
+
"docs-lint",
|
|
119
|
+
"docs-spellcheck",
|
|
120
|
+
"docs-build",
|
|
121
|
+
]
|
|
122
|
+
docs-serve = "sphinx-autobuild -b html docs/source docs/build/html --port 8000 --open-browser"
|
|
123
|
+
docs-mkreports = "python scripts/generate_example_reports.py"
|
|
124
|
+
demo = "vhs scripts/demo.tape"
|
|
125
|
+
release-preflight = "python scripts/release_preflight.py"
|
|
126
|
+
release-check = [
|
|
127
|
+
"check",
|
|
128
|
+
"test",
|
|
129
|
+
"docs",
|
|
130
|
+
"release-preflight",
|
|
131
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "exosphere-cli"
|
|
3
|
-
version = "3.0.
|
|
3
|
+
version = "3.0.1"
|
|
4
4
|
description = "CLI/TUI driven patch reporting for remote Unix-like systems."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -73,7 +73,7 @@ issues = "https://github.com/mrdaemon/exosphere/issues"
|
|
|
73
73
|
exosphere = "exosphere.main:main"
|
|
74
74
|
|
|
75
75
|
[build-system]
|
|
76
|
-
requires = ["uv_build>=0.7.19,<0.
|
|
76
|
+
requires = ["uv_build>=0.7.19,<0.13.0"]
|
|
77
77
|
build-backend = "uv_build"
|
|
78
78
|
|
|
79
79
|
[tool.uv.build-backend]
|
|
@@ -40,7 +40,7 @@ def show(
|
|
|
40
40
|
Displays the current configuration options, or the value of a specific option
|
|
41
41
|
if specified.
|
|
42
42
|
|
|
43
|
-
If
|
|
43
|
+
If ``--full`` is specified, it will show the entire configuration structure,
|
|
44
44
|
including the inventory, beyond just the "options" section.
|
|
45
45
|
|
|
46
46
|
Parameters
|
|
@@ -150,9 +150,9 @@ def diff(
|
|
|
150
150
|
file can exclusively contain the options you want to change.
|
|
151
151
|
|
|
152
152
|
This command allows you to see exactly what has been changed, optionally
|
|
153
|
-
in its context, using the
|
|
153
|
+
in its context, using the ``--full`` option.
|
|
154
154
|
|
|
155
|
-
For a full config dump, use the
|
|
155
|
+
For a full config dump, use the ``show`` command instead.
|
|
156
156
|
|
|
157
157
|
Parameters
|
|
158
158
|
----------
|
|
@@ -264,7 +264,7 @@ def edit(
|
|
|
264
264
|
|
|
265
265
|
try:
|
|
266
266
|
validate_config(target)
|
|
267
|
-
except
|
|
267
|
+
except (ValueError, OSError) as e:
|
|
268
268
|
err_console.print(f"[red]Configuration is invalid:[/red]\n{e}")
|
|
269
269
|
if Confirm.ask("Re-open editor to fix?", default=True):
|
|
270
270
|
continue
|
|
@@ -22,6 +22,7 @@ from exosphere.commands.utils import (
|
|
|
22
22
|
err_console,
|
|
23
23
|
save_inventory_state,
|
|
24
24
|
)
|
|
25
|
+
from exosphere.errors import DataRefreshError
|
|
25
26
|
from exosphere.objects import Host
|
|
26
27
|
|
|
27
28
|
# Simple spinner layout for long or pre-tasks
|
|
@@ -232,10 +233,10 @@ def discover(host: HostArg, /) -> int:
|
|
|
232
233
|
progress.add_task(f"Discovering platform for '{host.name}'", total=None)
|
|
233
234
|
try:
|
|
234
235
|
host.discover()
|
|
235
|
-
except
|
|
236
|
+
except DataRefreshError as e:
|
|
236
237
|
progress.console.print(
|
|
237
238
|
Panel.fit(
|
|
238
|
-
f"{
|
|
239
|
+
f"{e!s}",
|
|
239
240
|
title="[red]Error[/red]",
|
|
240
241
|
style="red",
|
|
241
242
|
title_align="left",
|
|
@@ -265,7 +266,7 @@ def refresh(
|
|
|
265
266
|
This command retrieves the host by name from the inventory
|
|
266
267
|
and refreshes its state and available updates.
|
|
267
268
|
|
|
268
|
-
If
|
|
269
|
+
If ``--sync`` is specified, the package repositories will also be
|
|
269
270
|
synchronized remotely.
|
|
270
271
|
|
|
271
272
|
Parameters
|
|
@@ -277,17 +278,17 @@ def refresh(
|
|
|
277
278
|
discover
|
|
278
279
|
Also refresh platform information
|
|
279
280
|
"""
|
|
280
|
-
with Progress(transient=True
|
|
281
|
+
with Progress(*SPINNER_ARGS, transient=True) as progress:
|
|
281
282
|
if discover:
|
|
282
283
|
task = progress.add_task(
|
|
283
284
|
f"Refreshing platform information for '{host.name}'", total=None
|
|
284
285
|
)
|
|
285
286
|
try:
|
|
286
287
|
host.discover()
|
|
287
|
-
except
|
|
288
|
+
except DataRefreshError as e:
|
|
288
289
|
progress.console.print(
|
|
289
290
|
Panel.fit(
|
|
290
|
-
f"{
|
|
291
|
+
f"{e!s}",
|
|
291
292
|
title="[red]Error[/red]",
|
|
292
293
|
style="red",
|
|
293
294
|
title_align="left",
|
|
@@ -304,10 +305,10 @@ def refresh(
|
|
|
304
305
|
)
|
|
305
306
|
try:
|
|
306
307
|
host.sync_repos()
|
|
307
|
-
except
|
|
308
|
+
except DataRefreshError as e:
|
|
308
309
|
progress.console.print(
|
|
309
310
|
Panel.fit(
|
|
310
|
-
f"{
|
|
311
|
+
f"{e!s}",
|
|
311
312
|
title="[red]Error[/red]",
|
|
312
313
|
style="red",
|
|
313
314
|
title_align="left",
|
|
@@ -321,10 +322,10 @@ def refresh(
|
|
|
321
322
|
task = progress.add_task(f"Refreshing updates for '{host.name}'", total=None)
|
|
322
323
|
try:
|
|
323
324
|
host.refresh_updates()
|
|
324
|
-
except
|
|
325
|
+
except DataRefreshError as e:
|
|
325
326
|
progress.console.print(
|
|
326
327
|
Panel.fit(
|
|
327
|
-
f"{
|
|
328
|
+
f"{e!s}",
|
|
328
329
|
title="[red]Error[/red]",
|
|
329
330
|
style="red",
|
|
330
331
|
title_align="left",
|
|
@@ -27,7 +27,7 @@ from exosphere.commands.utils import (
|
|
|
27
27
|
save_inventory_state,
|
|
28
28
|
)
|
|
29
29
|
from exosphere.inventory import FilterMode, Inventory, SortField
|
|
30
|
-
from exosphere.objects import HostOperation
|
|
30
|
+
from exosphere.objects import Host, HostOperation
|
|
31
31
|
|
|
32
32
|
# Constants for display
|
|
33
33
|
ERROR_STYLE = {
|
|
@@ -122,11 +122,11 @@ def refresh(
|
|
|
122
122
|
Connects to hosts in the inventory and retrieves pending package
|
|
123
123
|
updates.
|
|
124
124
|
|
|
125
|
-
If
|
|
125
|
+
If ``--discover`` is specified, the platform information (Operating
|
|
126
126
|
System flavor, version, package manager) will also be refreshed.
|
|
127
127
|
Also refreshes the online status in the process.
|
|
128
128
|
|
|
129
|
-
If
|
|
129
|
+
If ``--sync`` is specified, the package repositories will also be
|
|
130
130
|
synchronized remotely.
|
|
131
131
|
|
|
132
132
|
Synchronizing the package repositories involves invoking whatever mechanism
|
|
@@ -135,7 +135,7 @@ def refresh(
|
|
|
135
135
|
with a handful of slow hosts.
|
|
136
136
|
|
|
137
137
|
By default, only the progress bar is shown during the operation.
|
|
138
|
-
If
|
|
138
|
+
If ``--verbose`` is specified, the name and completion status of each host
|
|
139
139
|
will be shown in real time.
|
|
140
140
|
|
|
141
141
|
Parameters
|
|
@@ -274,7 +274,7 @@ def ping(*names: HostArg) -> int:
|
|
|
274
274
|
error_count += 1
|
|
275
275
|
if exc:
|
|
276
276
|
progress.console.print(
|
|
277
|
-
f" Host [bold]{host.name}[/bold]: [bold red]ERROR[/bold red] - {
|
|
277
|
+
f" Host [bold]{host.name}[/bold]: [bold red]ERROR[/bold red] - {exc!s}",
|
|
278
278
|
)
|
|
279
279
|
else:
|
|
280
280
|
progress.console.print(
|
|
@@ -342,18 +342,18 @@ def status(
|
|
|
342
342
|
online status and whether or not the data is stale.
|
|
343
343
|
|
|
344
344
|
Output can be filtered to show only hosts with pending updates
|
|
345
|
-
(
|
|
346
|
-
(
|
|
345
|
+
(``--updates-only``) or only those with pending security updates
|
|
346
|
+
(``--security-only``). These two filters are mutually exclusive.
|
|
347
347
|
|
|
348
|
-
Output can also be sorted by any column with
|
|
349
|
-
reversed with
|
|
348
|
+
Output can also be sorted by any column with ``--sort``, optionally
|
|
349
|
+
reversed with ``--reverse``. Sorting by 'version' groups hosts by
|
|
350
350
|
flavor first, since versions are not comparable across flavors.
|
|
351
351
|
|
|
352
352
|
When sorting by any column other than name, hosts with unknown
|
|
353
353
|
or unsupported values for that column will be grouped together at the
|
|
354
354
|
end.
|
|
355
355
|
|
|
356
|
-
Use
|
|
356
|
+
Use ``--full`` to include extra columns, such as the host description.
|
|
357
357
|
|
|
358
358
|
No matches when filtering will exit with code 3.
|
|
359
359
|
|
|
@@ -368,7 +368,7 @@ def status(
|
|
|
368
368
|
sort
|
|
369
369
|
Sort the table by the given column
|
|
370
370
|
reverse
|
|
371
|
-
Reverse the sort order (requires
|
|
371
|
+
Reverse the sort order (requires ``--sort``)
|
|
372
372
|
full
|
|
373
373
|
Show additional columns, including host descriptions
|
|
374
374
|
"""
|
|
@@ -420,12 +420,22 @@ def status(
|
|
|
420
420
|
caption_justify="right",
|
|
421
421
|
)
|
|
422
422
|
|
|
423
|
+
def get_platform_value(host: Host, value: str | None) -> str:
|
|
424
|
+
"""
|
|
425
|
+
Retrieve display value for host platform.
|
|
426
|
+
Will return appropriate placeholders as fallback.
|
|
427
|
+
"""
|
|
428
|
+
if value:
|
|
429
|
+
return value
|
|
430
|
+
elif not host.supported:
|
|
431
|
+
return "[dim](unsupported)[/dim]"
|
|
432
|
+
else:
|
|
433
|
+
return "[dim](undiscovered)[/dim]"
|
|
434
|
+
|
|
423
435
|
for host in hosts:
|
|
424
|
-
# Prepare some rendering data for suffixes
|
|
436
|
+
# Prepare some rendering data for suffixes
|
|
425
437
|
stale_suffix = " [dim]*[/dim]" if host.is_stale else ""
|
|
426
438
|
reboot_suffix = " [red]![/red]" if host.needs_reboot else ""
|
|
427
|
-
undiscovered_status = "[dim](undiscovered)[/dim]"
|
|
428
|
-
unsupported_status = "[dim](unsupported)[/dim]"
|
|
429
439
|
empty_placeholder = "[dim]—[/dim]"
|
|
430
440
|
|
|
431
441
|
# Prepare table row data
|
|
@@ -446,22 +456,12 @@ def status(
|
|
|
446
456
|
"[bold green]Online[/bold green]" if host.online else "[red]Offline[/red]"
|
|
447
457
|
) + reboot_suffix
|
|
448
458
|
|
|
449
|
-
# Helper function to get platform info with appropriate
|
|
450
|
-
# handling for unsupported and undiscovered hosts
|
|
451
|
-
def get_platform_info(value):
|
|
452
|
-
if value:
|
|
453
|
-
return value
|
|
454
|
-
elif not host.supported:
|
|
455
|
-
return unsupported_status
|
|
456
|
-
else:
|
|
457
|
-
return undiscovered_status
|
|
458
|
-
|
|
459
459
|
# Construct table row for host
|
|
460
460
|
row = [
|
|
461
461
|
host.name,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
get_platform_value(host, host.os),
|
|
463
|
+
get_platform_value(host, host.flavor),
|
|
464
|
+
get_platform_value(host, host.version),
|
|
465
465
|
updates,
|
|
466
466
|
security_updates,
|
|
467
467
|
online_status,
|
|
@@ -537,7 +537,10 @@ def clear(
|
|
|
537
537
|
|
|
538
538
|
try:
|
|
539
539
|
inventory.clear_state()
|
|
540
|
-
except Exception as e:
|
|
540
|
+
except Exception as e: # noqa: BLE001
|
|
541
|
+
# clear_state wraps cache errors as RuntimeError, but also
|
|
542
|
+
# re-runs init_all() afterwards, which reopens the full config
|
|
543
|
+
# and host construction surfaces, so this is broad on purpose.
|
|
541
544
|
err_console.print(
|
|
542
545
|
Panel.fit(
|
|
543
546
|
f"[bold red]Error clearing inventory state:[/bold red] {e}",
|
|
@@ -102,8 +102,8 @@ def generate(
|
|
|
102
102
|
the report will include all hosts in the inventory.
|
|
103
103
|
|
|
104
104
|
It can further be narrowed to hosts with pending updates
|
|
105
|
-
(
|
|
106
|
-
(
|
|
105
|
+
(``--updates-only``) or pending security updates
|
|
106
|
+
(``--security-updates-only``), which are mutually exclusive.
|
|
107
107
|
|
|
108
108
|
Note: Undiscovered or unsupported hosts are excluded from the report.
|
|
109
109
|
|
|
@@ -120,7 +120,7 @@ def generate(
|
|
|
120
120
|
security_only
|
|
121
121
|
Only report security updates
|
|
122
122
|
tee
|
|
123
|
-
Also print report to stdout (requires
|
|
123
|
+
Also print report to stdout (requires ``--output``)
|
|
124
124
|
quiet
|
|
125
125
|
Suppress informational messages
|
|
126
126
|
navigation
|
|
@@ -200,7 +200,7 @@ def generate(
|
|
|
200
200
|
if output:
|
|
201
201
|
try:
|
|
202
202
|
output.write_text(content, encoding="utf-8")
|
|
203
|
-
except
|
|
203
|
+
except (OSError, UnicodeEncodeError) as e:
|
|
204
204
|
err_console.print(f"[red]Failed to write to {output}: {e}[/red]")
|
|
205
205
|
return 2 # Application error
|
|
206
206
|
|
|
@@ -241,14 +241,14 @@ def schema(
|
|
|
241
241
|
Show or write the JSON Schema for the current version of Exosphere
|
|
242
242
|
|
|
243
243
|
Emits the JSON Schema (draft-07) describing the structure produced
|
|
244
|
-
by
|
|
244
|
+
by ``report generate --format json``, for the currently running
|
|
245
245
|
version of Exosphere.
|
|
246
246
|
|
|
247
247
|
This allows anyone to easily get an overview of the structure,
|
|
248
248
|
validate and integrate, offline, without a local source tree or
|
|
249
249
|
access to the online documentation.
|
|
250
250
|
|
|
251
|
-
By default the schema is printed to stdout. Use
|
|
251
|
+
By default the schema is printed to stdout. Use ``--output`` to
|
|
252
252
|
write it to a file instead.
|
|
253
253
|
|
|
254
254
|
Parameters
|
|
@@ -267,7 +267,7 @@ def schema(
|
|
|
267
267
|
if output:
|
|
268
268
|
try:
|
|
269
269
|
output.write_text(content, encoding="utf-8")
|
|
270
|
-
except
|
|
270
|
+
except (OSError, UnicodeEncodeError) as e:
|
|
271
271
|
err_console.print(f"[red]Failed to write to {output}: {e}[/red]")
|
|
272
272
|
return 2 # Application error
|
|
273
273
|
|
|
@@ -358,7 +358,7 @@ def run_task_with_progress(
|
|
|
358
358
|
else:
|
|
359
359
|
skipped = []
|
|
360
360
|
|
|
361
|
-
with Progress(transient=transient
|
|
361
|
+
with Progress(*progress_args, transient=transient) as progress:
|
|
362
362
|
task = progress.add_task(task_description, total=len(hosts))
|
|
363
363
|
|
|
364
364
|
# Surface skipped hosts up front, if any. It is easier to do
|
|
@@ -381,7 +381,7 @@ def run_task_with_progress(
|
|
|
381
381
|
if exc:
|
|
382
382
|
if immediate_error_display:
|
|
383
383
|
progress.console.print(
|
|
384
|
-
f"{operation.label}: [red]{
|
|
384
|
+
f"{operation.label}: [red]{exc!s}[/red]",
|
|
385
385
|
)
|
|
386
386
|
|
|
387
387
|
if collect_errors:
|
|
@@ -418,7 +418,8 @@ def save_inventory_state() -> None:
|
|
|
418
418
|
try:
|
|
419
419
|
inventory.save_state()
|
|
420
420
|
progress.stop_task(task)
|
|
421
|
-
except Exception as e:
|
|
421
|
+
except Exception as e: # noqa: BLE001
|
|
422
|
+
# Persistence failure is terminal for the caller either way
|
|
422
423
|
logger.error("Error saving inventory: %s", e)
|
|
423
424
|
progress.stop_task(task)
|
|
424
425
|
progress.console.print(
|
|
@@ -131,6 +131,9 @@ def check(
|
|
|
131
131
|
f"[red]Error:[/red] Unexpected response from PyPI API (missing key: {e})"
|
|
132
132
|
)
|
|
133
133
|
return 2 # Application error
|
|
134
|
-
except Exception as e:
|
|
134
|
+
except Exception as e: # noqa: BLE001
|
|
135
|
+
# Tail catch for any unexpected failures, covers json garbage,
|
|
136
|
+
# network issues, or anything else under the sun.
|
|
137
|
+
# "Is there an update" shouldn't just raise at runtime.
|
|
135
138
|
err_console.print(f"[red]Error:[/red] Failed to check for updates: {e}")
|
|
136
139
|
return 2 # Application error
|