diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6023235..98e47c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,11 @@ on: jobs: build: if: github.event_name == 'push' && contains(toJson(github.event.commits), '***NO_CI***') == false && contains(toJson(github.event.commits), '[ci skip]') == false && contains(toJson(github.event.commits), '[skip ci]') == false - runs-on: windows-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + dotnet: ['8.0.x', '9.0.x'] + runs-on: ${{ matrix.os }} env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -39,23 +43,30 @@ jobs: - name: setup .net core sdk uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 8.0.x - 9.0.x + dotnet-version: ${{ matrix.dotnet }} - name: dotnet build run: dotnet build solrevdev.seedfolder.sln --configuration Release + - name: run integration tests (linux/mac) + if: matrix.os != 'windows-latest' + run: ./tests/integration-test.sh + + - name: run integration tests (windows) + if: matrix.os == 'windows-latest' + run: powershell -File tests/integration-test.ps1 + - name: dotnet pack run: dotnet pack solrevdev.seedfolder.sln -c Release --no-build --include-source --include-symbols - name: setup nuget - if: github.event_name == 'push' && github.ref == 'refs/heads/master' + if: github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.os == 'ubuntu-latest' && matrix.dotnet == '8.0.x' uses: nuget/setup-nuget@v1 with: nuget-version: latest - name: Publish NuGet + if: github.event_name == 'push' && github.ref == 'refs/heads/master' && matrix.os == 'ubuntu-latest' && matrix.dotnet == '8.0.x' uses: rohith/publish-nuget@v2.1.1 with: PROJECT_FILE_PATH: src/solrevdev.seedfolder.csproj # Relative to repository root diff --git a/README.md b/README.md index 1d8102c..2562a31 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![GitHub last commit](https://img.shields.io/github/last-commit/solrevdev/seedfolder)](https://github.com/solrevdev/seedfolder) [![CI](https://github.com/solrevdev/seedfolder/workflows/CI/badge.svg)](https://github.com/solrevdev/seedfolder) [![Twitter Follow](https://img.shields.io/twitter/follow/solrevdev?label=Follow&style=social)](https://twitter.com/solrevdev) - ``` _ __ _ _ ___ ___ ___ __| |/ _| ___ | | __| | ___ _ __ @@ -11,100 +10,532 @@ |___/\___|\___|\__,_|_| \___/|_|\__,_|\___|_| ``` - ## Overview -This is a .NET Core Global Tool that will create a folder named after either the first argument passed to it or if no -argument is passed it will ask you for the folder name. It will then copy some default standard dotfiles over. +**SeedFolder** is a powerful .NET Global Tool that creates project directories and seeds them with curated template files for different project types. Whether you're starting a .NET project, Node.js app, Python script, Ruby gem, or documentation project, SeedFolder provides the right foundation with just one command. + +🚀 **Multi-Template System** - Support for 6 different project types +⚡ **Professional CLI** - Comprehensive command-line interface with dry-run, force mode, and more +🌐 **Cross-Platform** - Works on Windows, macOS, and Linux +🔄 **Interactive Mode** - Guided project creation with template selection +📊 **Progress Tracking** - Real-time feedback with visual progress indicators + +## Quick Start + +```bash +# Install globally +dotnet tool install --global solrevdev.seedfolder + +# Interactive mode - choose template and folder name +seedfolder + +# Create specific project types +seedfolder --template node myapp +seedfolder -t python data-analysis +seedfolder --type ruby my_gem + +# Preview before creating +seedfolder --dry-run -t node myapp +``` + +## Supported Project Templates + +### 📝 **markdown** - Documentation Project (Default) +Documentation and content projects: +- `README.md` - Project documentation template +- `.gitignore` - Documentation specific git ignore patterns +- `.gitattributes` - Documentation-focused git attributes with LFS for images/videos +- `.editorconfig` - Documentation optimized editor configuration + +### 🏗️ **dotnet** - .NET Project +Complete .NET development environment with standard dotfiles: +- `.dockerignore` - Docker ignore patterns +- `.editorconfig` - Comprehensive C# editor configuration with .NET naming conventions +- `.gitattributes` - Comprehensive .NET git attributes with LFS for binaries and C# language detection +- `.gitignore` - .NET specific git ignore patterns +- `.prettierignore` - Prettier ignore patterns +- `.prettierrc` - Prettier configuration +- `omnisharp.json` - OmniSharp configuration -For example: +### 📦 **node** - Node.js Project +Modern Node.js project setup: +- `package.json` - Node.js package configuration +- `index.js` - Main application entry point +- `.gitignore` - Node.js specific git ignore patterns +- `.gitattributes` - JavaScript/TypeScript focused git attributes with web asset handling +- `.editorconfig` - JavaScript/TypeScript optimized editor configuration +- `.prettierignore` - Prettier ignore patterns +- `.prettierrc` - Prettier configuration +### 🐍 **python** - Python Project +Python development environment: +- `main.py` - Main application entry point +- `requirements.txt` - Python dependencies +- `.gitignore` - Python specific git ignore patterns +- `.gitattributes` - Python-focused git attributes with LFS for wheels, data files, and ML models +- `.editorconfig` - PEP 8 compliant editor configuration + +### 💎 **ruby** - Ruby Project +Ruby development setup: +- `Gemfile` - Ruby dependencies +- `main.rb` - Main application entry point +- `.gitignore` - Ruby specific git ignore patterns +- `.gitattributes` - Ruby-focused git attributes with gem handling and ERB template support +- `.editorconfig` - Ruby standard editor configuration + +### 🌐 **universal** - Basic Project +Minimal project setup for any use case: +- `README.md` - Project documentation template +- `.gitignore` - Basic git ignore patterns +- `.gitattributes` - Conservative cross-platform git attributes with basic binary handling +- `.editorconfig` - Universal editor configuration + +## Command Line Interface + +``` +Usage: seedfolder [options] [folderName] + +Options: + --help, -h, -? Show this help message + --version, -v Show version information + --list-templates Show available template files + --template, --type, -t Specify project template type + --dry-run, --dry Preview operations without creating files + --force, -f Overwrite existing directory and files + --quiet, -q Suppress output (useful for scripting) + +Arguments: + folderName Name of the folder to create (optional) + +Template Types: + markdown Documentation project with README (default) + dotnet .NET project with standard dotfiles + node Node.js project with package.json + python Python project with requirements.txt + ruby Ruby project with Gemfile + universal Basic project with minimal files +``` + +## Usage Examples + +### Interactive Mode ```bash +# Start interactive mode with template selection seedfolder -dotnet run --project src/solrevdev.seedfolder.csproj --framework net8.0 + +# Example interactive session: + _ __ _ _ + ___ ___ ___ __| |/ _| ___ | | __| | ___ _ __ + / __|/ _ \/ _ \/ _` | |_ / _ \| |/ _` |/ _ \ '__| + \__ \ __/ __/ (_| | _| (_) | | (_| | __/ | + |___/\___|\___|\__,_|_| \___/|_|\__,_|\___|_| + +▲ Running in the path /current/directory +▲ Available project templates: + 1. markdown - Documentation project with README + 2. dotnet - .NET project with standard dotfiles + 3. node - Node.js project with package.json + 4. python - Python project with requirements.txt + 5. ruby - Ruby project with Gemfile + 6. universal - Basic project with minimal files + +▲ Select template type (1-6) or press Enter for markdown: 3 +▲ Selected template: Node ▲ Do you want to prefix the folder with the date? [Y/n] y -▲ What do you want the folder to be named? temp -‍▲ Creating the directory 2020-12-10_temp -‍▲ Copying .dockerignore to 2020-12-10_temp/.dockerignore -‍▲ Copying .editorconfig to 2020-12-10_temp/.editorconfig -‍▲ Copying .gitattributes to 2020-12-10_temp/.gitattributes -‍▲ Copying .gitignore to 2020-12-10_temp/.gitignore -‍▲ Copying .prettierignore to 2020-12-10_temp/.prettierignore -‍▲ Copying .prettierrc to 2020-12-10_temp/.prettierrc -‍▲ Copying omnisharp.json to 2020-12-10_temp/omnisharp.json +▲ What do you want the folder to be named? my-awesome-app +‍▲ Creating the directory 2024-01-15_my-awesome-app +‍▲ [1/7] Copying package.json + ✅ Created 2024-01-15_my-awesome-app/package.json +‍▲ [2/7] Copying index.js + ✅ Created 2024-01-15_my-awesome-app/index.js +‍▲ [3/7] Copying .gitignore + ✅ Created 2024-01-15_my-awesome-app/.gitignore +‍▲ [4/7] Copying .gitattributes + ✅ Created 2024-01-15_my-awesome-app/.gitattributes +‍▲ [5/7] Copying .editorconfig + ✅ Created 2024-01-15_my-awesome-app/.editorconfig +‍▲ [6/7] Copying .prettierignore + ✅ Created 2024-01-15_my-awesome-app/.prettierignore +‍▲ [7/7] Copying .prettierrc + ✅ Created 2024-01-15_my-awesome-app/.prettierrc ▲ Done! +▲ Successfully created 7 files in '2024-01-15_my-awesome-app' using Node template. -seedfolder -dotnet run --project src/solrevdev.seedfolder.csproj --framework net9.0 -▲ Do you want to prefix the folder with the date? [Y/n] n -▲ What do you want the folder to be named? temp -‍▲ Creating the directory temp -‍▲ Copying .dockerignore to temp/.dockerignore -‍▲ Copying .editorconfig to temp/.editorconfig -‍▲ Copying .gitattributes to temp/.gitattributes -‍▲ Copying .gitignore to temp/.gitignore -‍▲ Copying .prettierignore to temp/.prettierignore -‍▲ Copying .prettierrc to temp/.prettierrc -‍▲ Copying omnisharp.json to temp/omnisharp.json +▲ To initialize git and make your first commit, copy and paste these commands: + +cd "2024-01-15_my-awesome-app" +git init +git lfs install 2>/dev/null || echo "Git LFS not available" +git add . +git commit -m "Initial commit" +``` + +### Direct Template Selection +```bash +# Create different project types +seedfolder --template node myapp +▲ Using template type: Node + _ __ _ _ + ___ ___ ___ __| |/ _| ___ | | __| | ___ _ __ + / __|/ _ \/ _ \/ _` | |_ / _ \| |/ _` |/ _ \ '__| + \__ \ __/ __/ (_| | _| (_) | | (_| | __/ | + |___/\___|\___|\__,_|_| \___/|_|\__,_|\___|_| + +▲ Running in the path /current/directory +‍▲ Creating the directory myapp +‍▲ [1/7] Copying package.json + ✅ Created myapp/package.json +‍▲ [2/7] Copying index.js + ✅ Created myapp/index.js +‍▲ [3/7] Copying .gitignore + ✅ Created myapp/.gitignore +‍▲ [4/7] Copying .gitattributes + ✅ Created myapp/.gitattributes +‍▲ [5/7] Copying .editorconfig + ✅ Created myapp/.editorconfig +‍▲ [6/7] Copying .prettierignore + ✅ Created myapp/.prettierignore +‍▲ [7/7] Copying .prettierrc + ✅ Created myapp/.prettierrc ▲ Done! +▲ Successfully created 7 files in 'myapp' using Node template. + +▲ To initialize git and make your first commit, copy and paste these commands: + +cd "myapp" +git init +git lfs install 2>/dev/null || echo "Git LFS not available" +git add . +git commit -m "Initial commit" + +# Create Python project (spaces converted to dashes) +seedfolder -t python "machine learning project" +▲ Using template type: Python + _ __ _ _ + ___ ___ ___ __| |/ _| ___ | | __| | ___ _ __ + / __|/ _ \/ _ \/ _` | |_ / _ \| |/ _` |/ _ \ '__| + \__ \ __/ __/ (_| | _| (_) | | (_| | __/ | + |___/\___|\___|\__,_|_| \___/|_|\__,_|\___|_| + +▲ Running in the path /current/directory +‍▲ Creating the directory machine-learning-project +‍▲ [1/5] Copying main.py + ✅ Created machine-learning-project/main.py +‍▲ [2/5] Copying requirements.txt + ✅ Created machine-learning-project/requirements.txt +‍▲ [3/5] Copying .gitignore + ✅ Created machine-learning-project/.gitignore +‍▲ [4/5] Copying .gitattributes + ✅ Created machine-learning-project/.gitattributes +‍▲ [5/5] Copying .editorconfig + ✅ Created machine-learning-project/.editorconfig +▲ Done! +▲ Successfully created 5 files in 'machine-learning-project' using Python template. + +▲ To initialize git and make your first commit, copy and paste these commands: + +cd "machine-learning-project" +git init +git lfs install 2>/dev/null || echo "Git LFS not available" +git add . +git commit -m "Initial commit" + +# Other examples +seedfolder --type ruby my_gem_name # Creates Ruby project +seedfolder -t markdown my-docs # Creates documentation project +seedfolder --template universal basic-project # Creates universal project + +# Using folder name argument (skips interactive mode) +seedfolder myproject # Creates markdown project (default) +seedfolder --template python myapp # Creates Python project +``` + +### Preview and Force Operations +```bash +# Preview what would be created (dry-run mode) +seedfolder --dry-run -t node myapp +▲ Using template type: Node + _ __ _ _ + ___ ___ ___ __| |/ _| ___ | | __| | ___ _ __ + / __|/ _ \/ _ \/ _` | |_ / _ \| |/ _` |/ _ \ '__| + \__ \ __/ __/ (_| | _| (_) | | (_| | __/ | + |___/\___|\___|\__,_|_| \___/|_|\__,_|\___|_| + +▲ Running in the path /current/directory +▲ DRY RUN: Would create directory 'myapp' with template 'Node' +▲ Files that would be created: + • myapp/package.json + • myapp/index.js + • myapp/.gitignore + • myapp/.gitattributes + • myapp/.editorconfig + • myapp/.prettierignore + • myapp/.prettierrc +▲ Use without --dry-run to actually create the files. -seedfolder temp -dotnet run --project src/solrevdev.seedfolder.csproj --framework net8.0 temp -▲ Found 1 params to process. -‍▲ Creating the directory temp -‍▲ Copying .dockerignore to temp/.dockerignore -‍▲ Copying .editorconfig to temp/.editorconfig -‍▲ Copying .gitattributes to temp/.gitattributes -‍▲ Copying .gitignore to temp/.gitignore -‍▲ Copying .prettierignore to temp/.prettierignore -‍▲ Copying .prettierrc to temp/.prettierrc -‍▲ Copying omnisharp.json to temp/omnisharp.json +# Force overwrite existing directory +seedfolder --force existing-directory +▲ Warning: Directory 'existing-directory' exists, will overwrite files. +‍▲ Creating the directory existing-directory +‍▲ [1/4] Copying README.md + ✅ Created existing-directory/README.md +‍▲ [2/4] Copying .gitignore + ✅ Created existing-directory/.gitignore +‍▲ [3/4] Copying .gitattributes + ✅ Created existing-directory/.gitattributes +‍▲ [4/4] Copying .editorconfig + ✅ Created existing-directory/.editorconfig ▲ Done! +▲ Successfully created 4 files in 'existing-directory' using Markdown template. + +▲ To initialize git and make your first commit, copy and paste these commands: + +cd "existing-directory" +git init +git lfs install 2>/dev/null || echo "Git LFS not available" +git add . +git commit -m "Initial commit" + +# Quiet mode for scripting (no output shown) +seedfolder --quiet -t node myapp +``` + +### Template Information +```bash +# List all available templates with file details +seedfolder --list-templates +▲ Available project templates: + + markdown - Documentation project with README + • README.md Project documentation + • .gitignore Documentation specific git ignore patterns + • .gitattributes Git attributes for documentation projects + • .editorconfig Editor configuration for Markdown + + dotnet - Dotnet project with standard dotfiles + • .dockerignore Docker ignore patterns + • .editorconfig Editor configuration for .NET + • .gitattributes Git attributes + • .gitignore Git ignore patterns + • .prettierignore Prettier ignore patterns + • .prettierrc Prettier configuration + • omnisharp.json OmniSharp configuration + + node - Node.js project with package.json + • package.json Node.js package configuration + • index.js Main application entry point + • .gitignore Node.js specific git ignore patterns + • .gitattributes Git attributes for Node.js projects + • .editorconfig Editor configuration for Node.js + • .prettierignore Prettier ignore patterns + • .prettierrc Prettier configuration + python - Python project with requirements.txt + • main.py Main application entry point + • requirements.txt Python dependencies + • .gitignore Python specific git ignore patterns + • .gitattributes Git attributes for Python projects + • .editorconfig Editor configuration for Python + + ruby - Ruby project with Gemfile + • Gemfile Ruby dependencies + • main.rb Main application entry point + • .gitignore Ruby specific git ignore patterns + • .gitattributes Git attributes for Ruby projects + • .editorconfig Editor configuration for Ruby + + universal - Basic project with minimal files + • README.md Project documentation + • .gitignore Basic git ignore patterns + • .gitattributes Git attributes for universal projects + • .editorconfig Editor configuration for universal projects + +▲ Usage examples: + seedfolder --template node myproject + seedfolder -t python myapp + seedfolder --type ruby mygem + +# Show version information +seedfolder --version +▲ seedfolder version 1.3.1 + +# Show help +seedfolder --help +▲ seedfolder version 1.3.1 + +▲ Usage: seedfolder [options] [folderName] + +Options: + --help, -h, -? Show this help message + --version, -v Show version information + --list-templates Show available template files + --template, --type, -t Specify project template type + --dry-run, --dry Preview operations without creating files + --force, -f Overwrite existing directory and files + --quiet, -q Suppress output (useful for scripting) + +Arguments: + folderName Name of the folder to create (optional) + +Template Types: + dotnet .NET project with standard dotfiles + node Node.js project with package.json + python Python project with requirements.txt + ruby Ruby project with Gemfile + markdown Documentation project with README (default) + universal Basic project with minimal files + +Examples: + seedfolder # Interactive mode with template selection + seedfolder myproject # Create 'myproject' folder with markdown template + seedfolder --template node myapp # Create Node.js project + seedfolder -t python "my project" # Create Python project (spaces converted to dashes) + seedfolder --type ruby mygem # Create Ruby project + seedfolder --dry-run -t node myapp # Preview Node.js project creation + seedfolder --force myproject # Overwrite existing 'myproject' directory + seedfolder --quiet -t python myapp # Create Python project with no output ``` -It will also copy the following dotfiles from the `src/Data` folder over: +## Advanced Features -* .dockerignore -* .editorconfig -* .gitattributes -* .gitignore -* .prettierignore -* .prettierrc +### 🛡️ **Error Handling & Validation** +- **Disk Space Validation** - Checks for minimum 10MB free space before operations +- **Path Validation** - Automatically sanitizes folder names and handles invalid characters +- **Permission Checks** - Graceful handling of permission errors with actionable suggestions +- **Rollback Support** - Failed operations provide clear rollback information + +### 📊 **Progress Indicators** +Real-time progress tracking with visual feedback: +```bash +‍▲ [1/5] Copying main.py + ✅ Created test-python/main.py +‍▲ [2/5] Copying requirements.txt + ✅ Created test-python/requirements.txt +‍▲ [3/5] Copying .gitignore + ✅ Created test-python/.gitignore +‍▲ [4/5] Copying .gitattributes + ✅ Created test-python/.gitattributes +‍▲ [5/5] Copying .editorconfig + ✅ Created test-python/.editorconfig +▲ Done! +▲ Successfully created 5 files in 'test-python' using Python template. + +▲ To initialize git and make your first commit, copy and paste these commands: + +cd "test-python" +git init +git lfs install 2>/dev/null || echo "Git LFS not available" +git add . +git commit -m "Initial commit" +``` + +### 🔄 **Smart Input Handling** +- **Space Conversion** - Spaces in folder names automatically converted to dashes +- **Path Sanitization** - Invalid filesystem characters replaced with safe alternatives +- **Date Prefixing** - Optional YYYY-MM-DD prefix for organized project structure ## Requirements -This tool requires .NET 8.0 or .NET 9.0 SDK to be installed on your system. +This tool requires **.NET 8.0 or .NET 9.0 SDK** to be installed on your system. + +- **Supported Platforms**: Windows, macOS, Linux +- **Runtime**: .NET 8.0 or later +- **Installation**: Via .NET Global Tools ## Installation -Locally without publishing it on NuGet +### From NuGet (Recommended) +```bash +dotnet tool install --global solrevdev.seedfolder +``` -```powershell +### Local Development Installation +```bash +# Clone the repository +git clone https://github.com/solrevdev/seedfolder.git +cd seedfolder -dotnet pack -dotnet tool install --global --add-source ./nupkg solrevdev.seedfolder +# Build and install locally +dotnet pack -c release -o artifacts/nupkg +dotnet tool uninstall -g solrevdev.seedfolder # If previously installed +dotnet tool install -g --add-source artifacts/nupkg solrevdev.seedfolder +``` +### Uninstall +```bash +dotnet tool uninstall --global solrevdev.seedfolder ``` -Normally via NuGet +## Development -```powershell -dotnet tool install --global solrevdev.seedfolder +### Local Testing +```bash +## Run directly during development + +# Interactive mode +dotnet run --project src/solrevdev.seedfolder.csproj --framework net9.0 + +## With arguments + +# Show the application's version (pass `--version` to the app via `--`) +dotnet run --project src/solrevdev.seedfolder.csproj --framework net9.0 -- --version + +# Show the application's help text (pass `--help` to the app via `--`) +dotnet run --project src/solrevdev.seedfolder.csproj --framework net9.0 -- --help + +# Create a Node template project using the app (arguments after `--` are for the app) +dotnet run --project src/solrevdev.seedfolder.csproj --framework net9.0 -- --template node myapp + +# If you omit `--` and run `dotnet run --project ... --help`, the .NET CLI help will be shown. ``` -To uninstall +### Building and Testing +```bash +# Build the project +dotnet build -```powershell -dotnet tool uninstall -g solrevdev.seedfolder +# Run tests +dotnet test + +# Create package +dotnet pack -c release ``` -## Publish to Nuget +## Roadmap + +### ✅ Completed (v1.3.x) +- Multi-template system with 6 project types +- Professional CLI interface with comprehensive help +- Cross-platform testing and CI/CD +- Interactive template selection +- Dry-run mode and force operations +- Progress indicators and enhanced error handling + +### 🔮 Future Enhancements +- **External Template Sources** - Support for custom template directories +- **Configuration File Support** - User preferences and default settings +- **Template Management System** - Validation, versioning, and template marketplace -Uses a GitHub secret to store a `NUGET_API_KEY` API Key created over at NuGet in order to build and deploy via GitHub actions to NuGet. +## Contributing -When you commit bump the version in the `csproj` file +Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. + +## Publishing to NuGet + +The project uses GitHub Actions for automatic publishing to NuGet. Simply bump the version in `src/solrevdev.seedfolder.csproj`: ```xml -1.0.1 +1.3.2 ``` + +Commit to the `master` branch and GitHub Actions will automatically build and publish to NuGet. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Support + +- 🐛 **Issues**: [GitHub Issues](https://github.com/solrevdev/seedfolder/issues) +- 💬 **Discussions**: [GitHub Discussions](https://github.com/solrevdev/seedfolder/discussions) +- 🐦 **Twitter**: [@solrevdev](https://twitter.com/solrevdev) diff --git a/src/Data/Gemfile b/src/Data/Gemfile new file mode 100644 index 0000000..02a2c57 --- /dev/null +++ b/src/Data/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'bundler' \ No newline at end of file diff --git a/src/Data/README.md b/src/Data/README.md new file mode 100644 index 0000000..bae4ea1 --- /dev/null +++ b/src/Data/README.md @@ -0,0 +1,19 @@ +# Project Name + +Brief description of your project. + +## Installation + +Instructions for installing and setting up your project. + +## Usage + +Examples of how to use your project. + +## Contributing + +Guidelines for contributing to the project. + +## License + +Information about the project license. \ No newline at end of file diff --git a/src/Data/editorconfig-dotnet b/src/Data/editorconfig-dotnet new file mode 100644 index 0000000..6da0e9d --- /dev/null +++ b/src/Data/editorconfig-dotnet @@ -0,0 +1,201 @@ +# EditorConfig is awesome: +http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +guidelines = 140 + + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# only use var when it's obvious what the variable type is +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# use language keywords instead of BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# static fields should have _ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_prefix_style.required_prefix = _ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +dotnet_sort_system_directives_first = true +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false +dotnet_separate_import_directive_groups = false + +# IDE0005: Remove unnessecary using statements +dotnet_diagnostic.IDE0005.severity = error + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion + +# Inlined variable declarations +csharp_style_inlined_variable_declaration = true : suggestion + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# ---- Latest csharp features ----- # + +# new using statement +csharp_prefer_simple_using_statement = false:suggestion + +# Simplify new expression (IDE0090) +csharp_style_implicit_object_creation_when_type_is_apparent = false:suggestion + +# Use range operator (IDE0057) +csharp_style_prefer_range_operator = false:suggestion +csharp_style_prefer_index_operator = false:suggestion + +# file scoped namespaces +csharp_style_namespace_declarations = file_scoped +dotnet_diagnostic.IDE0161.severity = error + +# async methods should have "Async" suffix +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async +dotnet_naming_rule.async_methods_end_in_async.severity = error + +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +dotnet_naming_style.end_in_async.required_prefix = +dotnet_naming_style.end_in_async.required_suffix = Async +dotnet_naming_style.end_in_async.capitalization = pascal_case +dotnet_naming_style.end_in_async.word_separator = + +# IDE0290 Use primary constructor +# https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#primary-constructors +# https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/primary-constructors +# will disable this rule for now until I decide if I like it and want to use it. +# dotnet_diagnostic.IDE0290.severity = none | error | suggestion + +# IDE0290 Use primary constructor +dotnet_diagnostic.IDE0290.severity = none + +# ---- Latest csharp features ----- # + +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = none + +[*.{asm,inc}] +indent_size = 8 + +# Xml project files +[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +[CMakeLists.txt] +indent_size = 2 + +[*.cmd] +indent_size = 2 diff --git a/src/Data/editorconfig-markdown b/src/Data/editorconfig-markdown new file mode 100644 index 0000000..579335e --- /dev/null +++ b/src/Data/editorconfig-markdown @@ -0,0 +1,27 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 2 spaces as indentation for documentation projects +[*] +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true + +# Markdown files +[*.{md,markdown}] +trim_trailing_whitespace = false +indent_size = 2 + +# YAML frontmatter in Markdown +[*.{yml,yaml}] +indent_size = 2 + +# JSON files (for configuration) +[*.json] +indent_size = 2 \ No newline at end of file diff --git a/src/Data/editorconfig-node b/src/Data/editorconfig-node new file mode 100644 index 0000000..61e5625 --- /dev/null +++ b/src/Data/editorconfig-node @@ -0,0 +1,34 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 2 spaces as indentation for Node.js projects +[*] +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true + +# JavaScript files +[*.{js,jsx,ts,tsx}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 + +# YAML files +[*.{yml,yaml}] +indent_size = 2 + +# Markdown files +[*.md] +trim_trailing_whitespace = false + +# Package files +[package.json] +indent_size = 2 \ No newline at end of file diff --git a/src/Data/editorconfig-python b/src/Data/editorconfig-python new file mode 100644 index 0000000..8a3bf2a --- /dev/null +++ b/src/Data/editorconfig-python @@ -0,0 +1,36 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation (PEP 8) +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = 88 + +# Python files +[*.py] +indent_size = 4 +max_line_length = 88 + +# YAML files +[*.{yml,yaml}] +indent_size = 2 + +# TOML files (pyproject.toml, etc.) +[*.toml] +indent_size = 4 + +# Requirements files +[requirements*.txt] +indent_size = 4 + +# Markdown files +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/src/Data/editorconfig-ruby b/src/Data/editorconfig-ruby new file mode 100644 index 0000000..0e5487d --- /dev/null +++ b/src/Data/editorconfig-ruby @@ -0,0 +1,34 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 2 spaces as indentation (Ruby standard) +[*] +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true + +# Ruby files +[*.{rb,rake}] +indent_size = 2 + +# Gemfile and related files +[{Gemfile,Rakefile,*.gemspec}] +indent_size = 2 + +# YAML files +[*.{yml,yaml}] +indent_size = 2 + +# ERB templates +[*.erb] +indent_size = 2 + +# Markdown files +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/src/Data/editorconfig-universal b/src/Data/editorconfig-universal new file mode 100644 index 0000000..b775407 --- /dev/null +++ b/src/Data/editorconfig-universal @@ -0,0 +1,26 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 2 spaces as indentation (universal default) +[*] +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true + +# Markdown files +[*.md] +trim_trailing_whitespace = false + +# YAML files +[*.{yml,yaml}] +indent_size = 2 + +# JSON files +[*.json] +indent_size = 2 \ No newline at end of file diff --git a/src/Data/gitattributes-markdown b/src/Data/gitattributes-markdown new file mode 100644 index 0000000..6988e7b --- /dev/null +++ b/src/Data/gitattributes-markdown @@ -0,0 +1,45 @@ +############################### +# Git Line Endings # +############################### + +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use lf line endings +*.sh text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Images (common in documentation) +*.gif filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text + +# Documents +*.pdf filter=lfs diff=lfs merge=lfs -text + +# Videos (for documentation) +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text + +# Markdown language detection +*.md linguist-language=Markdown +*.markdown linguist-language=Markdown +*.mdown linguist-language=Markdown +*.mkd linguist-language=Markdown +*.mkdn linguist-language=Markdown +*.mdx linguist-language=MDX \ No newline at end of file diff --git a/src/Data/gitattributes-node b/src/Data/gitattributes-node new file mode 100644 index 0000000..9a57c27 --- /dev/null +++ b/src/Data/gitattributes-node @@ -0,0 +1,46 @@ +############################### +# Git Line Endings # +############################### + +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use lf line endings +*.sh text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.br filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text + +# Fonts +*.woff filter=lfs diff=lfs merge=lfs -text +*.woff2 filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.eot filter=lfs diff=lfs merge=lfs -text + +# Videos +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text + +# JavaScript/TypeScript language detection +*.ts linguist-language=TypeScript +*.tsx linguist-language=TypeScript +*.vue linguist-language=Vue \ No newline at end of file diff --git a/src/Data/gitattributes-python b/src/Data/gitattributes-python new file mode 100644 index 0000000..fa16fce --- /dev/null +++ b/src/Data/gitattributes-python @@ -0,0 +1,52 @@ +############################### +# Git Line Endings # +############################### + +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use lf line endings +*.sh text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Python wheels and eggs +*.whl filter=lfs diff=lfs merge=lfs -text +*.egg filter=lfs diff=lfs merge=lfs -text + +# Data files (common in Python projects) +*.csv filter=lfs diff=lfs merge=lfs -text +*.json filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.hdf5 filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text + +# Models and datasets +*.model filter=lfs diff=lfs merge=lfs -text +*.weights filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text + +# Python language detection +*.py linguist-language=Python +*.pyx linguist-language=Python +*.pxd linguist-language=Python +*.pxi linguist-language=Python \ No newline at end of file diff --git a/src/Data/gitattributes-ruby b/src/Data/gitattributes-ruby new file mode 100644 index 0000000..619fae9 --- /dev/null +++ b/src/Data/gitattributes-ruby @@ -0,0 +1,43 @@ +############################### +# Git Line Endings # +############################### + +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use lf line endings +*.sh text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Ruby gems +*.gem filter=lfs diff=lfs merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text + +# Documents +*.pdf filter=lfs diff=lfs merge=lfs -text + +# Ruby language detection +*.rb linguist-language=Ruby +*.rake linguist-language=Ruby +*.gemspec linguist-language=Ruby +*.ru linguist-language=Ruby +*.erb linguist-language=HTML+ERB +*.slim linguist-language=Slim +*.haml linguist-language=Haml \ No newline at end of file diff --git a/src/Data/gitattributes-universal b/src/Data/gitattributes-universal new file mode 100644 index 0000000..15ff642 --- /dev/null +++ b/src/Data/gitattributes-universal @@ -0,0 +1,36 @@ +############################### +# Git Line Endings # +############################### + +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use lf line endings +*.sh text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.svg filter=lfs diff=lfs merge=lfs -text + +# Documents +*.pdf filter=lfs diff=lfs merge=lfs -text + +# Common binaries +*.exe filter=lfs diff=lfs merge=lfs -text +*.dll filter=lfs diff=lfs merge=lfs -text +*.so filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/src/Data/gitignore-markdown b/src/Data/gitignore-markdown new file mode 100644 index 0000000..46094d3 --- /dev/null +++ b/src/Data/gitignore-markdown @@ -0,0 +1,18 @@ +# Documentation/Markdown specific ignores +.DS_Store +Thumbs.db + +# Editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Generated files +_site/ +.sass-cache/ +.jekyll-cache/ +.jekyll-metadata +node_modules/ +dist/ \ No newline at end of file diff --git a/src/Data/gitignore-node b/src/Data/gitignore-node new file mode 100644 index 0000000..73ee119 --- /dev/null +++ b/src/Data/gitignore-node @@ -0,0 +1,39 @@ +# Node.js specific ignores +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ +*.lcov + +# Dependency directories +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# production builds +dist/ +build/ \ No newline at end of file diff --git a/src/Data/gitignore-python b/src/Data/gitignore-python new file mode 100644 index 0000000..4e02745 --- /dev/null +++ b/src/Data/gitignore-python @@ -0,0 +1,57 @@ +# Python specific ignores +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Virtual environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo \ No newline at end of file diff --git a/src/Data/gitignore-ruby b/src/Data/gitignore-ruby new file mode 100644 index 0000000..fefe0b7 --- /dev/null +++ b/src/Data/gitignore-ruby @@ -0,0 +1,39 @@ +# Ruby specific ignores +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +.env + +# Ignore Byebug command history file. +.byebug_history + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore uploaded files in development +/storage/* +!/storage/.keep + +# IDE +.vscode/ +.idea/ \ No newline at end of file diff --git a/src/Data/index.js b/src/Data/index.js new file mode 100644 index 0000000..84dcd5b --- /dev/null +++ b/src/Data/index.js @@ -0,0 +1 @@ +console.log('Hello, World!'); \ No newline at end of file diff --git a/src/Data/main.py b/src/Data/main.py new file mode 100644 index 0000000..a8a1402 --- /dev/null +++ b/src/Data/main.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +""" +Main application entry point. +""" + +def main(): + """Main function.""" + print("Hello, World!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Data/main.rb b/src/Data/main.rb new file mode 100644 index 0000000..b8a8539 --- /dev/null +++ b/src/Data/main.rb @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby + +puts 'Hello, World!' \ No newline at end of file diff --git a/src/Data/package.json b/src/Data/package.json new file mode 100644 index 0000000..a9af525 --- /dev/null +++ b/src/Data/package.json @@ -0,0 +1,14 @@ +{ + "name": "my-project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "node --watch index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "MIT" +} \ No newline at end of file diff --git a/src/Data/requirements.txt b/src/Data/requirements.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/src/Data/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file diff --git a/src/Program.cs b/src/Program.cs index 53ef264..d3a65be 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -3,34 +3,168 @@ using System.Text; using System.Reflection; using System.Globalization; +using System.Runtime.InteropServices; + namespace solrevdev.seedfolder; +// Template metadata structure for future extensibility +internal record TemplateFile(string ResourceName, string FileName, string Description = ""); + +// Enum for supported project types +internal enum ProjectType +{ + Dotnet, + Node, + Python, + Ruby, + Markdown, + Universal +} + internal static class Program { - public static async Task Main(string[] args) + public static async Task Main(string[] args) { - ShowHeader(); - - WriteLine($"▲ Running in the path {Directory.GetCurrentDirectory()}"); - var folderName = ""; + var projectType = ProjectType.Markdown; // Default to markdown + var isDryRun = false; + var isForce = false; + var isQuiet = false; if (args?.Length > 0) { - var opts = new[] { "--help", "-h", "-?", "--version", "-v" }; - if (opts.Contains(args[0].ToLower(CultureInfo.InvariantCulture))) + var argIndex = 0; + while (argIndex < args.Length) { - ShowHelp(); - return; + var arg = args[argIndex].ToLower(CultureInfo.InvariantCulture); + + // Handle flags + if (arg is "--help" or "-h" or "-?") + { + ShowHelp(); + return 0; + } + + if (arg is "--version" or "-v") + { + ShowVersion(); + return 0; + } + + if (arg is "--list-templates" or "--list") + { + ShowTemplates(); + return 0; + } + + if (arg is "--dry-run" or "--dry") + { + isDryRun = true; + argIndex++; + continue; + } + + if (arg is "--force" or "-f") + { + isForce = true; + argIndex++; + continue; + } + + if (arg is "--quiet" or "-q") + { + isQuiet = true; + argIndex++; + continue; + } + + if (arg is "--template" or "--type" or "-t") + { + if (argIndex + 1 >= args.Length) + { + WriteLine("▲ Error: --template requires a template type.", ConsoleColor.DarkRed); + WriteLine("▲ Available types: dotnet, node, python, ruby, markdown, universal", ConsoleColor.DarkYellow); + return 1; + } + + var templateArg = args[argIndex + 1].ToLower(CultureInfo.InvariantCulture); + if (!TryParseProjectType(templateArg, out projectType)) + { + WriteLine($"▲ Error: Unknown template type '{args[argIndex + 1]}'.", ConsoleColor.DarkRed); + WriteLine("▲ Available template types:", ConsoleColor.DarkYellow); + WriteLine(" dotnet - .NET project with standard dotfiles", ConsoleColor.DarkYellow); + WriteLine(" node - Node.js project with package.json", ConsoleColor.DarkYellow); + WriteLine(" python - Python project with requirements.txt", ConsoleColor.DarkYellow); + WriteLine(" ruby - Ruby project with Gemfile", ConsoleColor.DarkYellow); + WriteLine(" markdown - Documentation project with README", ConsoleColor.DarkYellow); + WriteLine(" universal - Basic project with minimal files", ConsoleColor.DarkYellow); + WriteLine("▲ Use --list-templates to see all available templates and their files.", ConsoleColor.Cyan); + return 1; + } + + argIndex += 2; + + // Get folder name if provided + if (argIndex < args.Length) + { + folderName = args[argIndex]; + argIndex++; + } + + if (!isQuiet) + WriteLine($"▲ Using template type: {projectType}"); + break; + } + else + { + // This is the folder name + folderName = args[argIndex]; + argIndex++; + break; + } } + } - WriteLine($"▲ Found {args?.Length} params to process. "); - folderName = args[0]; + if (!isQuiet) + { + ShowHeader(); + WriteLine($"▲ Running in the path {Directory.GetCurrentDirectory()}"); } var sb = new StringBuilder(); if (string.IsNullOrWhiteSpace(folderName)) { + // Interactive template selection + if (projectType == ProjectType.Dotnet) // Only prompt if no template was specified + { + if (!isQuiet) + { + WriteLine("▲ Available project templates:"); + WriteLine(" 1. markdown - Documentation project with README"); + WriteLine(" 2. dotnet - .NET project with standard dotfiles"); + WriteLine(" 3. node - Node.js project with package.json"); + WriteLine(" 4. python - Python project with requirements.txt"); + WriteLine(" 5. ruby - Ruby project with Gemfile"); + WriteLine(" 6. universal - Basic project with minimal files"); + WriteLine(""); + } + + var templateChoice = Prompt.GetString("▲ Select template type (1-6) or press Enter for markdown", "1"); + + projectType = templateChoice switch + { + "2" or "dotnet" => ProjectType.Dotnet, + "3" or "node" => ProjectType.Node, + "4" or "python" => ProjectType.Python, + "5" or "ruby" => ProjectType.Ruby, + "6" or "universal" => ProjectType.Universal, + _ => ProjectType.Markdown + }; + + if (!isQuiet) + WriteLine($"▲ Selected template: {projectType}"); + } + var prefixWithDate = Prompt.GetYesNo("▲ Do you want to prefix the folder with the date?", defaultAnswer: true); if (prefixWithDate) { @@ -41,82 +175,346 @@ public static async Task Main(string[] args) folderName = Prompt.GetString("▲ What do you want the folder to be named?"); } + // Validate and sanitize folder name if (string.IsNullOrWhiteSpace(folderName)) { - WriteLine("▲ You must enter a folder name.", ConsoleColor.DarkRed); - return; + WriteLine("▲ Error: You must enter a folder name.", ConsoleColor.DarkRed); + return 1; } - else + + folderName = RemoveSpaces(folderName); + folderName = SafeNameForFileSystem(folderName); + + if (string.IsNullOrWhiteSpace(folderName)) { - folderName = RemoveSpaces(folderName); - folderName = SafeNameForFileSystem(folderName); - sb.Append(folderName); + WriteLine("▲ Error: Folder name contains only invalid characters.", ConsoleColor.DarkRed); + return 1; } + + sb.Append(folderName); var finalFolderName = sb.ToString(); + + // Check if directory already exists if (Directory.Exists(finalFolderName)) { - WriteLine($"▲ Sorry but {finalFolderName} already exists.", ConsoleColor.DarkRed); - return; + if (!isForce) + { + WriteLine($"▲ Error: Directory '{finalFolderName}' already exists.", ConsoleColor.DarkRed); + WriteLine("▲ Use --force to overwrite existing directory.", ConsoleColor.DarkYellow); + return 1; + } + else if (!isQuiet) + { + WriteLine($"▲ Warning: Directory '{finalFolderName}' exists, will overwrite files.", ConsoleColor.DarkYellow); + } } - WriteLine($"‍▲ Creating the directory {finalFolderName}"); - Directory.CreateDirectory(finalFolderName); + // Define template files based on project type + var templateFiles = GetTemplateFiles(projectType); - WriteLine($"‍▲ Copying .dockerignore to {finalFolderName}{Path.DirectorySeparatorChar}.dockerignore"); - await WriteFileAsync("dockerignore", $"{finalFolderName}{Path.DirectorySeparatorChar}.dockerignore").ConfigureAwait(false); + if (isDryRun) + { + WriteLine($"▲ DRY RUN: Would create directory '{finalFolderName}' with template '{projectType}'", ConsoleColor.Cyan); + WriteLine("▲ Files that would be created:", ConsoleColor.Cyan); + foreach (var templateFile in templateFiles) + { + var destinationPath = Path.Combine(finalFolderName, templateFile.FileName); + WriteLine($" • {destinationPath}", ConsoleColor.Cyan); + } + WriteLine("▲ Use without --dry-run to actually create the files.", ConsoleColor.Cyan); + return 0; + } - WriteLine($"‍▲ Copying .editorconfig to {finalFolderName}{Path.DirectorySeparatorChar}.editorconfig"); - await WriteFileAsync("editorconfig", $"{finalFolderName}{Path.DirectorySeparatorChar}.editorconfig").ConfigureAwait(false); + // Create directory with enhanced error handling + if (!isQuiet) + WriteLine($"‍▲ Creating the directory {finalFolderName}"); + + try + { + Directory.CreateDirectory(finalFolderName); + } + catch (UnauthorizedAccessException) + { + WriteLine($"▲ Error: Permission denied creating directory '{finalFolderName}'.", ConsoleColor.DarkRed); + WriteLine("▲ Please check that you have write permissions to this location.", ConsoleColor.DarkYellow); + return 1; + } + catch (DirectoryNotFoundException) + { + WriteLine($"▲ Error: Parent directory path not found for '{finalFolderName}'.", ConsoleColor.DarkRed); + WriteLine("▲ Please ensure the parent directory exists.", ConsoleColor.DarkYellow); + return 1; + } + catch (PathTooLongException) + { + WriteLine($"▲ Error: Directory path is too long: '{finalFolderName}'.", ConsoleColor.DarkRed); + WriteLine("▲ Please use a shorter folder name or path.", ConsoleColor.DarkYellow); + return 1; + } + catch (Exception ex) + { + WriteLine($"▲ Error creating directory: {ex.Message}", ConsoleColor.DarkRed); + WriteLine("▲ Please check your permissions and try again.", ConsoleColor.DarkYellow); + return 1; + } - WriteLine($"‍▲ Copying .gitattributes to {finalFolderName}{Path.DirectorySeparatorChar}.gitattributes"); - await WriteFileAsync("gitattributes", $"{finalFolderName}{Path.DirectorySeparatorChar}.gitattributes").ConfigureAwait(false); + // Validate disk space before creating files + if (!ValidateDiskSpace(finalFolderName)) + { + WriteLine("▲ Error: Insufficient disk space to create project files.", ConsoleColor.DarkRed); + WriteLine("▲ Please free up disk space and try again.", ConsoleColor.DarkYellow); + return 1; + } - WriteLine($"‍▲ Copying .gitignore to {finalFolderName}{Path.DirectorySeparatorChar}.gitignore"); - await WriteFileAsync("gitignore", $"{finalFolderName}{Path.DirectorySeparatorChar}.gitignore").ConfigureAwait(false); + // Copy template files using cross-platform path handling with progress indicators + var fileCount = templateFiles.Length; + for (int i = 0; i < fileCount; i++) + { + var templateFile = templateFiles[i]; + var destinationPath = Path.Combine(finalFolderName, templateFile.FileName); + + if (!isQuiet) + { + var progress = $"[{i + 1}/{fileCount}]"; + WriteLine($"‍▲ {progress} Copying {templateFile.FileName}"); + } + + try + { + await WriteFileAsync(templateFile.ResourceName, destinationPath).ConfigureAwait(false); + + if (!isQuiet) + WriteLine($" ✅ Created {destinationPath}", ConsoleColor.DarkGreen); + } + catch (UnauthorizedAccessException) + { + WriteLine($"▲ Error: Permission denied writing {templateFile.FileName}.", ConsoleColor.DarkRed); + WriteLine($"▲ Please check write permissions for '{destinationPath}'.", ConsoleColor.DarkYellow); + WriteLine($"▲ Failed at file {i + 1} of {fileCount}. Some files may have been created.", ConsoleColor.DarkYellow); + return 1; + } + catch (DirectoryNotFoundException) + { + WriteLine($"▲ Error: Directory not found for {templateFile.FileName}.", ConsoleColor.DarkRed); + WriteLine($"▲ The directory may have been deleted during operation.", ConsoleColor.DarkYellow); + WriteLine($"▲ Failed at file {i + 1} of {fileCount}. Some files may have been created.", ConsoleColor.DarkYellow); + return 1; + } + catch (IOException ioEx) + { + WriteLine($"▲ Error: I/O error writing {templateFile.FileName}: {ioEx.Message}", ConsoleColor.DarkRed); + WriteLine($"▲ This could be due to disk space, file locks, or permission issues.", ConsoleColor.DarkYellow); + WriteLine($"▲ Failed at file {i + 1} of {fileCount}. Some files may have been created.", ConsoleColor.DarkYellow); + return 1; + } + catch (Exception ex) + { + WriteLine($"▲ Error copying {templateFile.FileName}: {ex.Message}", ConsoleColor.DarkRed); + WriteLine($"▲ Failed at file {i + 1} of {fileCount}. Some files may have been created.", ConsoleColor.DarkYellow); + return 1; + } + } - WriteLine($"‍▲ Copying .prettierignore to {finalFolderName}{Path.DirectorySeparatorChar}.prettierignore"); - await WriteFileAsync("prettierignore", $"{finalFolderName}{Path.DirectorySeparatorChar}.prettierignore").ConfigureAwait(false); + if (!isQuiet) + { + WriteLine("▲ Done!", ConsoleColor.DarkGreen); + WriteLine($"▲ Successfully created {fileCount} files in '{finalFolderName}' using {projectType} template.", ConsoleColor.DarkGreen); + WriteLine(""); + ShowGitSetupInstructions(finalFolderName); + } + + return 0; + } - WriteLine($"‍▲ Copying .prettierrc to {finalFolderName}{Path.DirectorySeparatorChar}.prettierrc"); - await WriteFileAsync("prettierrc", $"{finalFolderName}{Path.DirectorySeparatorChar}.prettierrc").ConfigureAwait(false); + private static bool TryParseProjectType(string input, out ProjectType projectType) + { + projectType = input switch + { + "dotnet" or "net" or "csharp" => ProjectType.Dotnet, + "node" or "nodejs" or "javascript" or "js" => ProjectType.Node, + "python" or "py" => ProjectType.Python, + "ruby" or "rb" => ProjectType.Ruby, + "markdown" or "md" or "docs" => ProjectType.Markdown, + "universal" or "basic" or "minimal" => ProjectType.Universal, + _ => ProjectType.Dotnet + }; + + return input is "dotnet" or "net" or "csharp" or "node" or "nodejs" or "javascript" or "js" + or "python" or "py" or "ruby" or "rb" or "markdown" or "md" or "docs" + or "universal" or "basic" or "minimal"; + } - WriteLine($"‍▲ Copying omnisharp.json to {finalFolderName}{Path.DirectorySeparatorChar}omnisharp.json"); - await WriteFileAsync("omnisharp.json", $"{finalFolderName}{Path.DirectorySeparatorChar}omnisharp.json").ConfigureAwait(false); + private static TemplateFile[] GetTemplateFiles(ProjectType projectType) + { + return projectType switch + { + ProjectType.Node => GetNodeTemplate(), + ProjectType.Python => GetPythonTemplate(), + ProjectType.Ruby => GetRubyTemplate(), + ProjectType.Markdown => GetMarkdownTemplate(), + ProjectType.Universal => GetUniversalTemplate(), + _ => GetDotnetTemplate() + }; + } - WriteLine("▲ Done!"); + private static TemplateFile[] GetDotnetTemplate() + { + return new TemplateFile[] + { + new("dockerignore", ".dockerignore", "Docker ignore patterns"), + new("editorconfig-dotnet", ".editorconfig", "Editor configuration for .NET"), + new("gitattributes", ".gitattributes", "Git attributes"), + new("gitignore", ".gitignore", "Git ignore patterns"), + new("prettierignore", ".prettierignore", "Prettier ignore patterns"), + new("prettierrc", ".prettierrc", "Prettier configuration"), + new("omnisharp.json", "omnisharp.json", "OmniSharp configuration") + }; } - private static void ShowHelp() + private static TemplateFile[] GetNodeTemplate() { - var version = typeof(Program).GetTypeInfo().Assembly.GetCustomAttribute().InformationalVersion; + return new TemplateFile[] + { + new("package.json", "package.json", "Node.js package configuration"), + new("index.js", "index.js", "Main application entry point"), + new("gitignore-node", ".gitignore", "Node.js specific git ignore patterns"), + new("gitattributes-node", ".gitattributes", "Git attributes for Node.js projects"), + new("editorconfig-node", ".editorconfig", "Editor configuration for Node.js"), + new("prettierignore", ".prettierignore", "Prettier ignore patterns"), + new("prettierrc", ".prettierrc", "Prettier configuration") + }; + } - WriteLine($"▲ Version {version}"); - const string help = @"▲ Usage: dotnet run [folderName] + private static TemplateFile[] GetPythonTemplate() + { + return new TemplateFile[] + { + new("main.py", "main.py", "Main application entry point"), + new("requirements.txt", "requirements.txt", "Python dependencies"), + new("gitignore-python", ".gitignore", "Python specific git ignore patterns"), + new("gitattributes-python", ".gitattributes", "Git attributes for Python projects"), + new("editorconfig-python", ".editorconfig", "Editor configuration for Python") + }; + } -Passing no folderName will then interactively ask you for the folder name. otherwise it will use the folderName you pass and create a new directory in your current folder. + private static TemplateFile[] GetRubyTemplate() + { + return new TemplateFile[] + { + new("Gemfile", "Gemfile", "Ruby dependencies"), + new("main.rb", "main.rb", "Main application entry point"), + new("gitignore-ruby", ".gitignore", "Ruby specific git ignore patterns"), + new("gitattributes-ruby", ".gitattributes", "Git attributes for Ruby projects"), + new("editorconfig-ruby", ".editorconfig", "Editor configuration for Ruby") + }; + } -For example: + private static TemplateFile[] GetMarkdownTemplate() + { + return new TemplateFile[] + { + new("README.md", "README.md", "Project documentation"), + new("gitignore-markdown", ".gitignore", "Documentation specific git ignore patterns"), + new("gitattributes-markdown", ".gitattributes", "Git attributes for documentation projects"), + new("editorconfig-markdown", ".editorconfig", "Editor configuration for Markdown") + }; + } -seedfolder -▲ Do you want to prefix the folder with the date? [Y/n] y -▲ What do you want the folder to be named? temp -▲ Creating the directory 2020-12-10_temp -▲ Done! + private static TemplateFile[] GetUniversalTemplate() + { + return new TemplateFile[] + { + new("README.md", "README.md", "Project documentation"), + new("gitignore", ".gitignore", "Basic git ignore patterns"), + new("gitattributes-universal", ".gitattributes", "Git attributes for universal projects"), + new("editorconfig-universal", ".editorconfig", "Editor configuration for universal projects") + }; + } -seedfolder -▲ Do you want to prefix the folder with the date? [Y/n] n -▲ What do you want the folder to be named? temp -▲ Creating the directory temp -▲ Done! + private static TemplateFile[] GetDefaultTemplate() + { + return GetDotnetTemplate(); + } -seedfolder temp -▲ Found 1 params to process. -▲ Creating the directory temp -▲ Done! + private static void ShowTemplates() + { + WriteLine("▲ Available project templates:"); + WriteLine(""); + + var templates = new[] + { + ("markdown", "Documentation project with README", GetMarkdownTemplate()), + ("dotnet", "Dotnet project with standard dotfiles", GetDotnetTemplate()), + ("node", "Node.js project with package.json", GetNodeTemplate()), + ("python", "Python project with requirements.txt", GetPythonTemplate()), + ("ruby", "Ruby project with Gemfile", GetRubyTemplate()), + ("universal", "Basic project with minimal files", GetUniversalTemplate()) + }; + + foreach (var (name, description, files) in templates) + { + WriteLine($" {name,-12} - {description}"); + foreach (var file in files) + { + WriteLine($" • {file.FileName,-20} {file.Description}"); + } + WriteLine(""); + } + + WriteLine("▲ Usage examples:"); + WriteLine(" seedfolder --template node myproject"); + WriteLine(" seedfolder -t python myapp"); + WriteLine(" seedfolder --type ruby mygem"); + } -seedfolder will also copy various dotfiles to that folder. -"; + private static void ShowVersion() + { + var version = typeof(Program).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? "Unknown"; + WriteLine($"▲ seedfolder version {version}"); + } + + private static void ShowHelp() + { + var version = typeof(Program).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? "Unknown"; + + WriteLine($"▲ seedfolder version {version}"); + WriteLine(""); + const string help = @"▲ Usage: seedfolder [options] [folderName] + +Options: + --help, -h, -? Show this help message + --version, -v Show version information + --list-templates Show available template files + --template, --type, -t Specify project template type + --dry-run, --dry Preview operations without creating files + --force, -f Overwrite existing directory and files + --quiet, -q Suppress output (useful for scripting) + +Arguments: + folderName Name of the folder to create (optional) + +Template Types: + dotnet .NET project with standard dotfiles + node Node.js project with package.json + python Python project with requirements.txt + ruby Ruby project with Gemfile + markdown Documentation project with README (default) + universal Basic project with minimal files + +If no folder name is provided, seedfolder will interactively ask for the folder name +and whether to prefix it with the current date. + +Examples: + + seedfolder # Interactive mode with template selection + seedfolder myproject # Create 'myproject' folder with markdown template + seedfolder --template node myapp # Create Node.js project + seedfolder -t python ""my project"" # Create Python project (spaces converted to dashes) + seedfolder --type ruby mygem # Create Ruby project + seedfolder --dry-run -t node myapp # Preview Node.js project creation + seedfolder --force myproject # Overwrite existing 'myproject' directory + seedfolder --quiet -t python myapp # Create Python project with no output"; WriteLine(help); } @@ -137,15 +535,25 @@ private static void WriteLine(string text, ConsoleColor color = default) private static async Task WriteFileAsync(string filename, string destination) { var assembly = Assembly.GetEntryAssembly(); - var resourceStream = assembly.GetManifestResourceStream($"solrevdev.seedfolder.Data.{filename}"); - if (resourceStream != null) + var resourceName = $"solrevdev.seedfolder.Data.{filename}"; + var resourceStream = assembly?.GetManifestResourceStream(resourceName); + + if (resourceStream == null) { - using (var reader = new StreamReader(resourceStream, Encoding.UTF8)) - { - var fileContents = await reader.ReadToEndAsync().ConfigureAwait(false); - File.WriteAllText(destination, fileContents); - } + throw new InvalidOperationException($"Could not find embedded resource: {resourceName}"); } + + using var reader = new StreamReader(resourceStream, Encoding.UTF8); + var fileContents = await reader.ReadToEndAsync().ConfigureAwait(false); + + // Ensure destination directory exists + var destinationDir = Path.GetDirectoryName(destination); + if (!string.IsNullOrEmpty(destinationDir) && !Directory.Exists(destinationDir)) + { + Directory.CreateDirectory(destinationDir); + } + + await File.WriteAllTextAsync(destination, fileContents).ConfigureAwait(false); } private static void ShowHeader() @@ -168,7 +576,61 @@ private static void AppendBlankLines(int howMany = 2) private static string SafeNameForFileSystem(string name, char replace = '-') { + if (string.IsNullOrWhiteSpace(name)) + return string.Empty; + var invalids = Path.GetInvalidFileNameChars(); - return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray()); + var result = new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray()); + + // Remove any leading/trailing dashes and handle edge cases + result = result.Trim(replace); + + // Ensure we don't end up with an empty string after cleaning + return string.IsNullOrWhiteSpace(result) ? string.Empty : result; + } + + private static bool ValidateDiskSpace(string directoryPath) + { + try + { + var drive = new DriveInfo(Path.GetPathRoot(Path.GetFullPath(directoryPath)) ?? Directory.GetCurrentDirectory()); + + // Check if we have at least 10MB of free space (conservative estimate) + const long minimumSpaceRequired = 10 * 1024 * 1024; // 10MB in bytes + + return drive.AvailableFreeSpace >= minimumSpaceRequired; + } + catch + { + // If we can't determine disk space, assume we have enough (better to try and fail gracefully) + return true; + } + } + + private static void ShowGitSetupInstructions(string folderName) + { + WriteLine("▲ To initialize git and make your first commit, copy and paste these commands:", ConsoleColor.Cyan); + WriteLine(""); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Windows commands + WriteLine($"cd \"{folderName}\"", ConsoleColor.DarkYellow); + WriteLine("git init", ConsoleColor.DarkYellow); + WriteLine("git lfs install 2>nul || echo Git LFS not available", ConsoleColor.DarkYellow); + WriteLine("git add .", ConsoleColor.DarkYellow); + WriteLine("git commit -m \"Initial commit\"", ConsoleColor.DarkYellow); + } + else + { + // Unix-like systems (Linux, macOS) + WriteLine($"cd \"{folderName}\"", ConsoleColor.DarkYellow); + WriteLine("git init", ConsoleColor.DarkYellow); + WriteLine("git lfs install 2>/dev/null || echo \"Git LFS not available\"", ConsoleColor.DarkYellow); + WriteLine("git add .", ConsoleColor.DarkYellow); + WriteLine("git commit -m \"Initial commit\"", ConsoleColor.DarkYellow); + } + + WriteLine(""); } } diff --git a/src/solrevdev.seedfolder.csproj b/src/solrevdev.seedfolder.csproj index a90d73d..eee9e54 100644 --- a/src/solrevdev.seedfolder.csproj +++ b/src/solrevdev.seedfolder.csproj @@ -37,7 +37,18 @@ + + + + + + + + + + + @@ -47,10 +58,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/integration-test.ps1 b/tests/integration-test.ps1 new file mode 100644 index 0000000..68d9d19 --- /dev/null +++ b/tests/integration-test.ps1 @@ -0,0 +1,110 @@ +# Integration test for seedfolder - validates all template types work correctly (PowerShell) + +$ErrorActionPreference = "Stop" + +Write-Host "🚀 Starting seedfolder integration tests..." -ForegroundColor Green + +# Navigate to project root +Set-Location "$(Split-Path $PSScriptRoot -Parent)" + +# Build the project +Write-Host "📦 Building project..." -ForegroundColor Yellow +dotnet build src/solrevdev.seedfolder.csproj --configuration Release --framework net8.0 + +# Create test directory +$TestDir = "test-output" +if (Test-Path $TestDir) { Remove-Item $TestDir -Recurse -Force } +New-Item -ItemType Directory -Path $TestDir | Out-Null +Set-Location $TestDir + +Write-Host "✅ Testing all template types..." -ForegroundColor Green + +try { + # Test dotnet template (default) + Write-Host "🔹 Testing dotnet template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet test-dotnet + if (!(Test-Path "test-dotnet/.gitignore")) { throw "dotnet .gitignore missing" } + if (!(Test-Path "test-dotnet/omnisharp.json")) { throw "dotnet omnisharp.json missing" } + + # Test node template + Write-Host "🔹 Testing node template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node test-node + if (!(Test-Path "test-node/package.json")) { throw "node package.json missing" } + if (!(Test-Path "test-node/index.js")) { throw "node index.js missing" } + + # Test python template + Write-Host "🔹 Testing python template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --template python test-python + if (!(Test-Path "test-python/main.py")) { throw "python main.py missing" } + if (!(Test-Path "test-python/requirements.txt")) { throw "python requirements.txt missing" } + + # Test ruby template + Write-Host "🔹 Testing ruby template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --type ruby test-ruby + if (!(Test-Path "test-ruby/Gemfile")) { throw "ruby Gemfile missing" } + if (!(Test-Path "test-ruby/main.rb")) { throw "ruby main.rb missing" } + + # Test markdown template + Write-Host "🔹 Testing markdown template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t markdown test-markdown + if (!(Test-Path "test-markdown/README.md")) { throw "markdown README.md missing" } + + # Test universal template + Write-Host "🔹 Testing universal template..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t universal test-universal + if (!(Test-Path "test-universal/README.md")) { throw "universal README.md missing" } + + # Test dry-run mode + Write-Host "🔹 Testing dry-run mode..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --dry-run -t node test-dry-run + if (Test-Path "test-dry-run") { throw "dry-run should not create directory" } + + # Test force mode + Write-Host "🔹 Testing force mode..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node test-force-existing + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --force -t python test-force-existing + if (!(Test-Path "test-force-existing/main.py")) { throw "force mode should overwrite with python template" } + + # Test space handling + Write-Host "🔹 Testing space handling..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node "test space project" + if (!(Test-Path "test-space-project")) { throw "space handling failed" } + + # Test error handling - invalid template type + Write-Host "🔹 Testing error handling - invalid template..." -ForegroundColor Cyan + $result = dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- -t invalidtype test-error 2>$null + if ($LASTEXITCODE -eq 0) { throw "Should have failed with invalid template type" } + + # Test error handling - missing template argument + Write-Host "🔹 Testing error handling - missing template argument..." -ForegroundColor Cyan + $result = dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --template 2>$null + if ($LASTEXITCODE -eq 0) { throw "Should have failed with missing template argument" } + + # Test help command + Write-Host "🔹 Testing help command..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --help >$null + if ($LASTEXITCODE -ne 0) { throw "help command failed" } + + # Test version command + Write-Host "🔹 Testing version command..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --version >$null + if ($LASTEXITCODE -ne 0) { throw "version command failed" } + + # Test list templates command + Write-Host "🔹 Testing list templates command..." -ForegroundColor Cyan + dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --list-templates >$null + if ($LASTEXITCODE -ne 0) { throw "list templates command failed" } + + Write-Host "🎉 All integration tests passed!" -ForegroundColor Green + Write-Host "✅ Tested templates: dotnet, node, python, ruby, markdown, universal" -ForegroundColor Green + Write-Host "✅ Tested features: dry-run, force, quiet, space handling, error handling, help, version, list-templates" -ForegroundColor Green +} +catch { + Write-Host "❌ Test failed: $_" -ForegroundColor Red + exit 1 +} +finally { + # Clean up + Set-Location ".." + if (Test-Path $TestDir) { Remove-Item $TestDir -Recurse -Force } +} \ No newline at end of file diff --git a/tests/integration-test.sh b/tests/integration-test.sh new file mode 100755 index 0000000..72c112a --- /dev/null +++ b/tests/integration-test.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# Integration test for seedfolder - validates all template types work correctly + +set -e + +echo "🚀 Starting seedfolder integration tests..." + +# Navigate to project root +cd "$(dirname "$0")/.." + +# Build the project +echo "📦 Building project..." +dotnet build src/solrevdev.seedfolder.csproj --configuration Release --framework net8.0 + +# Create test directory +TEST_DIR="test-output" +rm -rf "$TEST_DIR" +mkdir -p "$TEST_DIR" +cd "$TEST_DIR" + +echo "✅ Testing all template types..." + +# Test markdown template (default) +echo "🔹 Testing markdown template (default)..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet test-default-markdown +[[ -f "test-default-markdown/README.md" ]] || { echo "❌ default markdown README.md missing"; exit 1; } +[[ -f "test-default-markdown/.gitignore" ]] || { echo "❌ default markdown .gitignore missing"; exit 1; } + +# Test dotnet template +echo "🔹 Testing dotnet template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --template dotnet test-dotnet +[[ -f "test-dotnet/.gitignore" ]] || { echo "❌ dotnet .gitignore missing"; exit 1; } +[[ -f "test-dotnet/omnisharp.json" ]] || { echo "❌ dotnet omnisharp.json missing"; exit 1; } + +# Test node template +echo "🔹 Testing node template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node test-node +[[ -f "test-node/package.json" ]] || { echo "❌ node package.json missing"; exit 1; } +[[ -f "test-node/index.js" ]] || { echo "❌ node index.js missing"; exit 1; } + +# Test python template +echo "🔹 Testing python template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --template python test-python +[[ -f "test-python/main.py" ]] || { echo "❌ python main.py missing"; exit 1; } +[[ -f "test-python/requirements.txt" ]] || { echo "❌ python requirements.txt missing"; exit 1; } + +# Test ruby template +echo "🔹 Testing ruby template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --type ruby test-ruby +[[ -f "test-ruby/Gemfile" ]] || { echo "❌ ruby Gemfile missing"; exit 1; } +[[ -f "test-ruby/main.rb" ]] || { echo "❌ ruby main.rb missing"; exit 1; } + +# Test markdown template +echo "🔹 Testing markdown template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t markdown test-markdown +[[ -f "test-markdown/README.md" ]] || { echo "❌ markdown README.md missing"; exit 1; } + +# Test universal template +echo "🔹 Testing universal template..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t universal test-universal +[[ -f "test-universal/README.md" ]] || { echo "❌ universal README.md missing"; exit 1; } + +# Test dry-run mode +echo "🔹 Testing dry-run mode..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --dry-run -t node test-dry-run +[[ ! -d "test-dry-run" ]] || { echo "❌ dry-run should not create directory"; exit 1; } + +# Test force mode +echo "🔹 Testing force mode..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node test-force-existing +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet --force -t python test-force-existing +[[ -f "test-force-existing/main.py" ]] || { echo "❌ force mode should overwrite with python template"; exit 1; } + +# Test space handling +echo "🔹 Testing space handling..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --quiet -t node "test space project" +[[ -d "test-space-project" ]] || { echo "❌ space handling failed"; exit 1; } + +# Test error handling - invalid template type +echo "🔹 Testing error handling - invalid template..." +if dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- -t invalidtype test-error 2>/dev/null; then + echo "❌ Should have failed with invalid template type" + exit 1 +fi + +# Test error handling - missing template argument +echo "🔹 Testing error handling - missing template argument..." +if dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --template 2>/dev/null; then + echo "❌ Should have failed with missing template argument" + exit 1 +fi + +# Test help command +echo "🔹 Testing help command..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --help > /dev/null || { echo "❌ help command failed"; exit 1; } + +# Test version command +echo "🔹 Testing version command..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --version > /dev/null || { echo "❌ version command failed"; exit 1; } + +# Test list templates command +echo "🔹 Testing list templates command..." +dotnet run --project ../src/solrevdev.seedfolder.csproj --framework net8.0 -- --list-templates > /dev/null || { echo "❌ list templates command failed"; exit 1; } + +# Clean up +cd .. +rm -rf "$TEST_DIR" + +echo "🎉 All integration tests passed!" +echo "✅ Tested templates: dotnet, node, python, ruby, markdown, universal" +echo "✅ Tested features: dry-run, force, quiet, space handling, error handling, help, version, list-templates" \ No newline at end of file