using System.CommandLine; using MycroForge.CLI.Commands.Interfaces; using MycroForge.CLI.Features; namespace MycroForge.CLI.Commands; public partial class MycroForge { #region GitIgnore private static readonly string[] GitIgnore = [ "# Byte-compiled / optimized / DLL files", "__pycache__/", "*.py[cod]", "*$py.class", "# C extensions", "*.so", "# Distribution / packaging", ".Python", "build/", "develop-eggs/", "dist/", "downloads/", "eggs/", ".eggs/", "lib/", "lib64/", "parts/", "sdist/", "var/", "wheels/", "share/python-wheels/", "*.egg-info/", ".installed.cfg", "*.egg", "MANIFEST", "# PyInstaller", "# Usually these files are written by a python script from a template", "# before PyInstaller builds the exe, so as to inject date/other infos into it.", "*.manifest", "*.spec", "# Installer logs", "pip-log.txt", "pip-delete-this-directory.txt", "# Unit test / coverage reports", "htmlcov/", ".tox/", ".nox/", ".coverage", ".coverage.*", ".cache", "nosetests.xml", "coverage.xml", "*.cover", "*.py,cover", ".hypothesis/", ".pytest_cache/", "cover/", "# Translations", "*.mo", "*.pot", "# Django stuff:", "*.log", "local_settings.py", "db.sqlite3", "db.sqlite3-journal", "# Flask stuff:", "instance/", ".webassets-cache", "# Scrapy stuff:", ".scrapy", "# Sphinx documentation", "docs/_build/", "# PyBuilder", ".pybuilder/", "target/", "# Jupyter Notebook", ".ipynb_checkpoints", "# IPython", "profile_default/", "ipython_config.py", "# pyenv", "# For a library or package, you might want to ignore these files since the code is", "# intended to run in multiple environments; otherwise, check them in:", "# .python-version", "# pipenv", "# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.", "# However, in case of collaboration, if having platform-specific dependencies or dependencies", "# having no cross-platform support, pipenv may install dependencies that don't work, or not", "# install all needed dependencies.", "#Pipfile.lock", "# poetry", "# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.", "# This is especially recommended for binary packages to ensure reproducibility, and is more", "# commonly ignored for libraries.", "# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control", "#poetry.lock", "# pdm", "# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.", "#pdm.lock", "# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it", "# in version control.", "# https://pdm.fming.dev/#use-with-ide", ".pdm.toml", "# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm", "__pypackages__/", "# Celery stuff", "celerybeat-schedule", "celerybeat.pid", "# SageMath parsed files", "*.sage.py", "# Environments", ".env", ".venv", "env/", "venv/", "ENV/", "env.bak/", "venv.bak/", "# Spyder project settings", ".spyderproject", ".spyproject", "# Rope project settings", ".ropeproject", "# mkdocs documentation", "/site", "# mypy", ".mypy_cache/", ".dmypy.json", "dmypy.json", "# Pyre type checker", ".pyre/", "# pytype static type analyzer", ".pytype/", "# Cython debug symbols", "cython_debug/", "# PyCharm", "# JetBrains specific template is maintained in a separate JetBrains.gitignore that can", "# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore", "# and can be added to the global gitignore or merged into this file. For a more nuclear", "# option (not recommended) you can uncomment the following to ignore the entire idea folder.", "#.idea/" ]; #endregion public class Init : Command, ISubCommandOf { private static readonly string[] DefaultFeatures = [ Features.Git.FeatureName, Features.Api.FeatureName, Features.Db.FeatureName ]; private static readonly Argument NameArgument = new(name: "name", description: "The name of your project"); private static readonly Option> WithoutOption = new Option>(name: "--without", description: "Features to exclude") { AllowMultipleArgumentsPerToken = true }.FromAmong(DefaultFeatures); private readonly ProjectContext _context; private readonly List _features; public Init(ProjectContext context, IEnumerable features) : base("init", "Initialize a new project") { _context = context; _features = features.ToList(); AddArgument(NameArgument); AddOption(WithoutOption); this.SetHandler(ExecuteAsync, NameArgument, WithoutOption); } private async Task ExecuteAsync(string name, IEnumerable without) { // Validate excluded features var withoutList = without.ToList(); foreach (var feature in withoutList) if (_features.All(f => f.Name != feature)) throw new Exception($"Feature {feature} does not exist."); // Create the project directory and change the directory for the ProjectContext var projectRoot = await CreateDirectory(name); _context.ChangeDirectory(projectRoot); // Create the config file and initialize the config await _context.CreateFile("m4g.json", "{}"); // Create the entrypoint file await _context.CreateFile("main.py"); // Create the default .gitignore folder await _context.CreateFile(".gitignore", GitIgnore); // Create the venv await _context.Bash($"python3 -m venv {Path.Combine(projectRoot, ".venv")}"); // Initialize default features foreach (var feature in _features.Where(f => DefaultFeatures.Contains(f.Name))) { if (!withoutList.Contains(feature.Name)) { Console.WriteLine($"Initializing feature {feature.Name}"); await feature.ExecuteAsync(_context); } else { Console.WriteLine($"Skipping feature {feature.Name}"); } } Console.WriteLine($"Directory {projectRoot} was successfully initialized"); } private async Task CreateDirectory(string name) { var directory = Path.Combine(Directory.GetCurrentDirectory(), name); if (Directory.Exists(directory)) throw new Exception($"Directory {directory} already exists."); Console.WriteLine($"Creating directory {directory}"); Directory.CreateDirectory(directory); await _context.Bash($"chmod -R 777 {directory}"); return directory; } } }