Last active
April 30, 2025 08:56
-
-
Save yeiichi/18d9cb1adb634d5f24b469241acd3b01 to your computer and use it in GitHub Desktop.
Quickstart script for generic project directories with Sphinx documentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Quickstart script for a Generic project. | |
| This Python script automates the creation of a generic project directory structure | |
| with Sphinx documentation integration. | |
| """ | |
| import argparse | |
| import shutil | |
| import subprocess | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Dict | |
| @dataclass(frozen=True) | |
| class ProjectConfig: | |
| """Configuration constants for project setup""" | |
| PROJECT_ROOT: Path = Path(__file__).resolve().parent | |
| TEMPLATE_DIR: Path = PROJECT_ROOT / 'templates' | |
| PJ_REPO_TEMPLATE: Path = TEMPLATE_DIR / 'pj_repo_tmpl' | |
| DOCS_SOURCE_DIR: str = 'docs/source' | |
| AUTHOR: str = 'Your Name' | |
| # ANSI color codes | |
| YELLOW: str = '\033[93m' | |
| RED: str = '\033[31m' | |
| RESET: str = '\033[0m' | |
| # Project directory structure | |
| DIRECTORIES = { | |
| 'specifications': 'specifications', | |
| 'contracts': 'contracts', | |
| 'schedule': 'schedule', | |
| 'meetings': 'meetings', | |
| 'deliverables': 'deliverables', | |
| 'references': 'references', | |
| 'miscellaneous': 'miscellaneous' | |
| } | |
| class ProjectCreator: | |
| def __init__(self, project_name: str): | |
| self.project_name = project_name | |
| self.cwd = Path.cwd() | |
| self.project_dir = self.cwd / project_name | |
| def create(self) -> None: | |
| """Create and set up the entire project structure""" | |
| self._ensure_project_directory_available() | |
| self._create_project_directory() | |
| self._create_symlinks() | |
| self._setup_sphinx() | |
| self._update_sphinx_toctree() | |
| self._show_completion_message() | |
| def _ensure_project_directory_available(self) -> None: | |
| """Ensure the project directory doesn't exist or get a new name""" | |
| while self.project_dir.exists(): | |
| print(f'Project name is already taken. Please choose a different name.') | |
| self.project_name = input( | |
| f'{ProjectConfig.YELLOW}Enter a new project name: {ProjectConfig.RESET}') | |
| self.project_dir = self.cwd / self.project_name | |
| def _create_project_directory(self) -> None: | |
| """Create the initial project directory structure""" | |
| try: | |
| shutil.copytree(ProjectConfig.PJ_REPO_TEMPLATE, self.project_dir, dirs_exist_ok=False) | |
| print(f'{ProjectConfig.YELLOW}Project created at {self.project_dir}{ProjectConfig.RESET}') | |
| except OSError as e: | |
| print(f'{ProjectConfig.RED}Error creating project directory: {e}{ProjectConfig.RESET}') | |
| raise | |
| def _create_symlinks(self) -> None: | |
| """Create symlinks for all project directories""" | |
| alias_map = self._get_directory_aliases() | |
| for source, alias in alias_map.items(): | |
| try: | |
| alias.parent.mkdir(parents=True, exist_ok=True) | |
| alias.symlink_to(source, target_is_directory=True) | |
| print(f'Symlink created at {alias}.') | |
| except OSError as e: | |
| print(f'{ProjectConfig.RED}Error creating symlink: {e}{ProjectConfig.RESET}') | |
| def _get_directory_aliases(self) -> Dict[Path, Path]: | |
| """Generate mapping of source directories to their aliases""" | |
| aliases = { | |
| self.project_dir / dir_name: | |
| self.project_dir / ProjectConfig.DOCS_SOURCE_DIR / dir_name | |
| for dir_name in ProjectConfig.DIRECTORIES.values() | |
| } | |
| return {source.resolve(): alias.resolve() for source, alias in aliases.items()} | |
| def _setup_sphinx(self) -> None: | |
| """Initialize Sphinx documentation""" | |
| print(f'{ProjectConfig.YELLOW}\nCreating Sphinx directories.{ProjectConfig.RESET}') | |
| subprocess.run( | |
| ['sphinx-quickstart', 'docs', '-q', '--sep', | |
| '-p', self.project_name, '-a', ProjectConfig.AUTHOR], | |
| cwd=self.project_dir | |
| ) | |
| def _update_sphinx_toctree(self) -> None: | |
| """Update the Sphinx toctree with project directories""" | |
| index_file = self.project_dir / ProjectConfig.DOCS_SOURCE_DIR / 'index.rst' | |
| toctree_entries = ' ' + '\n '.join( | |
| f'{dir_name}/index' for dir_name in ProjectConfig.DIRECTORIES.values()) | |
| try: | |
| lines = index_file.read_text().splitlines() | |
| lines.insert(12, toctree_entries) | |
| index_file.write_text('\n'.join(lines) + '\n') | |
| except OSError as e: | |
| print(f'{ProjectConfig.RED}Error updating toctree: {e}{ProjectConfig.RESET}') | |
| def _show_completion_message(self) -> None: | |
| """Display final instructions to the user""" | |
| print(f"""\ | |
| To move to the new project directory, run the following command: | |
| {ProjectConfig.YELLOW}cd {self.project_dir}{ProjectConfig.RESET} | |
| """) | |
| def main() -> None: | |
| parser = argparse.ArgumentParser( | |
| prog='pj_quickstart', | |
| description='Quickstart Generic project') | |
| parser.add_argument('project_name', help='Name of the project') | |
| args = parser.parse_args() | |
| creator = ProjectCreator(args.project_name) | |
| creator.create() | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment