From 15929d9ca5e903fcb9891f33fe42e0c45c8b8417 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Tue, 28 Oct 2025 22:58:37 +0545 Subject: [PATCH] feat: add CLI and generator scripts to orchestrate project setup --- src/cli.py | 111 +++++++++++++++++++++++++++++++++++++++++++++++ src/generator.py | 83 +++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) diff --git a/src/cli.py b/src/cli.py index e69de29..4df2415 100644 --- a/src/cli.py +++ b/src/cli.py @@ -0,0 +1,111 @@ +""" +Main CLI orchestrator for djinit. +Coordinates between different managers to create a complete Django project. +""" + +import os +from typing import Callable, List, Tuple + +from src.scripts.cicd_config_generator import CICDConfigGenerator +from src.scripts.console_ui import UIFormatter +from src.scripts.deployment_files_generator import DeploymentConfigGenerator +from src.scripts.secretkey_generator import generate_secret_command +from src.templates.files import FileManager +from src.templates.project import ProjectManager +from src.templates.settings import SettingsManager + + +class Cli: + def __init__(self, project_dir: str, project_name: str, primary_app: str, app_names: list, metadata: dict): + self.project_dir = project_dir + self.project_name = project_name + self.primary_app = primary_app + self.app_names = app_names + self.metadata = metadata + self.project_root = os.path.join(os.getcwd(), project_dir) + + # Initialize managers + self.project_manager = ProjectManager(project_dir, project_name, app_names) + self.settings_manager = SettingsManager(self.project_root, project_name, app_names) + self.file_manager = FileManager(self.project_root, project_name, app_names) + self.deployment_manager = DeploymentConfigGenerator(self.project_root, project_name) + self.cicd_manager = CICDConfigGenerator(self.project_root, project_name) + + def run_setup(self) -> bool: + steps: List[Tuple[str, Callable[[], bool]]] = [ + ("Creating Django project", self.project_manager.create_project), + ("Creating Django apps", self.project_manager.create_apps), + ("Validating project structure", self.project_manager.validate_project_structure), + ("Setting up project structure", self.settings_manager.create_settings_structure), + ("Configuring base settings", self.settings_manager.update_base_settings), + ("Configuring development settings", self.settings_manager.update_development_settings), + ("Configuring production settings", self.settings_manager.update_production_settings), + ("Creating utility files", self._create_utility_files), + ("Setting up app URLs", self.file_manager.create_app_urls), + ("Configuring comprehensive URLs", self.file_manager.update_project_urls), + ("Updating WSGI configuration", self.file_manager.update_wsgi_file), + ("Updating ASGI configuration", self.file_manager.update_asgi_file), + ("Updating manage.py", self.file_manager.update_manage_py), + ("Creating Justfile", self.deployment_manager.create_justfile), + ("Creating Procfile", self.deployment_manager.create_procfile), + ("Creating runtime.txt", lambda: self.deployment_manager.create_runtime_txt("3.13")), + ("Creating CI/CD pipelines", self._create_cicd_pipelines), + ] + + total_steps = len(steps) + success = True + + # Create live progress display + progress, task = UIFormatter.create_live_progress(description="Setup Progress", total_steps=total_steps) + + with progress: + # Execute each step with progress tracking + for step_number, (description, step_func) in enumerate(steps, 1): + result = step_func() + if not result: + success = False + UIFormatter.print_error(f"Step {step_number} failed: {description}") + break + + # Update the same progress bar + progress.update(task, advance=1, description=f"Step {step_number}/{total_steps}") + + return success + + def _create_utility_files(self) -> bool: + utility_steps = [ + self.file_manager.create_gitignore, + self.file_manager.create_requirements, + self.file_manager.create_readme, + self.file_manager.create_env_file, + lambda: self.file_manager.create_pyproject_toml(self.metadata), + ] + + for step_func in utility_steps: + result = step_func() + if not result: + return False + + UIFormatter.print_success("Created all utility files successfully!") + return True + + def _create_cicd_pipelines(self) -> bool: + if self.metadata.get("use_github_actions", True): + result = self.cicd_manager.create_github_actions() + if not result: + return False + + if self.metadata.get("use_gitlab_ci", True): + result = self.cicd_manager.create_gitlab_ci() + if not result: + return False + + return True + + def generate_secret_keys(self) -> bool: + try: + generate_secret_command() + return True + except Exception as e: + UIFormatter.print_error(f"Failed to generate secret keys: {e}") + return False diff --git a/src/generator.py b/src/generator.py index e69de29..8a71ff3 100644 --- a/src/generator.py +++ b/src/generator.py @@ -0,0 +1,83 @@ +""" +Main script entry point for djinit. +Handles user input validation and orchestrates the setup process. +""" + +import os +import subprocess +import sys + +from src.cli import Cli +from src.scripts.command_handler import handle_app_command, handle_secret_command, parse_arguments +from src.scripts.console_ui import UIFormatter, console +from src.scripts.input_handler import confirm_setup, get_user_input + + +def clear_screen() -> None: + try: + if os.name == "nt": + subprocess.run("cls", shell=True, check=True) # Windows + else: + subprocess.run("clear", shell=True, check=True) # Linux/MacOS + except subprocess.CalledProcessError: + # If clearing fails, just print some newlines + console.print("\n" * 50) + + +def main() -> None: + try: + # Parse command line arguments + args = parse_arguments() + + # Handle secret command + if args.command == "secret": + handle_secret_command(args) + return + + # Handle app command + if args.command == "app": + handle_app_command(args) + return + + # Default to setup command + # Clear screen + clear_screen() + + # Display welcome screen + console.print() + console.print(UIFormatter.create_welcome_panel()) + console.print() + + # Get user input + project_dir, project_name, primary_app, app_names, metadata = get_user_input() + + # Confirm setup + if not confirm_setup(project_dir, project_name, app_names, metadata): + UIFormatter.print_info("Setup cancelled by user.") + return + + # Run setup + console.print() + UIFormatter.print_info("Starting Django project setup...") + console.print() + + django_cli = Cli(project_dir, project_name, primary_app, app_names, metadata) + success = django_cli.run_setup() + + # Display completion summary + UIFormatter.create_summary_panel(project_dir, project_name, app_names, True) + + # Exit with appropriate code + sys.exit(0 if success else 1) + + except KeyboardInterrupt: + UIFormatter.print_info("\nSetup cancelled by user.") + sys.exit(0) + except Exception as e: + UIFormatter.print_error(f"Unexpected error: {str(e)}") + console.print("[dim]Please check your environment and try again.[/dim]") + sys.exit(1) + + +if __name__ == "__main__": + main()