|
| 1 | +import os |
| 2 | +import re |
| 3 | +import shutil |
| 4 | +from pathlib import Path |
| 5 | + |
| 6 | +from click import ClickException, UsageError, command, echo, option |
| 7 | + |
| 8 | + |
| 9 | +@command() |
| 10 | +@option("--name", required=True, help="Project new name") |
| 11 | +@option("--description", required=True, help="Project short description") |
| 12 | +@option("--author", required=True, help="Author name") |
| 13 | +@option("--email", required=True, help="Author email") |
| 14 | +@option("--github", required=True, help="GitHub username") |
| 15 | +def main(name: str, description: str, author: str, email: str, github: str): |
| 16 | + # Validate name to prevent directory traversal or other injection |
| 17 | + if not re.match(r"^[a-zA-Z0-9_-]+$", name): |
| 18 | + raise UsageError( |
| 19 | + f"Invalid project name '{name}'. Only alphanumeric characters, dashes, and underscores are allowed." |
| 20 | + ) |
| 21 | + |
| 22 | + source = name.replace("-", "_").lower() |
| 23 | + |
| 24 | + echo(f"Initializing project '{name}' (source: '{source}')...") |
| 25 | + |
| 26 | + # 1. Rename project directory |
| 27 | + if os.path.isdir("project"): |
| 28 | + shutil.move("project", source) |
| 29 | + elif not os.path.isdir(source): |
| 30 | + raise ClickException(f"Error: Neither 'project' nor '{source}' directory found.") |
| 31 | + |
| 32 | + # 2. File modifications |
| 33 | + replacements = [ |
| 34 | + ("docs/reference/app.md", r"^::: project\.app", f"::: {source}.app"), |
| 35 | + ("mkdocs.yml", r"^repo_name: .*", f"repo_name: {github}/{name}"), |
| 36 | + ("mkdocs.yml", r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"), |
| 37 | + ("pyproject.toml", r"^source = \[.*\]", f'source = ["{source}"]'), |
| 38 | + ("pyproject.toml", r'^app = "project\.app:main"', f'app = "{source}.app:main"'), |
| 39 | + ("pyproject.toml", r'^name = ".*"', f'name = "{source}"'), |
| 40 | + ("pyproject.toml", r'^description = ".*"', f'description = "{description}"'), |
| 41 | + ("pyproject.toml", r"^authors = \[.*\]", f'authors = ["{author} <{email}>"]'), |
| 42 | + ("docs/README.md", r"^# .*", f"# {description}"), |
| 43 | + (".github/CODEOWNERS", r"@.*", f"@{github}"), |
| 44 | + (".github/FUNDING.yml", r"^github: \[.*\]", f"github: [{github}]"), |
| 45 | + ] |
| 46 | + |
| 47 | + for filepath, pattern, replacement in replacements: |
| 48 | + path = Path(filepath) |
| 49 | + if not path.exists(): |
| 50 | + echo(f"Warning: File {filepath} not found, skipping.") |
| 51 | + continue |
| 52 | + |
| 53 | + content = path.read_text() |
| 54 | + new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE) |
| 55 | + path.write_text(new_content) |
| 56 | + echo(f"Updated {filepath}") |
| 57 | + |
| 58 | + echo("Project initialization complete.") |
| 59 | + |
| 60 | + |
| 61 | +if __name__ == "__main__": |
| 62 | + main() |
0 commit comments