exosphere-cli 0.9.9__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 (36) hide show
  1. exosphere_cli-0.9.9/LICENSE +21 -0
  2. exosphere_cli-0.9.9/PKG-INFO +104 -0
  3. exosphere_cli-0.9.9/README.md +71 -0
  4. exosphere_cli-0.9.9/pyproject.toml +87 -0
  5. exosphere_cli-0.9.9/src/exosphere/__init__.py +14 -0
  6. exosphere_cli-0.9.9/src/exosphere/cli.py +129 -0
  7. exosphere_cli-0.9.9/src/exosphere/commands/__init__.py +0 -0
  8. exosphere_cli-0.9.9/src/exosphere/commands/config.py +163 -0
  9. exosphere_cli-0.9.9/src/exosphere/commands/host.py +317 -0
  10. exosphere_cli-0.9.9/src/exosphere/commands/inventory.py +518 -0
  11. exosphere_cli-0.9.9/src/exosphere/commands/ui.py +31 -0
  12. exosphere_cli-0.9.9/src/exosphere/compat/__init__.py +0 -0
  13. exosphere_cli-0.9.9/src/exosphere/compat/win32readline.py +134 -0
  14. exosphere_cli-0.9.9/src/exosphere/config.py +281 -0
  15. exosphere_cli-0.9.9/src/exosphere/context.py +4 -0
  16. exosphere_cli-0.9.9/src/exosphere/data.py +31 -0
  17. exosphere_cli-0.9.9/src/exosphere/database.py +33 -0
  18. exosphere_cli-0.9.9/src/exosphere/errors.py +27 -0
  19. exosphere_cli-0.9.9/src/exosphere/inventory.py +363 -0
  20. exosphere_cli-0.9.9/src/exosphere/main.py +172 -0
  21. exosphere_cli-0.9.9/src/exosphere/objects.py +361 -0
  22. exosphere_cli-0.9.9/src/exosphere/providers/__init__.py +11 -0
  23. exosphere_cli-0.9.9/src/exosphere/providers/api.py +64 -0
  24. exosphere_cli-0.9.9/src/exosphere/providers/debian.py +144 -0
  25. exosphere_cli-0.9.9/src/exosphere/providers/factory.py +29 -0
  26. exosphere_cli-0.9.9/src/exosphere/providers/freebsd.py +183 -0
  27. exosphere_cli-0.9.9/src/exosphere/providers/redhat.py +226 -0
  28. exosphere_cli-0.9.9/src/exosphere/setup/__init__.py +0 -0
  29. exosphere_cli-0.9.9/src/exosphere/setup/detect.py +236 -0
  30. exosphere_cli-0.9.9/src/exosphere/ui/__init__.py +0 -0
  31. exosphere_cli-0.9.9/src/exosphere/ui/app.py +55 -0
  32. exosphere_cli-0.9.9/src/exosphere/ui/dashboard.py +150 -0
  33. exosphere_cli-0.9.9/src/exosphere/ui/elements.py +183 -0
  34. exosphere_cli-0.9.9/src/exosphere/ui/inventory.py +391 -0
  35. exosphere_cli-0.9.9/src/exosphere/ui/logs.py +110 -0
  36. exosphere_cli-0.9.9/src/exosphere/ui/style.tcss +160 -0
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2025 Alexandre Gauthier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: exosphere-cli
3
+ Version: 0.9.9
4
+ Summary: A simple centralized patch reporting tool for unix systems
5
+ Author: Alexandre Gauthier
6
+ Author-email: Alexandre Gauthier <alex@underwares.org>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: System :: Monitoring
17
+ Classifier: Topic :: System :: Systems Administration
18
+ Classifier: Topic :: Utilities
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: click-shell[readline]>=2.1 ; sys_platform != 'win32'
21
+ Requires-Dist: click-shell>=2.1 ; sys_platform == 'win32'
22
+ Requires-Dist: fabric>=3.2.2
23
+ Requires-Dist: typer>=0.15.2
24
+ Requires-Dist: textual>=3.2.0
25
+ Requires-Dist: pyyaml>=6.0.2
26
+ Requires-Dist: textual-serve>=1.1.2
27
+ Requires-Dist: pyreadline3>=3.5.4 ; sys_platform == 'win32'
28
+ Requires-Python: >=3.13
29
+ Project-URL: homepage, https://github.com/mrdaemon/exosphere
30
+ Project-URL: issues, https://github.com/mrdaemon/exosphere/issues
31
+ Project-URL: repository, https://github.com/mrdaemon/exosphere
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Exosphere
35
+
36
+ [![Exosphere Test Suite](https://github.com/mrdaemon/exosphere/actions/workflows/exosphere-test.yml/badge.svg)](https://github.com/mrdaemon/exosphere/actions/workflows/exosphere-test.yml)
37
+
38
+ Exosphere is a command line and Text User Interface driven utility to query and
39
+ get ad-hoc reports from server infrastructure.
40
+
41
+ It can, so far, be used to:
42
+
43
+ - Get an overview of pending package and security updates
44
+ - Aggregate host online status and basic information
45
+
46
+ It is meant to be simple and foregoes any rich features that could otherwise be
47
+ serviced by other, better written tools.
48
+
49
+ It is meant to give a high level view of your infrastructure and how it's going,
50
+ as well as query information about state that is otherwise difficult
51
+ to aggregate or obtain ad-hoc.
52
+
53
+ Because reporting sucks, and things could be better.
54
+
55
+ > ## Pre-release Version
56
+ > This is a pre-release version of Exosphere
57
+ > It can be used but is under-documented, and still changing rapidly
58
+ > You may not want to use this this just yet, and wait for 1.0
59
+
60
+ ## Development Quick Start
61
+
62
+ tl;dr, use [uv](https://docs.astral.sh/uv/getting-started/installation/)
63
+
64
+ ```bash
65
+ uv sync
66
+ uv run exosphere
67
+ ```
68
+
69
+ Linting, formatting and testing can be done with poe tasks:
70
+
71
+ ```bash
72
+ uv run poe format
73
+ uv run poe check
74
+ uv run poe test
75
+ ```
76
+
77
+ For more details, and available tasks, run:
78
+
79
+ ```bash
80
+ uv run poe --help
81
+ ```
82
+
83
+ ## UI Development Quick Start
84
+
85
+ The UI is built with [Textual](https://textual.textualize.io/).
86
+
87
+ A quickstart of running the UI with live editing and reloading, with debug
88
+ console is as follows:
89
+
90
+ ```bash
91
+ # Ensure you have the dev dependencies
92
+ uv sync --dev
93
+ # In a separate terminal, run the console
94
+ uv run textual console
95
+ # In another terminal, run the UI
96
+ uv run textual run --dev -c exosphere ui start
97
+ ```
98
+
99
+ Congratulations, editing any of the tcss files in the `ui/` directory will
100
+ reflect changes immediately.
101
+
102
+ Make sure you run the exosphere ui with 'exosphere ui start' otherwise the
103
+ configuration will not be loaded correctly, and the inventory will not be
104
+ populated.
@@ -0,0 +1,71 @@
1
+ # Exosphere
2
+
3
+ [![Exosphere Test Suite](https://github.com/mrdaemon/exosphere/actions/workflows/exosphere-test.yml/badge.svg)](https://github.com/mrdaemon/exosphere/actions/workflows/exosphere-test.yml)
4
+
5
+ Exosphere is a command line and Text User Interface driven utility to query and
6
+ get ad-hoc reports from server infrastructure.
7
+
8
+ It can, so far, be used to:
9
+
10
+ - Get an overview of pending package and security updates
11
+ - Aggregate host online status and basic information
12
+
13
+ It is meant to be simple and foregoes any rich features that could otherwise be
14
+ serviced by other, better written tools.
15
+
16
+ It is meant to give a high level view of your infrastructure and how it's going,
17
+ as well as query information about state that is otherwise difficult
18
+ to aggregate or obtain ad-hoc.
19
+
20
+ Because reporting sucks, and things could be better.
21
+
22
+ > ## Pre-release Version
23
+ > This is a pre-release version of Exosphere
24
+ > It can be used but is under-documented, and still changing rapidly
25
+ > You may not want to use this this just yet, and wait for 1.0
26
+
27
+ ## Development Quick Start
28
+
29
+ tl;dr, use [uv](https://docs.astral.sh/uv/getting-started/installation/)
30
+
31
+ ```bash
32
+ uv sync
33
+ uv run exosphere
34
+ ```
35
+
36
+ Linting, formatting and testing can be done with poe tasks:
37
+
38
+ ```bash
39
+ uv run poe format
40
+ uv run poe check
41
+ uv run poe test
42
+ ```
43
+
44
+ For more details, and available tasks, run:
45
+
46
+ ```bash
47
+ uv run poe --help
48
+ ```
49
+
50
+ ## UI Development Quick Start
51
+
52
+ The UI is built with [Textual](https://textual.textualize.io/).
53
+
54
+ A quickstart of running the UI with live editing and reloading, with debug
55
+ console is as follows:
56
+
57
+ ```bash
58
+ # Ensure you have the dev dependencies
59
+ uv sync --dev
60
+ # In a separate terminal, run the console
61
+ uv run textual console
62
+ # In another terminal, run the UI
63
+ uv run textual run --dev -c exosphere ui start
64
+ ```
65
+
66
+ Congratulations, editing any of the tcss files in the `ui/` directory will
67
+ reflect changes immediately.
68
+
69
+ Make sure you run the exosphere ui with 'exosphere ui start' otherwise the
70
+ configuration will not be loaded correctly, and the inventory will not be
71
+ populated.
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "exosphere-cli"
3
+ version = "0.9.9"
4
+ description = "A simple centralized patch reporting tool for unix systems"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Alexandre Gauthier", email = "alex@underwares.org" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Environment :: Console",
13
+ "Intended Audience :: System Administrators",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Natural Language :: English",
16
+ "Operating System :: OS Independent",
17
+ "Programming Language :: Python :: 3",
18
+ "Topic :: System :: Monitoring",
19
+ "Topic :: System :: Systems Administration",
20
+ "Topic :: Utilities",
21
+ "Typing :: Typed",
22
+ ]
23
+ license = "MIT"
24
+ license-files = [
25
+ "LICENSE"
26
+ ]
27
+
28
+ dependencies = [
29
+ "click-shell[readline]>=2.1 ; sys_platform != 'win32'",
30
+ "click-shell>=2.1 ; sys_platform == 'win32'",
31
+ "fabric>=3.2.2",
32
+ "typer>=0.15.2",
33
+ "textual>=3.2.0",
34
+ "pyyaml>=6.0.2",
35
+ "textual-serve>=1.1.2",
36
+ "pyreadline3>=3.5.4 ; sys_platform == 'win32'",
37
+ ]
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "fabric[pytest]>=3.2.2",
42
+ "poethepoet>=0.33.1",
43
+ "pyright>=1.1.400",
44
+ "pytest>=8.3.5",
45
+ "pytest-cov>=6.1.1",
46
+ "pytest-json-ctrf>=0.3.5",
47
+ "pytest-mock>=3.14.0",
48
+ "renku-sphinx-theme>=0.5.0",
49
+ "ruff>=0.11.5",
50
+ "sphinx>=8.2.3",
51
+ "sphinx-autobuild>=2024.10.3",
52
+ "sphinx-tabs>=3.4.7",
53
+ "textual-dev>=1.7.0",
54
+ ]
55
+
56
+ [project.urls]
57
+ homepage = "https://github.com/mrdaemon/exosphere"
58
+ repository = "https://github.com/mrdaemon/exosphere"
59
+ issues = "https://github.com/mrdaemon/exosphere/issues"
60
+
61
+ [project.scripts]
62
+ exosphere = "exosphere.main:main"
63
+
64
+ [build-system]
65
+ requires = ["uv_build>=0.7.19,<0.8.0"]
66
+ build-backend = "uv_build"
67
+
68
+ [tool.uv.build-backend]
69
+ module-name = "exosphere"
70
+
71
+ [tool.pytest.ini_options]
72
+ testpaths = ["tests"]
73
+
74
+ [tool.poe.tasks]
75
+ lint = "ruff check src tests"
76
+ isort = "ruff check --select I --fix src tests"
77
+ isort-check = "ruff check --select I src tests --diff"
78
+ typecheck = "pyright src tests"
79
+ ruff-format = "ruff format src tests"
80
+ ruff-format-check = "ruff format --check src tests --diff"
81
+ format = ['ruff-format', 'isort']
82
+ format-check = ['ruff-format-check', 'isort-check']
83
+ check = ['format-check', 'lint', 'typecheck']
84
+ test = "pytest -v --ctrf .tests_report.json"
85
+ coverage = "pytest --cov-report term-missing --cov-report html --cov=src tests/"
86
+ docs = "sphinx-build -b html docs/source docs/build/html"
87
+ livedocs = "sphinx-autobuild -b html docs/source docs/build/html --port 8000 --open-browser"
@@ -0,0 +1,14 @@
1
+ import importlib.metadata
2
+
3
+ from .config import Configuration
4
+
5
+ # Global Instances: configuration and GlobalState
6
+ # These are set at runtime and should be used as singletons
7
+ # to hold the global state and configuration.
8
+
9
+ app_config = Configuration() # Has default values out of the box
10
+
11
+ # Current software version, imported from pyproject metadata
12
+ __version__ = importlib.metadata.version("exosphere_cli")
13
+
14
+ __all__ = ["__version__", "app_config"]
@@ -0,0 +1,129 @@
1
+ import logging
2
+ import sys
3
+ from typing import Annotated
4
+
5
+ # ------------------win32 readline monkeypatch---------------------
6
+ if sys.platform == "win32":
7
+ try:
8
+ # On windows, we use a wrapper module for pyreadline3 in order
9
+ # to provide readline compatibility.
10
+ from exosphere.compat import win32readline as readline
11
+
12
+ # This needs monkeypatched in order for click_shell to make use
13
+ # of it instead of its internal, broken, legacy pyreadline.
14
+ sys.modules["readline"] = readline
15
+ except ImportError:
16
+ sys.stderr.write(
17
+ "Warning: pyreadline3 not found. "
18
+ "Interactive shell may not enable all features.\n"
19
+ )
20
+ # -----------------------------------------------------------------
21
+
22
+ from click_shell import make_click_shell
23
+ from rich import print
24
+ from rich.panel import Panel
25
+ from typer import Argument, Context, Exit, Typer
26
+
27
+ from exosphere import __version__
28
+ from exosphere.commands import config, host, inventory, ui
29
+
30
+ banner = f"""[turquoise4]
31
+ ▗▖
32
+ ▐▌
33
+ ▟█▙ ▝█ █▘ ▟█▙ ▗▟██▖▐▙█▙ ▐▙██▖ ▟█▙ █▟█▌ ▟█▙
34
+ ▐▙▄▟▌ ▐█▌ ▐▛ ▜▌▐▙▄▖▘▐▛ ▜▌▐▛ ▐▌▐▙▄▟▌ █▘ ▐▙▄▟▌
35
+ ▐▛▀▀▘ ▗█▖ ▐▌ ▐▌ ▀▀█▖▐▌ ▐▌▐▌ ▐▌▐▛▀▀▘ █ ▐▛▀▀▘
36
+ ▝█▄▄▌ ▟▀▙ ▝█▄█▘▐▄▄▟▌▐█▄█▘▐▌ ▐▌▝█▄▄▌ █ ▝█▄▄▌
37
+ ▝▀▀ ▝▀ ▀▘ ▝▀▘ ▀▀▀ ▐▌▀▘ ▝▘ ▝▘ ▝▀▀ ▀ ▝▀▀
38
+ ▐▌ [green]v{__version__}[/green][/turquoise4]
39
+ """
40
+
41
+ app = Typer(
42
+ no_args_is_help=False,
43
+ )
44
+
45
+ # Setup commands from modules
46
+ app.add_typer(inventory.app, name="inventory")
47
+ app.add_typer(host.app, name="host")
48
+ app.add_typer(ui.app, name="ui")
49
+ app.add_typer(config.app, name="config")
50
+
51
+
52
+ @app.command(hidden=True)
53
+ def help(ctx: Context, command: Annotated[str | None, Argument()] = None):
54
+ """
55
+ Help for interactive REPL use
56
+
57
+ Provides help for the root REPL command when used interactively,
58
+ in a way that is friendler for that specific context.
59
+ If a command is specified, it will show help for that command.
60
+
61
+ This only applies when in the interactive REPL, commands (including
62
+ the root 'exosphere' program) will use the standard Typer help
63
+ system when invoked from the command line or non-interactively.
64
+ """
65
+
66
+ msg = "\nUse '<command> --help' or 'help <command>' for help on a specific command."
67
+
68
+ # Show root help if no command is specified
69
+ if not command:
70
+ if ctx.parent and getattr(ctx.parent, "command", None):
71
+ subcommands = getattr(ctx.parent.command, "commands", {})
72
+ lines = []
73
+ for name, cmd in subcommands.items():
74
+ if cmd.hidden:
75
+ continue
76
+ lines.append(
77
+ f"[cyan]{name:<11}[/cyan] {cmd.help or 'No description available.'}"
78
+ )
79
+ content = "\n".join(lines)
80
+ panel = Panel.fit(
81
+ content,
82
+ title="Commands",
83
+ title_align="left",
84
+ )
85
+ print("\nAvailable modules during interactive use:\n")
86
+ print(panel)
87
+ print(msg)
88
+ return
89
+
90
+ # Show command help if one is specified
91
+ subcommand = None
92
+ if ctx.parent and getattr(ctx.parent, "command", None):
93
+ subcommands = getattr(ctx.parent.command, "commands", None)
94
+ subcommand = subcommands.get(command) if subcommands else None
95
+ if subcommand:
96
+ subcommand.get_help(ctx)
97
+ print(f"\nUse '{str(subcommand.name)} <command> --help' for more details.")
98
+ return
99
+
100
+ # Fall through for unknown commands
101
+ print(f"[red]Unkown command '{command}'[/red]")
102
+ print(msg)
103
+
104
+
105
+ @app.callback(invoke_without_command=True)
106
+ def cli(ctx: Context) -> None:
107
+ """
108
+ Exosphere CLI
109
+
110
+ The main command-line interface for Exosphere.
111
+ It provides a REPL interface for interactive use as a prompt, but can
112
+ also be used to run commands directly from the command line.
113
+
114
+ Run without arguments to start the interactive mode.
115
+ """
116
+ if ctx.invoked_subcommand is None:
117
+ logger = logging.getLogger(__name__)
118
+ logger.info("Starting Exosphere REPL interface")
119
+
120
+ # Print the banner
121
+ print(banner)
122
+
123
+ # Start interactive REPL
124
+ repl = make_click_shell(
125
+ ctx,
126
+ prompt="exosphere> ",
127
+ )
128
+ repl.cmdloop()
129
+ Exit(0)
File without changes
@@ -0,0 +1,163 @@
1
+ import os
2
+ from typing import Annotated
3
+
4
+ import typer
5
+ from rich.console import Console
6
+ from rich.pretty import Pretty
7
+ from rich.text import Text
8
+
9
+ from exosphere import app_config, context
10
+ from exosphere.config import Configuration
11
+
12
+ app = typer.Typer(
13
+ help="Runtime Configuration Commands",
14
+ no_args_is_help=True,
15
+ )
16
+
17
+ console = Console()
18
+ err_console = Console(stderr=True)
19
+
20
+
21
+ @app.command()
22
+ def show(
23
+ option: Annotated[
24
+ str | None,
25
+ typer.Argument(help="Name of the option to show. All if not specified."),
26
+ ] = None,
27
+ full: Annotated[
28
+ bool,
29
+ typer.Option(
30
+ "--full",
31
+ "-f",
32
+ help="Show full configuration structure, including inventory.",
33
+ ),
34
+ ] = False,
35
+ ) -> None:
36
+ """
37
+ Show the current configuration.
38
+
39
+ Displays the current configuration options, or the value of a specific option
40
+ if specified.
41
+
42
+ If `--full` is specified, it will show the entire configuration structure,
43
+ including the inventory, beyond just the "options" section.
44
+ """
45
+
46
+ if full:
47
+ if option:
48
+ err_console.print(
49
+ "[yellow]Full configuration requested, ignoring option name.[/yellow]"
50
+ )
51
+ console.print(Pretty(app_config, expand_all=True, max_depth=None))
52
+ return
53
+
54
+ if option:
55
+ if option in app_config["options"]:
56
+ console.print(app_config["options"][option])
57
+ else:
58
+ err_console.print(
59
+ f"[red]Option '{option}' not found in configuration.[/red]"
60
+ )
61
+
62
+ return
63
+
64
+ console.print(Pretty(app_config["options"], expand_all=True))
65
+
66
+
67
+ @app.command()
68
+ def source(
69
+ env: Annotated[
70
+ bool,
71
+ typer.Option(
72
+ help="Show environment variables that affect the configuration.",
73
+ ),
74
+ ] = True,
75
+ ) -> None:
76
+ """
77
+ Show the configuration source, where it was loaded from.
78
+
79
+ Displays the path of the configuration file loaded, if any, and
80
+ any environment variables that affect the configuration.
81
+ """
82
+
83
+ if context.confpath:
84
+ console.print(f"{context.confpath}")
85
+ else:
86
+ err_console.print("No configuration loaded, using defaults.")
87
+
88
+ if not env:
89
+ return
90
+
91
+ env_lines: list[str] = []
92
+
93
+ prefix = "EXOSPHERE_OPTIONS_"
94
+
95
+ for key, value in os.environ.items():
96
+ if (
97
+ key.startswith(prefix)
98
+ and key.removeprefix(prefix).lower() in app_config["options"]
99
+ ):
100
+ env_lines.append(f"{key}={value}")
101
+
102
+ if env_lines:
103
+ console.print()
104
+ console.print("Environment variable overrides:\n")
105
+ for line in env_lines:
106
+ console.print(f" {line}")
107
+ console.print()
108
+
109
+
110
+ @app.command()
111
+ def diff(
112
+ full: Annotated[
113
+ bool,
114
+ typer.Option(
115
+ "--full",
116
+ "-f",
117
+ help="Show full configuration diff, including unmodified options.",
118
+ ),
119
+ ] = False,
120
+ ):
121
+ """
122
+ Show the differences between the current configuration and the defaults.
123
+
124
+ Exosphere follows convention over configuration, so your configuration
125
+ file can exclusively contain the options you want to change.
126
+
127
+ This command allows you to see exactly what has been changed, optionally
128
+ in its context, using the `--full` option.
129
+
130
+ For a full config dump, use the `show` command instead.
131
+ """
132
+
133
+ default_config = Configuration.DEFAULTS["options"]
134
+ current_config = app_config["options"]
135
+
136
+ for key in set(default_config) | set(current_config):
137
+ if default_config.get(key, None) != current_config.get(key, None):
138
+ break
139
+ else:
140
+ console.print("No differences found between current and default configuration.")
141
+ return
142
+
143
+ lines = []
144
+ for key in sorted(set(default_config) | set(current_config)):
145
+ default_value = default_config.get(key, None)
146
+ current_value = current_config.get(key, None)
147
+
148
+ line: Text | None
149
+
150
+ if default_value != current_value:
151
+ line = Text(f"{key!r}: {current_value!r},", style="bold green")
152
+ line.append(f" # default: {default_value!r}", style="yellow")
153
+ else:
154
+ line = Text(f"{key!r}: {current_value!r},", style="dim") if full else None
155
+
156
+ if line:
157
+ lines.append(line)
158
+
159
+ console.print("{")
160
+ for line in lines:
161
+ console.print(" ", end="")
162
+ console.print(line)
163
+ console.print("}")