mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:31:08 -05:00
feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application - Add frontend/ directory with Vite + TypeScript setup ready for Next.js - Add comprehensive shared/ directory with: - Complete documentation and memory-bank archives - Media files and avatars (letters, park/ride images) - Deployment scripts and automation tools - Shared types and utilities - Add architecture/ directory with migration guides - Configure pnpm workspace for monorepo development - Update .gitignore to exclude .django_tailwind_cli/ build artifacts - Preserve all historical documentation in shared/docs/memory-bank/ - Set up proper structure for full-stack development with shared resources
This commit is contained in:
212
shared/scripts/unraid/validate-autoinstall.py
Executable file
212
shared/scripts/unraid/validate-autoinstall.py
Executable file
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validate autoinstall configuration against Ubuntu's schema.
|
||||
This script provides basic validation to check if our autoinstall config
|
||||
complies with the official schema structure.
|
||||
"""
|
||||
|
||||
import yaml
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_autoinstall_config(template_path: str) -> dict:
|
||||
"""Load the autoinstall configuration from the template file."""
|
||||
with open(template_path, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# Parse the cloud-config YAML
|
||||
config = yaml.safe_load(content)
|
||||
|
||||
# Extract the autoinstall section
|
||||
if "autoinstall" in config:
|
||||
return config["autoinstall"]
|
||||
else:
|
||||
raise ValueError("No autoinstall section found in cloud-config")
|
||||
|
||||
|
||||
def validate_required_fields(config: dict) -> list:
|
||||
"""Validate required fields according to schema."""
|
||||
errors = []
|
||||
|
||||
# Check version field (required)
|
||||
if "version" not in config:
|
||||
errors.append("Missing required field: version")
|
||||
elif not isinstance(config["version"], int) or config["version"] != 1:
|
||||
errors.append("Invalid version: must be integer 1")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_identity_section(config: dict) -> list:
|
||||
"""Validate identity section."""
|
||||
errors = []
|
||||
|
||||
if "identity" in config:
|
||||
identity = config["identity"]
|
||||
required_fields = ["username", "hostname", "password"]
|
||||
|
||||
for field in required_fields:
|
||||
if field not in identity:
|
||||
errors.append(f"Identity section missing required field: {field}")
|
||||
|
||||
# Additional validation
|
||||
if "username" in identity and not isinstance(identity["username"], str):
|
||||
errors.append("Identity username must be a string")
|
||||
|
||||
if "hostname" in identity and not isinstance(identity["hostname"], str):
|
||||
errors.append("Identity hostname must be a string")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_network_section(config: dict) -> list:
|
||||
"""Validate network section."""
|
||||
errors = []
|
||||
|
||||
if "network" in config:
|
||||
network = config["network"]
|
||||
|
||||
if "version" not in network:
|
||||
errors.append("Network section missing required field: version")
|
||||
elif network["version"] != 2:
|
||||
errors.append("Network version must be 2")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_keyboard_section(config: dict) -> list:
|
||||
"""Validate keyboard section."""
|
||||
errors = []
|
||||
|
||||
if "keyboard" in config:
|
||||
keyboard = config["keyboard"]
|
||||
|
||||
if "layout" not in keyboard:
|
||||
errors.append("Keyboard section missing required field: layout")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_ssh_section(config: dict) -> list:
|
||||
"""Validate SSH section."""
|
||||
errors = []
|
||||
|
||||
if "ssh" in config:
|
||||
ssh = config["ssh"]
|
||||
|
||||
if "install-server" in ssh and not isinstance(ssh["install-server"], bool):
|
||||
errors.append("SSH install-server must be boolean")
|
||||
|
||||
if "authorized-keys" in ssh and not isinstance(ssh["authorized-keys"], list):
|
||||
errors.append("SSH authorized-keys must be an array")
|
||||
|
||||
if "allow-pw" in ssh and not isinstance(ssh["allow-pw"], bool):
|
||||
errors.append("SSH allow-pw must be boolean")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_packages_section(config: dict) -> list:
|
||||
"""Validate packages section."""
|
||||
errors = []
|
||||
|
||||
if "packages" in config:
|
||||
packages = config["packages"]
|
||||
|
||||
if not isinstance(packages, list):
|
||||
errors.append("Packages must be an array")
|
||||
else:
|
||||
for i, package in enumerate(packages):
|
||||
if not isinstance(package, str):
|
||||
errors.append(f"Package at index {i} must be a string")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_commands_sections(config: dict) -> list:
|
||||
"""Validate early-commands and late-commands sections."""
|
||||
errors = []
|
||||
|
||||
for section_name in ["early-commands", "late-commands"]:
|
||||
if section_name in config:
|
||||
commands = config[section_name]
|
||||
|
||||
if not isinstance(commands, list):
|
||||
errors.append(f"{section_name} must be an array")
|
||||
else:
|
||||
for i, command in enumerate(commands):
|
||||
if not isinstance(command, (str, list)):
|
||||
errors.append(
|
||||
f"{section_name} item at index {i} must be string or array"
|
||||
)
|
||||
elif isinstance(command, list):
|
||||
for j, cmd_part in enumerate(command):
|
||||
if not isinstance(cmd_part, str):
|
||||
errors.append(
|
||||
f"{section_name}[{i}][{j}] must be a string"
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def validate_shutdown_section(config: dict) -> list:
|
||||
"""Validate shutdown section."""
|
||||
errors = []
|
||||
|
||||
if "shutdown" in config:
|
||||
shutdown = config["shutdown"]
|
||||
valid_values = ["reboot", "poweroff"]
|
||||
|
||||
if shutdown not in valid_values:
|
||||
errors.append(f"Shutdown must be one of: {valid_values}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main():
|
||||
"""Main validation function."""
|
||||
template_path = Path(__file__).parent / "cloud-init-template.yaml"
|
||||
|
||||
if not template_path.exists():
|
||||
print(f"Error: Template file not found at {template_path}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
# Load the autoinstall configuration
|
||||
print(f"Loading autoinstall config from {template_path}")
|
||||
config = load_autoinstall_config(str(template_path))
|
||||
|
||||
# Run validation checks
|
||||
all_errors = []
|
||||
|
||||
all_errors.extend(validate_required_fields(config))
|
||||
all_errors.extend(validate_identity_section(config))
|
||||
all_errors.extend(validate_network_section(config))
|
||||
all_errors.extend(validate_keyboard_section(config))
|
||||
all_errors.extend(validate_ssh_section(config))
|
||||
all_errors.extend(validate_packages_section(config))
|
||||
all_errors.extend(validate_commands_sections(config))
|
||||
all_errors.extend(validate_shutdown_section(config))
|
||||
|
||||
# Report results
|
||||
if all_errors:
|
||||
print("\n❌ Validation failed with the following errors:")
|
||||
for error in all_errors:
|
||||
print(f" - {error}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("\n✅ Autoinstall configuration validation passed!")
|
||||
print("Configuration appears to comply with Ubuntu autoinstall schema.")
|
||||
|
||||
# Print summary of detected sections
|
||||
sections = list(config.keys())
|
||||
print(f"\nDetected sections: {', '.join(sorted(sections))}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during validation: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user