-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup_bootstrap.py
More file actions
415 lines (340 loc) Β· 15.3 KB
/
setup_bootstrap.py
File metadata and controls
415 lines (340 loc) Β· 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#!/usr/bin/env python3
"""
Ubuntu Bootstrap Setup - Main Entry Point
This is the new Python-based setup script that replaces setup.sh
with better error handling, modularity, and maintainability.
"""
import os
import sys
import argparse
from pathlib import Path
from typing import Optional, List
# Add lib directory to Python path
lib_path = Path(__file__).parent / 'lib'
sys.path.insert(0, str(lib_path))
try:
from setup_manager import SetupManager, SetupError
from config_interface import ConfigurationInterface
from config_models import BootstrapConfiguration, load_config, create_default_config
from menu_schema import MenuSchema
except ImportError as e:
print(f"Error importing modules: {e}")
print("Please ensure you're running this script from the project root directory.")
sys.exit(1)
class WelcomeMenu:
"""Professional welcome menu for setup options"""
def __init__(self):
self.setup_manager = SetupManager()
def show_welcome(self) -> None:
"""Display welcome message"""
print("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
print("β β")
print("β π§ Ubuntu Desktop Bootstrap π§ β")
print("β β")
print("β Transform Your Ubuntu Experience β")
print("β β")
print("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
print()
print("π§ Welcome to Ubuntu Desktop Bootstrap!")
print()
print("This tool will help you set up a personalized Ubuntu desktop")
print("with your preferred applications and customizations.")
print()
def show_menu(self) -> int:
"""Show main menu and get user choice"""
options = [
"π Fresh Install - Configure a brand new Ubuntu installation",
"π§ Modify Setup - Tweak your existing configuration",
"π¦ Apply Profile - Restore from a saved configuration",
"πΎ Backup Config - Save your current setup",
"π― Quick Setup - Install prerequisites only",
"π System Check - Verify system compatibility",
"β Help - Get help and documentation",
"πͺ Exit - See you later!"
]
print("What would you like to do?")
print()
for i, option in enumerate(options, 1):
print(f" {i}. {option}")
print()
while True:
try:
choice = input("Enter your choice (1-8): ").strip()
if choice.isdigit() and 1 <= int(choice) <= 8:
return int(choice)
else:
print("Please enter a number between 1 and 8.")
except (KeyboardInterrupt, EOFError):
return 8 # Exit
def handle_choice(self, choice: int) -> bool:
"""Handle user menu choice"""
try:
if choice == 1:
return self._fresh_install()
elif choice == 2:
return self._modify_setup()
elif choice == 3:
return self._apply_profile()
elif choice == 4:
return self._backup_config()
elif choice == 5:
return self._quick_setup()
elif choice == 6:
return self._system_check()
elif choice == 7:
return self._show_help()
elif choice == 8:
print("\nπ Thanks for using Ubuntu Desktop Bootstrap!")
return False
else:
print("Invalid choice")
return True
except Exception as e:
print(f"Error: {e}")
return True
def _fresh_install(self) -> bool:
"""Handle fresh installation"""
print("\nπ Starting fresh installation...")
# Run system checks and prerequisites first
if not self.setup_manager.quick_setup():
print("β Prerequisites installation failed")
return True
# Launch configuration wizard
print("\nπ Launching configuration interface...")
# Run the standard TUI configuration
import subprocess
result = subprocess.run([sys.executable, "configure_standard_tui.py"],
capture_output=False)
if result.returncode == 0:
print("\nβ
Configuration completed!")
# Ask if user wants to apply the configuration
apply = input("\nWould you like to apply the configuration now? (y/N): ").strip().lower()
if apply in ['y', 'yes']:
return self._run_ansible()
else:
print("\nβ Configuration cancelled or failed")
return True
def _modify_setup(self) -> bool:
"""Handle setup modification"""
print("\nπ§ Modifying existing setup...")
# Check if configuration exists
if not self.setup_manager.config_manager.has_configuration():
print("β No existing configuration found")
print("Please run 'Fresh Install' first to create a configuration")
return True
# Load existing configuration
config = self.setup_manager.config_manager.load_configuration()
if not config:
print("β Failed to load existing configuration")
return True
print(f"β
Loaded existing configuration")
# Show configuration options
print("\nAvailable modification options:")
print(" 1. Re-run configuration wizard")
print(" 2. Apply existing configuration")
print(" 3. Run specific Ansible tags")
print(" 4. Back to main menu")
choice = input("\nEnter your choice (1-4): ").strip()
if choice == '1':
# Re-run configuration interface
import subprocess
result = subprocess.run([sys.executable, "configure_standard_tui.py"],
capture_output=False)
if result.returncode == 0:
apply = input("\nApply updated configuration? (y/N): ").strip().lower()
if apply in ['y', 'yes']:
return self._run_ansible()
elif choice == '2':
return self._run_ansible()
elif choice == '3':
return self._run_ansible_tags()
return True
def _apply_profile(self) -> bool:
"""Handle profile application"""
print("\nπ¦ Applying saved profile...")
profiles_dir = self.setup_manager.config_manager.profiles_dir
if not profiles_dir.exists():
print("β No profiles directory found")
return True
# List available profiles
profiles = list(profiles_dir.glob("*.yml"))
if not profiles:
print("β No saved profiles found")
return True
print("\nAvailable profiles:")
for i, profile in enumerate(profiles, 1):
print(f" {i}. {profile.stem}")
try:
choice = input(f"\nEnter profile number (1-{len(profiles)}): ").strip()
if choice.isdigit() and 1 <= int(choice) <= len(profiles):
selected_profile = profiles[int(choice) - 1]
# Copy profile to config.yml
import shutil
shutil.copy2(selected_profile, "config.yml")
print(f"β
Applied profile: {selected_profile.stem}")
# Ask if user wants to run Ansible
apply = input("\nRun Ansible with this profile? (y/N): ").strip().lower()
if apply in ['y', 'yes']:
return self._run_ansible()
else:
print("Invalid choice")
except ValueError:
print("Invalid input")
return True
def _backup_config(self) -> bool:
"""Handle configuration backup"""
print("\nπΎ Backing up configuration...")
name = input("Enter backup name (leave empty for timestamp): ").strip()
if self.setup_manager.config_manager.backup_configuration(name or None):
print("β
Configuration backed up successfully")
else:
print("β Backup failed")
return True
def _quick_setup(self) -> bool:
"""Handle quick setup"""
print("\nπ― Running quick setup...")
if self.setup_manager.quick_setup():
print("β
Quick setup completed successfully")
else:
print("β Quick setup failed")
return True
def _system_check(self) -> bool:
"""Handle system check"""
print("\nπ Running system checks...")
if self.setup_manager.run_system_checks():
print("β
System checks passed")
print(f"System: {self.setup_manager.system_info.os_name} {self.setup_manager.system_info.os_version}")
print(f"Python: {self.setup_manager.system_info.python_version}")
print(f"Architecture: {self.setup_manager.system_info.architecture}")
else:
print("β System checks failed")
return True
def _show_help(self) -> bool:
"""Show help information"""
print("\nβ Ubuntu Desktop Bootstrap Help")
print("=" * 50)
print()
print("This tool helps you configure and customize your Ubuntu desktop.")
print()
print("Available interface:")
print(" - configure_standard_tui.py - Professional TUI interface")
print()
print("Configuration files:")
print(" - config.yml - Main configuration file")
print(" - group_vars/all/main.yml - Default settings")
print(" - ~/.config/ubuntu-bootstrap/ - User configuration directory")
print()
print("Ansible playbooks:")
print(" - bootstrap.yml - Initial system setup")
print(" - site.yml - Complete configuration")
print()
print("For more information, see:")
print(" - README.md")
print(" - QUICK_START.md")
print(" - CLAUDE.md")
print()
input("Press Enter to continue...")
return True
def _run_ansible(self) -> bool:
"""Run Ansible playbook"""
print("\nπ€ Running Ansible playbook...")
if not Path("config.yml").exists():
print("β No configuration file found")
return True
# Ask for confirmation
print("This will apply your configuration to the system.")
confirm = input("Continue? (y/N): ").strip().lower()
if confirm not in ['y', 'yes']:
print("β Cancelled")
return True
success = self.setup_manager.run_bootstrap()
if success:
print("β
Ansible playbook completed successfully")
else:
print("β Ansible playbook failed")
return True
def _run_ansible_tags(self) -> bool:
"""Run Ansible with specific tags"""
print("\nπ·οΈ Running Ansible with specific tags...")
available_tags = [
"desktop", "applications", "development", "security",
"system", "dotfiles", "performance"
]
print("\nAvailable tags:")
for i, tag in enumerate(available_tags, 1):
print(f" {i}. {tag}")
tag_input = input("\nEnter tag numbers (comma-separated) or tag names: ").strip()
tags = []
if tag_input:
for item in tag_input.split(','):
item = item.strip()
if item.isdigit():
idx = int(item) - 1
if 0 <= idx < len(available_tags):
tags.append(available_tags[idx])
else:
tags.append(item)
if not tags:
print("β No valid tags specified")
return True
print(f"Running with tags: {', '.join(tags)}")
success = self.setup_manager.run_bootstrap(tags=tags)
if success:
print("β
Ansible playbook completed successfully")
else:
print("β Ansible playbook failed")
return True
def main():
"""Main entry point"""
parser = argparse.ArgumentParser(description="Ubuntu Desktop Bootstrap Setup")
parser.add_argument('--check', action='store_true',
help="Run system checks only")
parser.add_argument('--quick', action='store_true',
help="Run quick setup (prerequisites only)")
parser.add_argument('--config', type=str, default="config.yml",
help="Configuration file path")
parser.add_argument('--tags', type=str,
help="Ansible tags to run (comma-separated)")
parser.add_argument('--dry-run', action='store_true',
help="Run Ansible in check mode")
parser.add_argument('--interface', type=str,
choices=['wizard', 'interactive', 'compact', 'hierarchical'],
help="Launch specific configuration interface")
args = parser.parse_args()
setup_manager = SetupManager()
try:
# Handle command-line arguments
if args.check:
success = setup_manager.run_system_checks()
sys.exit(0 if success else 1)
if args.quick:
success = setup_manager.quick_setup()
sys.exit(0 if success else 1)
if args.interface:
# Launch the TUI interface
if args.interface in ['wizard', 'interactive', 'compact', 'hierarchical']:
# All interface options now use the standard TUI
os.system(f'python3 configure_standard_tui.py')
else:
print(f"Interface '{args.interface}' not available")
sys.exit(0)
if args.tags:
# Run Ansible with specific tags
tags = [tag.strip() for tag in args.tags.split(',')]
success = setup_manager.run_bootstrap(args.config, tags, args.dry_run)
sys.exit(0 if success else 1)
# If no arguments, show interactive menu
welcome = WelcomeMenu()
welcome.show_welcome()
while True:
choice = welcome.show_menu()
if not welcome.handle_choice(choice):
break
except KeyboardInterrupt:
print("\n\nπ Setup cancelled by user")
sys.exit(130)
except Exception as e:
print(f"\nβ Unexpected error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()