octopize.deploy_tool 0.1.0__py3-none-any.whl
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.
- octopize_avatar_deploy/__init__.py +26 -0
- octopize_avatar_deploy/cli_test_harness.py +217 -0
- octopize_avatar_deploy/configure.py +705 -0
- octopize_avatar_deploy/defaults.yaml +63 -0
- octopize_avatar_deploy/download_templates.py +251 -0
- octopize_avatar_deploy/input_gatherer.py +324 -0
- octopize_avatar_deploy/printer.py +216 -0
- octopize_avatar_deploy/state_manager.py +136 -0
- octopize_avatar_deploy/steps/__init__.py +25 -0
- octopize_avatar_deploy/steps/authentik.py +46 -0
- octopize_avatar_deploy/steps/authentik_blueprint.py +79 -0
- octopize_avatar_deploy/steps/base.py +199 -0
- octopize_avatar_deploy/steps/database.py +37 -0
- octopize_avatar_deploy/steps/email.py +88 -0
- octopize_avatar_deploy/steps/logging.py +32 -0
- octopize_avatar_deploy/steps/required.py +81 -0
- octopize_avatar_deploy/steps/storage.py +32 -0
- octopize_avatar_deploy/steps/telemetry.py +58 -0
- octopize_avatar_deploy/steps/user.py +89 -0
- octopize_avatar_deploy/version_compat.py +344 -0
- octopize_deploy_tool-0.1.0.dist-info/METADATA +346 -0
- octopize_deploy_tool-0.1.0.dist-info/RECORD +24 -0
- octopize_deploy_tool-0.1.0.dist-info/WHEEL +4 -0
- octopize_deploy_tool-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Octopize Avatar Deployment Tool."""
|
|
2
|
+
|
|
3
|
+
from .configure import DeploymentConfigurator, DeploymentRunner, main
|
|
4
|
+
from .printer import ConsolePrinter, Printer, SilentPrinter
|
|
5
|
+
from .version_compat import (
|
|
6
|
+
SCRIPT_VERSION,
|
|
7
|
+
VersionError,
|
|
8
|
+
check_version_compatibility,
|
|
9
|
+
validate_template_compatibility,
|
|
10
|
+
validate_template_version,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
__all__ = [
|
|
15
|
+
"main",
|
|
16
|
+
"DeploymentRunner",
|
|
17
|
+
"DeploymentConfigurator",
|
|
18
|
+
"Printer",
|
|
19
|
+
"ConsolePrinter",
|
|
20
|
+
"SilentPrinter",
|
|
21
|
+
"SCRIPT_VERSION",
|
|
22
|
+
"VersionError",
|
|
23
|
+
"check_version_compatibility",
|
|
24
|
+
"validate_template_compatibility",
|
|
25
|
+
"validate_template_version",
|
|
26
|
+
]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI Test Harness for Avatar Deployment Tool
|
|
4
|
+
|
|
5
|
+
Allows testing the CLI with pre-configured responses instead of manual input.
|
|
6
|
+
This enables automated end-to-end testing of the entire CLI workflow.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from octopize_avatar_deploy.configure import main
|
|
14
|
+
from octopize_avatar_deploy.input_gatherer import MockInputGatherer
|
|
15
|
+
from octopize_avatar_deploy.printer import FilePrinter, SilentPrinter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CLITestHarness:
|
|
19
|
+
"""
|
|
20
|
+
Test harness for running CLI with mocked input/output.
|
|
21
|
+
|
|
22
|
+
This allows automated testing of the complete CLI workflow by:
|
|
23
|
+
1. Injecting pre-configured responses via MockInputGatherer
|
|
24
|
+
2. Optionally capturing output via SilentPrinter or FilePrinter
|
|
25
|
+
3. Setting command-line arguments programmatically
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
responses: list[str | bool],
|
|
31
|
+
args: list[str] | None = None,
|
|
32
|
+
silent: bool = False,
|
|
33
|
+
log_file: Path | str | None = None,
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Initialize CLI test harness.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
responses: List of pre-configured responses for MockInputGatherer
|
|
40
|
+
args: Command-line arguments to pass (default: [])
|
|
41
|
+
silent: Whether to suppress output completely (default: False)
|
|
42
|
+
log_file: Optional path to log file for capturing output
|
|
43
|
+
If provided, overrides silent=True
|
|
44
|
+
"""
|
|
45
|
+
self.responses = responses
|
|
46
|
+
self.args = args or []
|
|
47
|
+
self.silent = silent
|
|
48
|
+
self.log_file = log_file
|
|
49
|
+
self._original_argv = None
|
|
50
|
+
self._original_env: dict[str, str | None] = {}
|
|
51
|
+
|
|
52
|
+
def __enter__(self):
|
|
53
|
+
"""Set up test environment."""
|
|
54
|
+
# Save original argv
|
|
55
|
+
self._original_argv = sys.argv.copy()
|
|
56
|
+
|
|
57
|
+
# Set new argv (program name + args)
|
|
58
|
+
sys.argv = ["octopize-avatar-deploy"] + self.args
|
|
59
|
+
|
|
60
|
+
# Save and set environment variables
|
|
61
|
+
env_vars = [
|
|
62
|
+
"AVATAR_DEPLOY_TEST_MODE",
|
|
63
|
+
"AVATAR_DEPLOY_TEST_RESPONSES",
|
|
64
|
+
"AVATAR_DEPLOY_TEST_SILENT",
|
|
65
|
+
"AVATAR_DEPLOY_TEST_LOG_FILE",
|
|
66
|
+
]
|
|
67
|
+
for var in env_vars:
|
|
68
|
+
self._original_env[var] = os.environ.get(var)
|
|
69
|
+
|
|
70
|
+
os.environ["AVATAR_DEPLOY_TEST_MODE"] = "1"
|
|
71
|
+
os.environ["AVATAR_DEPLOY_TEST_RESPONSES"] = self.serialize_responses(self.responses)
|
|
72
|
+
|
|
73
|
+
if self.log_file:
|
|
74
|
+
os.environ["AVATAR_DEPLOY_TEST_LOG_FILE"] = str(self.log_file)
|
|
75
|
+
elif self.silent:
|
|
76
|
+
os.environ["AVATAR_DEPLOY_TEST_SILENT"] = "1"
|
|
77
|
+
|
|
78
|
+
return self
|
|
79
|
+
|
|
80
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
81
|
+
"""Clean up test environment."""
|
|
82
|
+
# Restore original argv
|
|
83
|
+
sys.argv = self._original_argv
|
|
84
|
+
|
|
85
|
+
# Restore environment
|
|
86
|
+
for var, value in self._original_env.items():
|
|
87
|
+
if value is not None:
|
|
88
|
+
os.environ[var] = value
|
|
89
|
+
else:
|
|
90
|
+
os.environ.pop(var, None)
|
|
91
|
+
|
|
92
|
+
def run(self) -> int:
|
|
93
|
+
"""
|
|
94
|
+
Run the CLI with test configuration.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Exit code (0 for success, non-zero for failure)
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
with self:
|
|
102
|
+
main()
|
|
103
|
+
return 0
|
|
104
|
+
except SystemExit as e:
|
|
105
|
+
# e.code can be int, str, or None
|
|
106
|
+
if e.code is None:
|
|
107
|
+
return 0
|
|
108
|
+
if isinstance(e.code, int):
|
|
109
|
+
return e.code
|
|
110
|
+
# If it's a string or other type, return 1
|
|
111
|
+
return 1
|
|
112
|
+
except Exception as e:
|
|
113
|
+
if not self.silent and not self.log_file:
|
|
114
|
+
print(f"CLI test failed with exception: {e}", file=sys.stderr)
|
|
115
|
+
return 1
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def serialize_responses(responses: list[str | bool]) -> str:
|
|
119
|
+
"""Serialize responses to string format."""
|
|
120
|
+
serialized = []
|
|
121
|
+
for r in responses:
|
|
122
|
+
if isinstance(r, bool):
|
|
123
|
+
serialized.append("__BOOL_TRUE__" if r else "__BOOL_FALSE__")
|
|
124
|
+
else:
|
|
125
|
+
# Escape delimiter
|
|
126
|
+
serialized.append(str(r).replace("|||", "\\|||"))
|
|
127
|
+
return "|||".join(serialized)
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def deserialize_responses(serialized: str) -> list[str | bool]:
|
|
131
|
+
"""Deserialize responses from string format."""
|
|
132
|
+
if not serialized:
|
|
133
|
+
return []
|
|
134
|
+
|
|
135
|
+
responses: list[str | bool] = []
|
|
136
|
+
for r in serialized.split("|||"):
|
|
137
|
+
# Unescape delimiter
|
|
138
|
+
r = r.replace("\\|||", "|||")
|
|
139
|
+
|
|
140
|
+
# Convert bool markers back to bool
|
|
141
|
+
if r == "__BOOL_TRUE__":
|
|
142
|
+
responses.append(True)
|
|
143
|
+
elif r == "__BOOL_FALSE__":
|
|
144
|
+
responses.append(False)
|
|
145
|
+
else:
|
|
146
|
+
responses.append(r)
|
|
147
|
+
return responses
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_test_input_gatherer():
|
|
151
|
+
"""
|
|
152
|
+
Get MockInputGatherer if in test mode, otherwise None.
|
|
153
|
+
|
|
154
|
+
This function is called by configure.py to inject test responses.
|
|
155
|
+
"""
|
|
156
|
+
if os.environ.get("AVATAR_DEPLOY_TEST_MODE") != "1":
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
serialized = os.environ.get("AVATAR_DEPLOY_TEST_RESPONSES", "")
|
|
160
|
+
responses = CLITestHarness.deserialize_responses(serialized)
|
|
161
|
+
|
|
162
|
+
if not responses:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
return MockInputGatherer(responses)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_test_printer():
|
|
169
|
+
"""
|
|
170
|
+
Get appropriate printer for test mode.
|
|
171
|
+
|
|
172
|
+
Returns FilePrinter if log file specified, SilentPrinter if silent mode,
|
|
173
|
+
otherwise None.
|
|
174
|
+
"""
|
|
175
|
+
log_file = os.environ.get("AVATAR_DEPLOY_TEST_LOG_FILE")
|
|
176
|
+
if log_file:
|
|
177
|
+
return FilePrinter(log_file)
|
|
178
|
+
|
|
179
|
+
if os.environ.get("AVATAR_DEPLOY_TEST_SILENT") == "1":
|
|
180
|
+
return SilentPrinter()
|
|
181
|
+
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# Convenience function for quick testing
|
|
186
|
+
def run_cli_test(
|
|
187
|
+
responses: list[str | bool],
|
|
188
|
+
args: list[str] | None = None,
|
|
189
|
+
silent: bool = False,
|
|
190
|
+
log_file: Path | str | None = None,
|
|
191
|
+
) -> int:
|
|
192
|
+
"""
|
|
193
|
+
Convenience function to run a CLI test.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
responses: Pre-configured responses
|
|
197
|
+
args: Command-line arguments
|
|
198
|
+
silent: Whether to suppress output completely
|
|
199
|
+
log_file: Optional path to log file (overrides silent)
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Exit code (0 for success)
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> # Test with file logging
|
|
206
|
+
>>> exit_code = run_cli_test(
|
|
207
|
+
... responses=["https://api.example.com", False, False],
|
|
208
|
+
... args=["--output-dir", "/tmp/test"],
|
|
209
|
+
... log_file="/tmp/test/deployment.log"
|
|
210
|
+
... )
|
|
211
|
+
>>> # Check log file if test failed
|
|
212
|
+
>>> if exit_code != 0:
|
|
213
|
+
... with open("/tmp/test/deployment.log") as f:
|
|
214
|
+
... print(f.read())
|
|
215
|
+
"""
|
|
216
|
+
harness = CLITestHarness(responses, args, silent, log_file)
|
|
217
|
+
return harness.run()
|