Last active
November 1, 2025 05:12
-
-
Save weehong/dd5c3935e81d6d6784aea8456bf1acf7 to your computer and use it in GitHub Desktop.
This script create a .NET project utilizing project structure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' # No Color | |
| # Configuration (Project name can be provided as CLI argument) | |
| PROJECT_NAME=$1 | |
| INCLUDE_TESTS=true | |
| DATABASE_PROVIDER="sqlserver" | |
| DOTNET_VERSION="net9.0" | |
| SCRIPT_DIR="$(pwd)" | |
| print_info() { echo -e "${GREEN}✓${NC} $1"; } | |
| print_error() { echo -e "${RED}✗${NC} $1"; } | |
| print_warning() { echo -e "${YELLOW}⚠${NC} $1"; } | |
| print_step() { echo -e "${BLUE}▶${NC} $1"; } | |
| handle_error() { | |
| print_error "An error occurred on line $1" | |
| print_error "Command: $2" | |
| exit 1 | |
| } | |
| trap 'handle_error $LINENO "$BASH_COMMAND"' ERR | |
| check_dotnet_version() { | |
| if ! command -v dotnet &> /dev/null; then | |
| print_error ".NET SDK is not installed. Install .NET 9.0 SDK first." | |
| exit 1 | |
| fi | |
| } | |
| validate_project_name() { [[ $1 =~ ^[a-zA-Z][a-zA-Z0-9._-]*$ ]]; } | |
| get_project_configuration() { | |
| echo "========================================" | |
| echo " Clean Architecture CQRS Setup" | |
| echo " .NET 9.0 Production-Ready" | |
| echo "========================================" | |
| echo "" | |
| if [ -z "$PROJECT_NAME" ]; then | |
| if [ -t 0 ]; then | |
| while true; do | |
| read -rp "Enter project name: " PROJECT_NAME | |
| [ -z "$PROJECT_NAME" ] && { print_error "Project name cannot be empty."; continue; } | |
| validate_project_name "$PROJECT_NAME" || { print_error "Invalid project name."; continue; } | |
| [ -d "$PROJECT_NAME" ] && { print_error "Directory already exists."; exit 1; } | |
| break | |
| done | |
| else | |
| print_error "No project name provided, and no interactive terminal available." | |
| echo "Usage: $0 <ProjectName>" | |
| exit 1 | |
| fi | |
| else | |
| print_info "Using project name: $PROJECT_NAME" | |
| fi | |
| read -rp "Include test projects? (Y/n): " include_tests | |
| INCLUDE_TESTS=${include_tests:-Y} | |
| echo "" | |
| echo "Select database provider:" | |
| echo " 1) SQL Server (default)" | |
| echo " 2) PostgreSQL" | |
| echo " 3) SQLite" | |
| read -rp "Enter choice [1-3]: " db_choice | |
| case ${db_choice:-1} in | |
| 1) DATABASE_PROVIDER="sqlserver" ;; | |
| 2) DATABASE_PROVIDER="postgresql" ;; | |
| 3) DATABASE_PROVIDER="sqlite" ;; | |
| *) DATABASE_PROVIDER="sqlserver" ;; | |
| esac | |
| } | |
| create_directory_structure() { | |
| print_step "Creating directory structure..." | |
| mkdir -p "$PROJECT_NAME"/src | |
| [[ $INCLUDE_TESTS =~ ^[Yy]$ ]] && mkdir -p "$PROJECT_NAME"/tests | |
| cd "$PROJECT_NAME" | |
| print_info "Directory structure created" | |
| } | |
| create_solution() { | |
| print_step "Creating solution..." | |
| dotnet new sln -n "$PROJECT_NAME" --force | |
| print_info "Solution created" | |
| } | |
| create_project() { | |
| dotnet new "$1" -n "$2" -f "$DOTNET_VERSION" -o "$3" --force | |
| dotnet sln "$PROJECT_NAME.sln" add "$3/$2.csproj" | |
| echo " ✓ $2" | |
| } | |
| create_source_projects() { | |
| print_step "Creating source projects..." | |
| create_project classlib "$PROJECT_NAME.Domain" src/$PROJECT_NAME.Domain | |
| create_project classlib "$PROJECT_NAME.Application" src/$PROJECT_NAME.Application | |
| create_project classlib "$PROJECT_NAME.Infrastructure" src/$PROJECT_NAME.Infrastructure | |
| create_project classlib "$PROJECT_NAME.Contracts" src/$PROJECT_NAME.Contracts | |
| dotnet new webapi -n "$PROJECT_NAME.API" -f "$DOTNET_VERSION" -o src/$PROJECT_NAME.API --use-controllers --force | |
| dotnet sln "$PROJECT_NAME.sln" add src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj | |
| print_info "Source projects created" | |
| } | |
| create_test_projects() { | |
| [[ ! $INCLUDE_TESTS =~ ^[Yy]$ ]] && return | |
| print_step "Creating test projects..." | |
| create_project xunit "$PROJECT_NAME.Domain.Tests" tests/$PROJECT_NAME.Domain.Tests | |
| create_project xunit "$PROJECT_NAME.Application.Tests" tests/$PROJECT_NAME.Application.Tests | |
| create_project xunit "$PROJECT_NAME.Infrastructure.Tests" tests/$PROJECT_NAME.Infrastructure.Tests | |
| create_project xunit "$PROJECT_NAME.API.Tests" tests/$PROJECT_NAME.API.Tests | |
| print_info "Test projects created" | |
| } | |
| add_reference() { local project=$1; shift; for ref in "$@"; do dotnet add "$project" reference "$ref"; done; } | |
| add_project_references() { | |
| print_step "Adding project references..." | |
| # Application depends on Domain + Contracts | |
| add_reference src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Domain/$PROJECT_NAME.Domain.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| # Infrastructure depends on Application + Domain + Contracts | |
| add_reference src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj \ | |
| src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Domain/$PROJECT_NAME.Domain.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| # API depends on everything except tests | |
| add_reference src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj \ | |
| src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| } | |
| add_package() { dotnet add "$1" package "$2"; } | |
| install_mediatr_packages() { | |
| print_step "Installing MediatR..." | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "MediatR" | |
| } | |
| install_serilog_packages() { | |
| print_step "Installing Serilog..." | |
| add_package src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj "Serilog.AspNetCore" | |
| } | |
| install_ef_packages() { | |
| print_step "Installing EF Core..." | |
| add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore" | |
| case $DATABASE_PROVIDER in | |
| sqlserver) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore.SqlServer" ;; | |
| postgresql) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Npgsql.EntityFrameworkCore.PostgreSQL" ;; | |
| sqlite) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore.Sqlite" ;; | |
| esac | |
| } | |
| install_production_packages() { | |
| print_step "Installing FluentValidation and AutoMapper..." | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "FluentValidation" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "AutoMapper" | |
| } | |
| install_test_packages() { | |
| [[ ! $INCLUDE_TESTS =~ ^[Yy]$ ]] && return | |
| print_step "Installing test packages..." | |
| for proj in tests/*/*.csproj; do | |
| add_package "$proj" "FluentAssertions" | |
| add_package "$proj" "Moq" | |
| done | |
| } | |
| cleanup_default_files() { | |
| print_step "Cleaning up defaults..." | |
| find . -name "WeatherForecast*" -delete 2>/dev/null | |
| find . -name "Class1.cs" -delete 2>/dev/null | |
| } | |
| create_configuration_files() { | |
| print_step "Creating README.md..." | |
| echo "# $PROJECT_NAME" > README.md | |
| } | |
| build_solution() { | |
| print_step "Building solution..." | |
| dotnet build | |
| print_info "Build successful!" | |
| } | |
| display_summary() { | |
| echo "" | |
| print_info "Setup Complete!" | |
| echo "cd $PROJECT_NAME/src/$PROJECT_NAME.API && dotnet run" | |
| } | |
| main() { | |
| clear | |
| check_dotnet_version | |
| get_project_configuration | |
| create_directory_structure | |
| create_solution | |
| create_source_projects | |
| create_test_projects | |
| add_project_references | |
| install_mediatr_packages | |
| install_serilog_packages | |
| install_ef_packages | |
| install_production_packages | |
| install_test_packages | |
| cleanup_default_files | |
| create_configuration_files | |
| build_solution | |
| display_summary | |
| } | |
| main |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| PROJECT_NAME=$1 | |
| INCLUDE_TESTS=true | |
| DATABASE_PROVIDER="sqlserver" | |
| DOTNET_VERSION="net9.0" | |
| print_info() { echo -e "${GREEN}✓${NC} $1"; } | |
| print_error() { echo -e "${RED}✗${NC} $1"; } | |
| print_warning() { echo -e "${YELLOW}⚠${NC} $1"; } | |
| print_step() { echo -e "${BLUE}▶${NC} $1"; } | |
| trap 'print_error "Error on line $LINENO: $BASH_COMMAND"; exit 1' ERR | |
| check_requirements() { | |
| command -v dotnet >/dev/null || { print_error ".NET SDK missing."; exit 1; } | |
| command -v jq >/dev/null || { print_error "'jq' is required. Install via apt/brew."; exit 1; } | |
| } | |
| validate_project_name() { [[ $1 =~ ^[a-zA-Z][a-zA-Z0-9._-]*$ ]]; } | |
| get_project_configuration() { | |
| if [ -z "$PROJECT_NAME" ]; then | |
| read -rp "Enter project name: " PROJECT_NAME | |
| validate_project_name "$PROJECT_NAME" || { print_error "Invalid project name."; exit 1; } | |
| [ -d "$PROJECT_NAME" ] && { print_error "Directory exists."; exit 1; } | |
| fi | |
| read -rp "Include test projects? (Y/n): " include_tests | |
| INCLUDE_TESTS=${include_tests:-Y} | |
| echo "" | |
| echo "Select database provider:" | |
| echo "1) SQL Server (default)" | |
| echo "2) PostgreSQL" | |
| echo "3) SQLite" | |
| read -rp "Choice [1-3]: " db_choice | |
| case ${db_choice:-1} in | |
| 1) DATABASE_PROVIDER="sqlserver" ;; | |
| 2) DATABASE_PROVIDER="postgresql" ;; | |
| 3) DATABASE_PROVIDER="sqlite" ;; | |
| esac | |
| } | |
| create_structure() { | |
| print_step "Creating folder structure..." | |
| mkdir -p "$PROJECT_NAME"/src "$PROJECT_NAME"/tests | |
| cd "$PROJECT_NAME" | |
| print_info "Structure created." | |
| } | |
| create_solution() { | |
| print_step "Creating solution..." | |
| dotnet new sln -n "$PROJECT_NAME" --force | |
| } | |
| create_project() { | |
| dotnet new "$1" -n "$2" -f "$DOTNET_VERSION" -o "$3" --force | |
| dotnet sln add "$3/$2.csproj" | |
| } | |
| create_projects() { | |
| print_step "Creating source projects..." | |
| create_project classlib "$PROJECT_NAME.Domain" src/$PROJECT_NAME.Domain | |
| create_project classlib "$PROJECT_NAME.Application" src/$PROJECT_NAME.Application | |
| create_project classlib "$PROJECT_NAME.Infrastructure" src/$PROJECT_NAME.Infrastructure | |
| create_project classlib "$PROJECT_NAME.Contracts" src/$PROJECT_NAME.Contracts | |
| dotnet new webapi -n "$PROJECT_NAME.API" -f "$DOTNET_VERSION" -o src/$PROJECT_NAME.API --use-controllers --force | |
| dotnet sln add src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj | |
| if [[ $INCLUDE_TESTS =~ ^[Yy]$ ]]; then | |
| print_step "Creating test projects..." | |
| create_project xunit "$PROJECT_NAME.Application.Tests" tests/$PROJECT_NAME.Application.Tests | |
| create_project xunit "$PROJECT_NAME.Infrastructure.Tests" tests/$PROJECT_NAME.Infrastructure.Tests | |
| fi | |
| } | |
| add_reference() { local proj=$1; shift; for ref in "$@"; do dotnet add "$proj" reference "$ref"; done; } | |
| configure_references() { | |
| print_step "Adding project references..." | |
| add_reference src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Domain/$PROJECT_NAME.Domain.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| add_reference src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj \ | |
| src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Domain/$PROJECT_NAME.Domain.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| add_reference src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj \ | |
| src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj \ | |
| src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj \ | |
| src/$PROJECT_NAME.Contracts/$PROJECT_NAME.Contracts.csproj | |
| } | |
| get_latest_nuget_version() { | |
| local package="$1" | |
| curl -s "https://api.nuget.org/v3-flatcontainer/${package,,}/index.json" | jq -r '.versions[-1]' | |
| } | |
| add_package() { | |
| local project="$1" | |
| local package="$2" | |
| local version | |
| version=$(get_latest_nuget_version "$package") | |
| if [[ -n "$version" && "$version" != "null" ]]; then | |
| print_step "$package v$version → $project" | |
| dotnet add "$project" package "$package" --version "$version" | |
| else | |
| print_warning "$package version not found, auto-resolving." | |
| dotnet add "$project" package "$package" | |
| fi | |
| } | |
| install_packages() { | |
| print_step "Installing Application Layer packages..." | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "MediatR" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "MediatR.Extensions.Microsoft.DependencyInjection" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "FluentValidation" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "FluentValidation.DependencyInjectionExtensions" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "AutoMapper" | |
| add_package src/$PROJECT_NAME.Application/$PROJECT_NAME.Application.csproj "AutoMapper.Extensions.Microsoft.DependencyInjection" | |
| print_step "Installing Infrastructure Layer packages..." | |
| add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore" | |
| case $DATABASE_PROVIDER in | |
| sqlserver) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore.SqlServer" ;; | |
| postgresql) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Npgsql.EntityFrameworkCore.PostgreSQL" ;; | |
| sqlite) add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Microsoft.EntityFrameworkCore.Sqlite" ;; | |
| esac | |
| add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Serilog" | |
| add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Serilog.Sinks.Console" | |
| add_package src/$PROJECT_NAME.Infrastructure/$PROJECT_NAME.Infrastructure.csproj "Serilog.Sinks.File" | |
| print_step "Installing API Layer packages..." | |
| add_package src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj "Serilog.AspNetCore" | |
| add_package src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj "Serilog.Settings.Configuration" | |
| add_package src/$PROJECT_NAME.API/$PROJECT_NAME.API.csproj "Asp.Versioning.Http" | |
| } | |
| cleanup() { | |
| print_step "Removing template files..." | |
| find . -name "WeatherForecast*" -delete | |
| find . -name "Class1.cs" -delete | |
| } | |
| finish() { | |
| print_info "Setup Complete ✅" | |
| echo "Run the API:" | |
| echo "cd src/$PROJECT_NAME.API && dotnet run" | |
| } | |
| main() { | |
| clear | |
| check_requirements | |
| get_project_configuration | |
| create_structure | |
| create_solution | |
| create_projects | |
| configure_references | |
| install_packages | |
| cleanup | |
| finish | |
| } | |
| main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment