diff --git a/.cursor/rules/checklist-maintenance.mdc b/.cursor/rules/checklist-maintenance.mdc new file mode 100644 index 0000000..a909c02 --- /dev/null +++ b/.cursor/rules/checklist-maintenance.mdc @@ -0,0 +1,58 @@ +--- +description: Rules for maintaining project checklists +globs: "**/*checklist.md" +alwaysApply: true +--- + +# Checklist Maintenance Rules + +## Status Indicators + +- āœ… Completed: Task is fully implemented and verified +- - [ ] Todo: Task not yet started or completed +- šŸ”„ In-progress: Work has started but not completed +- āš ļø Blocked: Waiting on external dependency + +## Organization + +- Group by functional area (Docker, Security, etc.) +- List completed items first within each section +- Place priority items immediately after completed items +- Use logical workflow order for sections + +## Formatting + +- Use h1 (`#`) for document title +- Use h2 (`##`) for main sections +- Use h3 (`###`) for subsections +- Use bullet lists for checklist items +- Use 2-space indentation for nested items +- Keep line length under 120 characters +- Use code blocks with language identifiers + +## Example + +```markdown +## Security + +āœ… HTTPS redirection configured +āœ… Sensitive files excluded from source control + +- [ ] Add rate limiting for API endpoints +- [ ] Implement CORS policy configuration +``` + +## Update Process + +1. Review project status to identify completed items +2. Update status indicators accordingly +3. Add new requirements that have emerged +4. Remove duplicate or obsolete items +5. Ensure cross-references remain valid + +## Rule Types + +- **Manual** (`alwaysApply: false`): Only applied when explicitly requested +- **Always** (`alwaysApply: true`): Applied across all matching files automatically +- **Auto Attached**: Automatically attaches to relevant files based on content (Cursor feature) +- **Agent Requested**: Applied when the AI determines they're relevant (Cursor feature) \ No newline at end of file diff --git a/.cursor/rules/console-commands-for-powershell.mdc b/.cursor/rules/console-commands-for-powershell.mdc deleted file mode 100644 index 8f1224b..0000000 --- a/.cursor/rules/console-commands-for-powershell.mdc +++ /dev/null @@ -1,22 +0,0 @@ ---- -description: PowerShell 7 Command Generation (Not Bash) -globs: "*.ps1", "*.psm1", "*.psd1", "*.md", "*.txt" -alwaysApply: false ---- ---- -description: PowerShell 7 Command Generation (Not Bash) -globs: "*.ps1", "*.psm1", "*.psd1", "*.md", "*.txt" -alwaysApply: true ---- -Generate PowerShell 7 commands for Windows 11, not Bash syntax: - -- PowerShell cmdlets: Get-ChildItem (not ls), Set-Location (not cd) -- Paths use backslashes (C:\Users\) -- Flags use single hyphen (-Force) -- Variables: $variable, string interpolation: "$()" -- Environment variables: $env:VARIABLE -- Piping: | (with proper object handling) -- Command separation: ; (not &&) -- Redirection: >, >>, 2> - -Prefer PowerShell-native approaches over cmd.exe commands. diff --git a/.cursor/rules/git-bash-scripting.mdc b/.cursor/rules/git-bash-scripting.mdc new file mode 100644 index 0000000..2c8f712 --- /dev/null +++ b/.cursor/rules/git-bash-scripting.mdc @@ -0,0 +1,34 @@ +--- +description: Guidelines for creating and using Git Bash scripts +globs: +alwaysApply: false +--- +--- +description: Guidelines for creating and using Git Bash scripts +globs: "*.sh" +alwaysApply: false +--- + +# Selected Git Bash Scripting Guidelines + +## File Format +- Use LF line endings (not CRLF) +- Use UTF-8 without BOM +- Start with shebang: `#!/bin/bash` + +## Path Handling +- Use forward slashes in paths: `/path/to/dir` +- Always quote paths with spaces +- Use relative paths when possible + +## Error Handling +- Use `set -e` for immediate exit on error +- Check exit codes: `if [ $? -ne 0 ]; then ... fi` +- Log errors to stderr: `echo "Error: message" >&2` + +## PowerShell Integration +- Use `Invoke-GitBash` helper +- Example: + ```powershell + Invoke-GitBash "./scripts/your-script.sh" + ``` diff --git a/.cursor/rules/markdown-guidelines.mdc b/.cursor/rules/markdown-guidelines.mdc new file mode 100644 index 0000000..1085f11 --- /dev/null +++ b/.cursor/rules/markdown-guidelines.mdc @@ -0,0 +1,41 @@ +--- +description: Guidelines for creating Markdown files +globs: "*.md", "*.markdown", "README*" +alwaysApply: false +--- + +# Markdown Best Practices + +## Document Structure +- Use a single `# Title` at the top +- Follow with a brief description +- Organize content with hierarchical headings (`##`, `###`) +- Include a TOC for documents longer than 3 sections + +## Formatting +- Keep lines under 120 characters for readability +- Break long lines at natural points (after periods, commas, or logical breaks) +- Use **bold** for emphasis, *italics* for terminology +- Code blocks: Triple backticks with language identifier +- Inline code: Single backticks for commands, variables +- Horizontal rules (`---`) only to separate major sections + +## Lists +- Use ordered lists (1. 2. 3.) for sequential steps +- Use unordered lists (- or *) for non-sequential items +- Maintain consistent indentation for nested lists + +## Links and References +- Use descriptive link text: `[descriptive text](mdc:URL)` +- Prefer relative links for project files +- Group related references at document bottom + +## Images +- Include alt text: `![alt text](mdc:image-path)` +- Keep width under 900px for readability +- Store images in dedicated `/assets` or `/images` folder + +## Tables +- Use tables for structured data comparisons +- Include header row and alignment indicators +- Keep tables simple; avoid nested tables \ No newline at end of file diff --git a/.cursor/rules/powershell-scripting.mdc b/.cursor/rules/powershell-scripting.mdc new file mode 100644 index 0000000..028bc54 --- /dev/null +++ b/.cursor/rules/powershell-scripting.mdc @@ -0,0 +1,38 @@ +--- +description: Guidelines for creating PowerShell scripts +globs: "*.ps1", "*.psm1", "*.psd1" +alwaysApply: false +--- + +# PowerShell Scripting Guidelines + +Prefer PowerShell 7 commands for Windows 11, not Bash syntax. +Prefer PowerShell-native approaches over cmd.exe commands. + +## Command Syntax +- Use PowerShell cmdlets: `Get-ChildItem` (not `ls`), `Set-Location` (not `cd`) +- Flags use single hyphen (`-Force`) +- Command separation: `;` (not `&&`) + +## Path Handling +- Use backslashes in Windows paths: `C:\Users\` +- Use `Join-Path` for path construction +- Always quote paths with spaces + +## Variables and Environment +- Variables: `$variable`, string interpolation: `"$(...)"` +- Environment variables: `$env:VARIABLE` + +## Input/Output +- Piping: `|` (with proper object handling) +- Redirection: `>`, `>>`, `2>` + +## Error Handling +- Check `$LASTEXITCODE` after external commands +- Use try/catch blocks for risky operations +- Log errors with `Write-Error` to stderr + +## Git Bash Integration +- Use `Invoke-GitBash.ps1` for bash scripts +- Handle path conversions +- Propagate exit codes correctly \ No newline at end of file diff --git a/.cursor/rules/shell-choice.mdc b/.cursor/rules/shell-choice.mdc new file mode 100644 index 0000000..ed56d36 --- /dev/null +++ b/.cursor/rules/shell-choice.mdc @@ -0,0 +1,16 @@ +--- +description: Guidelines for choosing between PowerShell and Git Bash +globs: +alwaysApply: false +--- +--- +description: Guidelines for choosing between PowerShell and Git Bash +globs: "*.ps1", "*.sh", "*.psm1", "*.psd1", "*.md" +alwaysApply: false +--- + +# Shell Choice Guidelines + +- Use PowerShell for Windows-specific tasks and system automation +- Use Git Bash for cross-platform scripts and git operations +- Prefer PowerShell when working with .NET or Windows services diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dbd60de --- /dev/null +++ b/.dockerignore @@ -0,0 +1,29 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +.cursor/ +.vs/ +_testbuild/ +docs/ \ No newline at end of file diff --git a/.github/workflows/azure-deploy.yml b/.github/workflows/azure-deploy.yml new file mode 100644 index 0000000..7a8db5a --- /dev/null +++ b/.github/workflows/azure-deploy.yml @@ -0,0 +1,44 @@ +name: Build and Deploy to Azure + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} + + - name: Login to Azure + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy to Azure App Service + uses: azure/webapps-deploy@v3 + with: + app-name: "clarusmens-api" + images: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} diff --git a/.gitignore b/.gitignore index f54304e..665eb4b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ logs/ .env *.env secrets.json +!.env.example +!*.env.template # Dependency-Cache (NuGet) packages/ @@ -39,8 +41,6 @@ libman.json # Docker docker-compose.override.yml -**/Dockerfile -**/.dockerignore # Rider / JetBrains IDE .idea/ @@ -90,4 +90,4 @@ changes.txt staged_files.txt staged.txt -.fake \ No newline at end of file +.fake diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9f0a9fa..ec1af81 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,26 +1,26 @@ { - "version": "2.0.0", - "tasks": [ - { - "label": "Bump Patch Version", - "type": "shell", - "command": "pwsh -File ${workspaceFolder}/Update-Version.ps1 -VersionType patch", - "problemMatcher": [], - "group": "none" - }, - { - "label": "Bump Minor Version", - "type": "shell", - "command": "pwsh -File ${workspaceFolder}/Update-Version.ps1 -VersionType minor", - "problemMatcher": [], - "group": "none" - }, - { - "label": "Bump Major Version", - "type": "shell", - "command": "pwsh -File ${workspaceFolder}/Update-Version.ps1 -VersionType major", - "problemMatcher": [], - "group": "none" - } - ] -} \ No newline at end of file + "version": "2.0.0", + "tasks": [ + { + "label": "Bump Patch Version", + "type": "shell", + "command": "pwsh -File ${workspaceFolder}/scripts/Update-Version.ps1 -VersionType patch", + "problemMatcher": [], + "group": "none" + }, + { + "label": "Bump Minor Version", + "type": "shell", + "command": "pwsh -File ${workspaceFolder}/scripts/Update-Version.ps1 -VersionType minor", + "problemMatcher": [], + "group": "none" + }, + { + "label": "Bump Major Version", + "type": "shell", + "command": "pwsh -File ${workspaceFolder}/scripts/Update-Version.ps1 -VersionType major", + "problemMatcher": [], + "group": "none" + } + ] +} diff --git a/ClarusMensAPI/ClarusMensAPI.csproj b/ClarusMensAPI/ClarusMensAPI.csproj index e9fc3b8..1e57af2 100644 --- a/ClarusMensAPI/ClarusMensAPI.csproj +++ b/ClarusMensAPI/ClarusMensAPI.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -9,6 +9,7 @@ true ClarusMensAPI.FunctionalTests + 5ed24bd9-a72b-4ad1-ab12-24e36e8e60fa @@ -23,7 +24,11 @@ + + + + diff --git a/ClarusMensAPI/Configuration/ApiConfiguration.cs b/ClarusMensAPI/Configuration/ApiConfiguration.cs new file mode 100644 index 0000000..5e2e620 --- /dev/null +++ b/ClarusMensAPI/Configuration/ApiConfiguration.cs @@ -0,0 +1,72 @@ +using Microsoft.Extensions.Configuration; +using Azure.Identity; +using Azure.Security.KeyVault.Secrets; + +namespace ClarusMensAPI.Configuration; + +/// +/// Handles configuration setup for the API, including environment-specific settings +/// and secrets management. +/// +public static class ApiConfiguration +{ + /// + /// Configures the application's configuration sources based on the environment. + /// + public static void ConfigureAppConfiguration(WebApplicationBuilder builder) + { + var environment = builder.Environment; + var configuration = builder.Configuration; + + // Base configuration (already loaded by default) + // - appsettings.json + // - appsettings.{environment}.json + // - User secrets (in Development) + // - Environment variables + + if (environment.IsDevelopment()) + { + // Enable user secrets in development + builder.Configuration.AddUserSecrets(); + } + else if (environment.IsProduction()) + { + // In production, use Azure Key Vault + var keyVaultUrl = configuration["KeyVault:Url"]; + if (!string.IsNullOrEmpty(keyVaultUrl)) + { + configuration.AddAzureKeyVault( + new Uri(keyVaultUrl), + new DefaultAzureCredential()); + } + } + + // Add environment variables with prefix + configuration.AddEnvironmentVariables("CLARUSMENS_"); + } + + /// + /// Validates required configuration settings based on the environment. + /// + public static void ValidateConfiguration(IConfiguration configuration, IWebHostEnvironment environment) + { + // Validate API contact information is available + var contactName = configuration["ApiInfo:Contact:Name"]; + var contactEmail = configuration["ApiInfo:Contact:Email"]; + + if (string.IsNullOrEmpty(contactName) || string.IsNullOrEmpty(contactEmail)) + { + if (environment.IsProduction()) + { + throw new InvalidOperationException( + "API contact information is not configured. In production, these must be set via Key Vault or environment variables."); + } + else + { + // In development, we'll use defaults from appsettings.json + // Log a warning that we're using defaults + Console.WriteLine("Warning: Using default API contact information from appsettings.json"); + } + } + } +} \ No newline at end of file diff --git a/ClarusMensAPI/Program.cs b/ClarusMensAPI/Program.cs index 2d221ea..8426d78 100644 --- a/ClarusMensAPI/Program.cs +++ b/ClarusMensAPI/Program.cs @@ -1,20 +1,40 @@ +/* + * Program.cs + * + * Entry point and configuration for the Clarus Mens API. + * This file: + * - Initializes the web application + * - Configures services, middleware, and endpoints + * - Sets up OpenAPI/Swagger documentation + * - Defines application lifecycle events + * - Contains a non-static Program class to support WebApplicationFactory testing + */ + using ClarusMensAPI.Services; using ClarusMensAPI.Extensions; using ClarusMensAPI.Endpoints; +using ClarusMensAPI.Configuration; // API contract version constant const string ApiContractVersion = "v0"; var builder = WebApplication.CreateBuilder(args); +// Configure application configuration sources +ApiConfiguration.ConfigureAppConfiguration(builder); + // Service Registration builder.Services.AddApplicationServices(); builder.Services.AddOpenApiServices(ApiContractVersion); builder.Services.AddHttpsRedirection(7043); builder.Services.AddHealthChecks(); +builder.Services.AddApplicationInsightsTelemetry(); var app = builder.Build(); +// Validate configuration +ApiConfiguration.ValidateConfiguration(app.Configuration, app.Environment); + // Application Lifecycle Events app.Lifetime.ApplicationStarted.Register(() => { @@ -29,7 +49,7 @@ } // OpenAPI/Swagger Configuration -// I make OpenAPI available regardless of environment. +// Swagger will be active in production so interviewers will be able to test the API. app.MapOpenApi(); // Makes JSON spec available at /openapi app.UseSwagger(); app.UseSwaggerUI(options => diff --git a/ClarusMensAPI/ClarusMensAPI.http b/ClarusMensAPI/api-manual-endpoint-requests.http similarity index 100% rename from ClarusMensAPI/ClarusMensAPI.http rename to ClarusMensAPI/api-manual-endpoint-requests.http diff --git a/ClarusMensAPI/appsettings.Production.json b/ClarusMensAPI/appsettings.Production.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/ClarusMensAPI/appsettings.Production.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8a4d0a6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +# This Dockerfile containerizes an ASP.NET Core API application for deployment +# to Azure App Service. +# It follows a multi-stage build pattern to create an optimized image: +# 1. Build stage: Restores dependencies and builds the application +# 2. Publish stage: Creates a production-ready version of the application +# 3. Test stage: Creates an image for running tests (only used with --target=test) +# 4. Final stage: Creates a minimal image with only what's needed to run the application + +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy csproj and restore dependencies +COPY *.sln . +COPY ClarusMensAPI/*.csproj ./ClarusMensAPI/ +RUN dotnet restore ClarusMensAPI/ClarusMensAPI.csproj + +# Copy all files after restore to leverage build layer caching +COPY . . + +# Build the app +WORKDIR /src/ClarusMensAPI +RUN dotnet build -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false + +# Test stage (use with --target=test) +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS test +WORKDIR /src +COPY . . +RUN dotnet restore +ENV ASPNETCORE_URLS= +ENV ASPNETCORE_ENVIRONMENT=Development +ENV DOTNET_RUNNING_IN_CONTAINER=true +ENTRYPOINT ["dotnet", "test"] + +# Final stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Set environment variables +ENV ASPNETCORE_URLS=http://+:80 +ENV ASPNETCORE_ENVIRONMENT=Production + +# Create non-root user for security +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app + +# Install curl and create health check script +USER root +RUN apt-get update && \ + apt-get install -y curl --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* && \ + echo '#!/bin/sh\ncurl -f http://localhost:80/health || exit 1' > /usr/local/bin/healthcheck.sh && \ + chmod +x /usr/local/bin/healthcheck.sh + +USER appuser + +# Configure container health check (using exec form) +HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ + CMD ["/usr/local/bin/healthcheck.sh"] + +EXPOSE 80 +ENTRYPOINT ["dotnet", "ClarusMensAPI.dll"] \ No newline at end of file diff --git a/README.md b/README.md index 7ac3526..065f4fc 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,43 @@ # ClarusMens -A clear mind, a clear path forward. +A .NET API Template Project - Learning by Building + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [About](#about) +- [Using as a Template](#using-as-a-template) +- [Prerequisites](#prerequisites) +- [Getting Started](#getting-started) +- [Testing the API](#testing-the-api) + - [Automated Tests](#automated-tests) + - [Docker-based Testing](#docker-based-testing) + - [Using REST Client](#using-rest-client) + - [Using Browser or Postman](#using-browser-or-postman) +- [Project Structure](#project-structure) +- [Development](#development) + - [Using Hot Reload](#using-hot-reload) +- [Documentation](#documentation) +- [Versioning](#versioning) +- [License](#license) ## About -ClarusMens is a .NET-based API project that generates structured answers (MVP), learning questions with answers, and quizzes based on user input. +ClarusMens is primarily a training project that demonstrates how to build and deploy a production-ready .NET API. +While it includes a simple question-answering endpoint, its main value lies in showcasing: + +- Modern .NET API architecture and best practices +- Complete CI/CD and deployment setup +- Comprehensive testing infrastructure +- Production-ready Docker configuration +- Structured documentation approach + +The question-answering functionality serves as a simple example endpoint, allowing focus on the +infrastructure and deployment aspects of API development. ## Using as a Template -This project is designed to serve as a template for future .NET API projects. It includes: +This project is designed as a learning resource and template for future .NET API projects. It includes: - Production-ready architecture following modern .NET API best practices - Comprehensive test infrastructure with unit, integration, and functional tests @@ -65,12 +94,14 @@ The API will be available at . ### Automated Tests Run the full test suite with the Testing configuration: +(Run the command from the root directory.) ```powershell dotnet test -c Testing ``` -The `Testing` configuration is a specialized build configuration optimized for test execution, with separate output directories and test-specific settings. +The `Testing` configuration is a specialized build configuration optimized for test execution, + with separate output directories and test-specific settings. > **Note**: You may see a warning about no tests in the IntegrationTests project - this is expected and can be ignored. @@ -81,6 +112,19 @@ $env:CLARUSMENS_TEST_TIMEOUT = 60000 # Set longer timeout (in ms) dotnet test -c Testing ``` +### Docker-based Testing + +Tests can be included in the Docker image if needed: + +```powershell +docker build -t clarusmens-api-test --target=test . +docker run -it clarusmens-api-test +``` + +For standard testing, use the non-containerized approach detailed above. + +For detailed Docker build and testing documentation, see [Docker Build Documentation](docs/deployment/docker-build-documentation.md). + For more detailed testing options, CI/CD integration details, and environment variables, see the [tests README](./tests/README.md). ### Using REST Client @@ -88,7 +132,7 @@ For more detailed testing options, CI/CD integration details, and environment va This project includes `.http` files for testing API endpoints with the REST Client VS Code extension. 1. Install the REST Client extension in VS Code/Cursor -2. Open `ClarusMensAPI/ClarusMensAPI.http` +2. Open `ClarusMensAPI/api-manual-endpoint-requests.http` 3. Click "Send Request" above any request definition 4. View the response in the split window diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f8d0e6d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +version: "3.8" + +services: + api: + build: + context: . + dockerfile: Dockerfile + ports: + - "5000:80" + environment: + - ASPNETCORE_ENVIRONMENT=Development + - CLARUSMENS_ApiInfo__Contact__Name=Development Team + - CLARUSMENS_ApiInfo__Contact__Email=dev-team@example.com + volumes: + - ${APPDATA}/Microsoft/UserSecrets:/home/appuser/.microsoft/usersecrets:ro + + api-staging: + build: + context: . + dockerfile: Dockerfile + ports: + - "5001:80" + environment: + - ASPNETCORE_ENVIRONMENT=Staging + - CLARUSMENS_ApiInfo__Contact__Name=Staging Support + - CLARUSMENS_ApiInfo__Contact__Email=staging-support@example.com + - KeyVault__Url=https://your-keyvault.vault.azure.net/ + + api-prod: + build: + context: . + dockerfile: Dockerfile + ports: + - "5002:80" + environment: + - ASPNETCORE_ENVIRONMENT=Production + - KeyVault__Url=https://your-keyvault.vault.azure.net/ + # In production, we'll use Azure Key Vault for sensitive data diff --git a/docs/README.md b/docs/README.md index 65cb994..f332ae6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,19 +1,40 @@ # ClarusMens Documentation -This directory contains documentation for the ClarusMens project. - -## Documentation Structure - -- [API Testing Guide](TESTING-GUIDE.md) - How to test the API using REST Client - -## Adding Documentation - -When adding new documentation: - -1. Use markdown files (`.md`) for all textual documentation -2. Place files in the appropriate subdirectory -3. Link to new documentation from this README -4. Follow consistent formatting (see existing files for reference) +> **Project Purpose**: ClarusMens is a training project that demonstrates building and deploying a +> production-ready .NET API. While it includes basic API functionality, its main value is in showcasing +> infrastructure, testing, and deployment practices. + +## Documentation Overview + +This folder contains documentation for the Clarus Mens project. Below is an overview of the documentation structure. + +## Folder Structure + +- **getting-started/**: Onboarding and initial setup documentation + - [Troubleshooting](getting-started/troubleshooting.md): Common issues and solutions + - [Azure Training Guide](getting-started/azure-training-guide.md): Recommended Microsoft training for Azure deployment +- **development/**: Development guidelines and specifications + - [Project Structure](development/project-structure.md): Details of the project's folder structure + - [MVP Specification](development/mvp-spec.md): Minimum Viable Product specification + - [Versioning](development/versioning.md): Version management approach for the project + - [Architecture](development/architecture.md): System architecture and design principles + - [Testing Guide](development/testing-guide.md): Guidelines for testing the application +- **tools/**: Documentation about tools used in the project + - [Markdown Guidelines](tools/markdown-guidelines.md): Guidelines for creating markdown documents + - [MDC Guidelines](tools/mdc-guidelines.md): Guidelines for creating MDC files for Cursor AI + - [PowerShell Execution Policy](tools/powershell-execution-policy.md): Guide for PowerShell execution policies + - [Documentation Guide](tools/documentation-guide.md): Standards for writing documentation + - [Template Usage](tools/template-usage.md): How to use documentation templates +- **shell/**: Documentation about shell usage and scripting + - [Shell Usage Guidelines](shell/shell-usage-guidelines.md): Guidelines for choosing between PowerShell and Git Bash +- **configuration/**: Configuration-related documentation + - [Configuration](configuration/configuration.md): Configuration options for the application +- **deployment/**: Deployment and publishing documentation + - [Publishing Checklist](deployment/publishing-checklist.md): Checklist for publishing the application + - [Pre-publishing Checklist](deployment/prepublishing-checklist.md): Preparations before publishing + - [Azure Deployment Guide](deployment/azure-deployment-guide.md): Deploying with Docker to Azure + - [Application Insights](deployment/application-insights.md): Setting up and using Application Insights + - [ASP.NET Hosting Options](deployment/asp-net-hosting-options.md): Hosting options for ASP.NET applications ## Style Guide diff --git a/docs/after-mvp/ENHANCED-VERSION-SPEC.md b/docs/after-mvp/ENHANCED-VERSION-SPEC.md index bcd478a..2d2a6cb 100644 --- a/docs/after-mvp/ENHANCED-VERSION-SPEC.md +++ b/docs/after-mvp/ENHANCED-VERSION-SPEC.md @@ -2,7 +2,8 @@ ## Goal -ClarusMensAPI aims to be a robust AI-driven API that generates structured answers, learning questions, and quizzes based on user input, expanding beyond the MVP to improve functionality, usability, and security. +ClarusMensAPI aims to be a robust AI-driven API that generates structured answers, learning questions, and quizzes based +on user input, expanding beyond the MVP to improve functionality, usability, and security. ## Core Features @@ -45,4 +46,5 @@ ClarusMensAPI aims to be a robust AI-driven API that generates structured answer - **Multilingual Support**: Extend AI models to support multiple languages. -This specification builds upon the MVP, ensuring that ClarusMensAPI evolves into a powerful, scalable, and secure AI-driven API for learning and knowledge generation. +This specification builds upon the MVP, ensuring that ClarusMensAPI evolves into a powerful, scalable, +and secure AI-driven API for learning and knowledge generation. diff --git a/docs/after-mvp/api-versioning-strategy.md b/docs/after-mvp/api-versioning-strategy.md new file mode 100644 index 0000000..24b4a2b --- /dev/null +++ b/docs/after-mvp/api-versioning-strategy.md @@ -0,0 +1,308 @@ +# API Versioning Strategy + +## Overview + +This document outlines the API versioning strategy that will be implemented after the initial publication +of the Clarus Mens API. While the initial release includes basic version tracking and display functionality, +a more comprehensive API versioning approach will enable us to evolve the API over time while maintaining +backward compatibility. + +## Table of Contents + +- [Overview](#overview) +- [Table of Contents](#table-of-contents) +- [Current State](#current-state) +- [Proposed Implementation](#proposed-implementation) +- [Implementation Steps](#implementation-steps) + - [1. Install Required Packages](#1-install-required-packages) + - [2. Configure API Versioning Services](#2-configure-api-versioning-services) + - [3. Register API Versioning in Program.cs](#3-register-api-versioning-in-programcs) + - [4. Update Endpoints with Version Information](#4-update-endpoints-with-version-information) + - [5. Configure Swagger to Support API Versions](#5-configure-swagger-to-support-api-versions) + - [6. Update Swagger UI Configuration](#6-update-swagger-ui-configuration) +- [API Versioning Policy](#api-versioning-policy) + - [URL Path Versioning](#url-path-versioning) + - [Supporting Multiple Versions](#supporting-multiple-versions) + - [Version Deprecation](#version-deprecation) + - [Versioning Decision Guidelines](#versioning-decision-guidelines) +- [Client Migration Strategy](#client-migration-strategy) +- [Testing Strategy](#testing-strategy) +- [Future Considerations](#future-considerations) +- [Implementation Timeline](#implementation-timeline) + - [Traditional Development (Without AI Assistance)](#traditional-development-without-ai-assistance) + - [AI-Assisted Development (With Cursor/AI)](#ai-assisted-development-with-cursorai) + +## Current State + +The current implementation includes: + +- Version display in API responses via `VersionService` +- Version information endpoint at `/api/version` +- Swagger/OpenAPI documentation with version information +- SemVer 2.0.0 compliant versioning + +However, it lacks a formal mechanism for managing multiple API versions simultaneously, which is critical +for evolving the API without breaking existing clients. + +## Proposed Implementation + +After the initial publication, we will implement a comprehensive API versioning strategy using +the Microsoft.AspNetCore.Mvc.Versioning package. This approach will allow: + +1. Multiple versions of API endpoints to coexist +2. Clear versioning in API URLs, headers, or query parameters +3. Proper versioning documentation in Swagger/OpenAPI + +## Implementation Steps + +### 1. Install Required Packages + +```bash +dotnet add package Microsoft.AspNetCore.Mvc.Versioning +dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer +``` + +### 2. Configure API Versioning Services + +Add the following to the `ServiceCollectionExtensions.cs` file: + +```csharp +/// +/// Configures API versioning services +/// +public static IServiceCollection AddApiVersioningServices(this IServiceCollection services) +{ + // Add API versioning services + services.AddApiVersioning(options => + { + options.DefaultApiVersion = new ApiVersion(1, 0); + options.AssumeDefaultVersionWhenUnspecified = true; + options.ReportApiVersions = true; + // Configure readers for URL path segments, headers, and query strings + options.ApiVersionReader = ApiVersionReader.Combine( + new UrlSegmentApiVersionReader(), + new HeaderApiVersionReader("X-Api-Version"), + new QueryStringApiVersionReader("api-version")); + }); + + // Add versioned API explorer for Swagger/OpenAPI + services.AddVersionedApiExplorer(options => + { + // Format like 'v1' + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return services; +} +``` + +### 3. Register API Versioning in Program.cs + +Update the `Program.cs` file to register the API versioning services: + +```csharp +// Service Registration +builder.Services.AddApplicationServices(); +builder.Services.AddApiVersioningServices(); // Add this line +builder.Services.AddOpenApiServices(ApiContractVersion); +builder.Services.AddHttpsRedirection(7043); +builder.Services.AddHealthChecks(); +builder.Services.AddApplicationInsightsTelemetry(); +``` + +### 4. Update Endpoints with Version Information + +Modify the endpoint registration in each endpoint class to include version information: + +```csharp +app.MapGet("/api/v{version:apiVersion}/question", async (string query, IQuestionService questionService) => +{ + // Existing implementation +}) +.WithName("GetAnswerV1") +.HasApiVersion(new ApiVersion(1, 0)); +``` + +### 5. Configure Swagger to Support API Versions + +Create a new `SwaggerVersioningSetup.cs` class that properly configures Swagger to display multiple +API versions: + +```csharp +/// +/// Configures Swagger/OpenAPI documentation to support API versioning +/// +public class SwaggerVersioningSetup : IConfigureOptions +{ + private readonly IApiVersionDescriptionProvider _provider; + private readonly VersionService _versionService; + private readonly IConfiguration _config; + + public SwaggerVersioningSetup( + IApiVersionDescriptionProvider provider, + VersionService versionService, + IConfiguration config) + { + _provider = provider; + _versionService = versionService; + _config = config; + } + + public void Configure(SwaggerGenOptions options) + { + var apiName = _config["ApiInfo:Name"] ?? "Clarus Mens API"; + var apiDescription = _config["ApiInfo:Description"] ?? "API for Clarus Mens question answering service"; + + // Add a swagger document for each API version + foreach (var description in _provider.ApiVersionDescriptions) + { + options.SwaggerDoc( + description.GroupName, + new OpenApiInfo + { + Title = $"{apiName} {description.ApiVersion}", + Version = description.ApiVersion.ToString(), + Description = apiDescription, + Contact = new OpenApiContact + { + Name = _config["ApiInfo:Contact:Name"], + Email = _config["ApiInfo:Contact:Email"] + }, + License = new OpenApiLicense + { + Name = _config["ApiInfo:License:Name"] ?? "Apache License 2.0", + Url = !string.IsNullOrEmpty(_config["ApiInfo:License:Url"]) + ? new Uri(_config["ApiInfo:License:Url"]!) + : new Uri("https://www.apache.org/licenses/LICENSE-2.0") + } + }); + } + } +} +``` + +### 6. Update Swagger UI Configuration + +Update the Swagger UI configuration in `Program.cs`: + +```csharp +// OpenAPI/Swagger Configuration +app.MapOpenApi(); +app.UseSwagger(); +app.UseSwaggerUI(options => +{ + // Build a swagger endpoint for each API version + foreach (var description in app.Services.GetRequiredService() + .ApiVersionDescriptions) + { + options.SwaggerEndpoint( + $"/swagger/{description.GroupName}/swagger.json", + $"Clarus Mens API {description.GroupName}"); + } + options.RoutePrefix = "swagger"; +}); +``` + +## API Versioning Policy + +### URL Path Versioning + +The primary versioning method will be URL path versioning: + +- `/api/v1/question` for version 1 +- `/api/v2/question` for version 2 + +This approach is preferred because: + +- It's the most visible and client-friendly approach +- It works well with API gateways and caching +- It allows easy testing in browsers and tools + +### Supporting Multiple Versions + +We will support at least two versions of the API concurrently: + +- The current version +- The previous version + +This ensures clients have time to migrate to newer versions. + +### Version Deprecation + +When deprecating an API version: + +1. Mark it as deprecated in Swagger documentation +2. Set a sunset date at least 6 months in the future +3. Return deprecation notices in API responses +4. Provide clear migration paths to newer versions + +### Versioning Decision Guidelines + +1. **Major Version Change (v1 → v2)**: Required for breaking changes: + - Removing or renaming endpoints + - Removing required request parameters + - Changing response structure in incompatible ways + +2. **Minor Version Change (v1.1 → v1.2)**: For non-breaking additions: + - Adding new optional parameters + - Adding new properties to responses + - Adding new endpoints + +## Client Migration Strategy + +To facilitate client migration between API versions: + +1. Provide clear documentation of changes between versions +2. Include version information in error responses +3. Maintain migration guides in API documentation +4. Monitor usage of deprecated API versions + +## Testing Strategy + +For each API version, we will: + +1. Maintain separate integration test suites +2. Test backward compatibility explicitly +3. Include version-specific test cases +4. Test migration paths between versions + +## Future Considerations + +1. **API Gateway Integration**: Consider how API versioning integrates with API gateways +2. **Health Checks By Version**: Provide version-specific health checks +3. **Cross-Version Feature Flags**: Consider flags to enable/disable features across versions + +## Implementation Timeline + +The API versioning strategy will be implemented after the first publication, before introducing any +breaking changes to the API. Below are two separate estimations based on development approach: + +### Traditional Development (Without AI Assistance) + +Estimated timeline with traditional development approaches: + +1. Package installation and basic setup: 1 day +2. Endpoint versioning implementation: 2 days +3. Swagger/OpenAPI integration: 1 day +4. Testing and documentation: 2 days + +**Total estimated effort:** 6 working days (1.2 weeks) + +### AI-Assisted Development (With Cursor/AI) + +Estimated timeline with Cursor/AI assistance: + +1. Package installation and basic setup: 0.5 day +2. Endpoint versioning implementation: 1 day +3. Swagger/OpenAPI integration: 0.5 day +4. Testing and documentation: 1.5 days + +**Total estimated effort:** 3.5 working days (0.7 weeks) + +The AI-assisted approach can significantly reduce development time, particularly for code generation +and documentation tasks. However, testing requirements remain similar as verification of functionality +still requires human validation across different scenarios. + +Peter: I'm sure this isn't correct. I think the AI-assisted approach will be more like 4 - 7 hours for my training +project (including learning and documentation). Strange new dev world! diff --git a/docs/Configuration.md b/docs/configuration/configuration.md similarity index 100% rename from docs/Configuration.md rename to docs/configuration/configuration.md diff --git a/docs/cursor/terminal-pop-out-guide.md b/docs/cursor/terminal-pop-out-guide.md new file mode 100644 index 0000000..4533042 --- /dev/null +++ b/docs/cursor/terminal-pop-out-guide.md @@ -0,0 +1,70 @@ +# Understanding the "Pop out" Button in Terminal Commands + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [What It Does](#what-it-does) +- [When to Use Pop out](#when-to-use-pop-out) +- [What Happened in Our Testing](#what-happened-in-our-testing) +- [Better Approaches for Long-Running Commands](#better-approaches-for-long-running-commands) + +The "Pop out" button moves a terminal command to run in a separate window instead of directly in the chat interface. + +## What It Does + +When you click "Pop out": + +1. The command starts running in a background terminal window +2. The command continues executing, but you only see partial output in the chat +3. The conversation can continue while the command runs +4. A new terminal session is started for your next command + +## When to Use Pop out + +**Use Pop out when:** + +- Running long-running commands that might take minutes to complete +- Executing commands that produce a lot of output you don't need to see +- Starting services or servers that need to keep running +- You want to continue the conversation without waiting for command completion + +**Don't use Pop out when:** + +- You need to see the complete command output to continue +- Running quick diagnostic commands where the results are important +- Testing commands where success/failure determination is crucial +- Debugging issues where you need to analyze the full output + +## What Happened in Our Testing + +When you used "Pop out" during our Docker test commands: + +1. The commands continued running in the background +2. We only saw partial output in the chat +3. We couldn't properly analyze test results +4. Each new command started in a new shell session + +For Docker container tests, it's better to let the commands run to completion in the chat interface +so we can see the full output, including test results or error messages. + +## Better Approaches for Long-Running Commands + +Instead of using "Pop out", consider: + +1. For Docker tests: Use output redirection to files + + ```powershell + docker run [container] > results.txt + ``` + +2. For background services: Add `-d` (detached) to Docker run commands + + ```powershell + docker run -d [container] + ``` + +3. For verbose commands: Add filtering in the command itself + + ```powershell + docker logs [container] | findstr "PASS|FAIL" + ``` diff --git a/docs/deployment/application-insights.md b/docs/deployment/application-insights.md new file mode 100644 index 0000000..b38863d --- /dev/null +++ b/docs/deployment/application-insights.md @@ -0,0 +1,194 @@ +# Application Insights Integration Guide + +Application Insights is an Azure service that provides application performance management (APM) +and monitoring for your web applications. +This guide explains how to integrate and use Application Insights with the ClarusMens API. + +## Overview + +Application Insights automatically detects performance anomalies, +helps diagnose issues, +and provides analytics about how users interact with your API. +It's particularly valuable for monitoring production deployments. + +## Key Features + +### 1. Automatic Detection + +- Performance anomalies and bottlenecks +- Exception tracking with full stack traces +- Failed requests and dependency failures +- Server response time issues + +### 2. API Monitoring + +- Endpoint usage statistics +- Response times and throughput +- Dependency tracking (HTTP calls, database) +- Geographic distribution of requests + +### 3. Performance Analytics + +- Live metrics stream +- Custom metric tracking +- Resource utilization +- Performance counters + +### 4. Diagnostics + +- Detailed exception data +- Request correlation +- Log analytics +- Dependency tracking + +## Implementation Steps + +### 1. Install Required Package + +```powershell +dotnet add package Microsoft.ApplicationInsights.AspNetCore +``` + +### 2. Update Program.cs + +```csharp +var builder = WebApplication.CreateBuilder(args); + +// Add Application Insights +builder.Services.AddApplicationInsightsTelemetry(); +``` + +### 3. Configure Connection String + +Add to `appsettings.Production.json`: + +```json +{ + "ApplicationInsights": { + "ConnectionString": "YOUR_CONNECTION_STRING" + } +} +``` + +### 4. Create Azure Resource + +```powershell +# Create Application Insights resource +az monitor app-insights component create --app ClarusMensInsights --location eastus --resource-group ClarusMensRG --application-type web +``` + +## Cost Management + +### Free Tier Includes + +- 5 GB data ingestion per month +- 30 days data retention +- Basic features and alerts + +### Cost Optimization + +1. **Sampling** + - Reduce data volume while maintaining statistical accuracy + - Configure adaptive sampling in high-traffic scenarios + +2. **Data Filtering** + - Filter out unnecessary telemetry + - Focus on business-critical data + +3. **Retention Settings** + - Adjust data retention period based on needs + - Archive important data for long-term storage + +## Best Practices + +### 1. Custom Telemetry + +```csharp +// Inject telemetry client +private readonly TelemetryClient _telemetryClient; + +// Track custom events +_telemetryClient.TrackEvent("ApiEndpointCalled", new Dictionary +{ + { "EndpointName", "QuestionEndpoint" }, + { "QueryType", "Standard" } +}); +``` + +### 2. Alert Configuration + +- Set up alerts for: + - Response time thresholds + - Failed request rate + - Exception volume + - Dependency failures + +### 3. Dashboard Setup + +- Create custom dashboards for: + - API health overview + - Performance metrics + - Error monitoring + - Usage statistics + +### 4. Monitoring Strategy + +- Monitor key metrics: + - Server response time + - Request success rate + - Dependency health + - Resource utilization + +## Security Considerations + +1. **Data Privacy** + - Review collected data for PII + - Configure data scrubbing rules + - Set appropriate access controls + +2. **Access Management** + - Use Azure RBAC for access control + - Implement least-privilege principle + - Regular access review + +## Integration with Azure App Service + +Application Insights works seamlessly with Azure App Service: + +1. **Enable from Azure Portal** + + ```powershell + az webapp config appsettings set --resource-group ClarusMensRG --name clarusmens-api --settings APPLICATIONINSIGHTS_CONNECTION_STRING="YOUR_CONNECTION_STRING" + ``` + +2. **Container Configuration** + - No additional configuration needed + - Works automatically with ASP.NET Core + +## Troubleshooting + +### Common Issues + +1. **Data not appearing** + - Verify connection string + - Check sampling settings + - Ensure proper SDK initialization + +2. **Performance Impact** + - Monitor CPU usage + - Adjust sampling rate + - Review custom telemetry volume + +### Verification Steps + +1. Check live metrics stream +2. Verify telemetry ingestion +3. Test custom tracking +4. Validate alert configurations + +## Resources + +- [Official Documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) +- [ASP.NET Core Monitoring](https://docs.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core) +- [Pricing Details](https://azure.microsoft.com/en-us/pricing/details/monitor/) +- [Best Practices Guide](https://docs.microsoft.com/en-us/azure/azure-monitor/app/sampling) diff --git a/docs/deployment/asp-net-hosting-options.md b/docs/deployment/asp-net-hosting-options.md new file mode 100644 index 0000000..fa24b9a --- /dev/null +++ b/docs/deployment/asp-net-hosting-options.md @@ -0,0 +1,168 @@ +# ASP.NET Core Hosting Options + +This document outlines various options for hosting ASP.NET Core applications, from cloud providers to self-hosting solutions. + +## Cloud Hosting Options + +### 1. Microsoft Azure + +#### Azure App Service + +- **Description**: Fully managed platform for building, deploying, and scaling web apps +- **Pricing**: Free tier available (F1) with limitations; paid tiers starting ~$14/month +- **Benefits**: + + - Native integration with .NET and SQL Server + - Auto-scaling capabilities + - Deployment slots for testing + - Integrated with Azure DevOps for CI/CD +- **Limitations**: + - Free tier has CPU/memory limitations + - Costs can increase with additional services + +#### Azure Container Apps + +- **Description**: Serverless container service that lets you run microservices +- **Pricing**: Pay-per-use model; can be cost-effective for low-traffic apps +- **Benefits**: + - Built-in Kubernetes-style orchestration + - Scale to zero capability (cost savings) + - Dapr integration +- **Limitations**: + - More complex setup than App Service + +### 2. Amazon Web Services (AWS) + +#### AWS Elastic Beanstalk + +- **Description**: Platform-as-a-service for deploying web applications +- **Pricing**: Free tier available (750 hours/month of t2.micro instance) +- **Benefits**: + - Simple deployment workflow + - Auto-scaling capabilities + - Managed platform updates +- **Limitations**: + - AWS-specific configuration learning curve + +#### AWS App Runner + +- **Description**: Fully managed container application service +- **Pricing**: Pay-per-compute-second; starts around $5/month +- **Benefits**: + - Automatic scaling + - Simplified deployment from container images +- **Limitations**: + - Less mature than other AWS services + +### 3. Google Cloud Platform (GCP) + +#### Google Cloud Run + +- **Description**: Fully managed container platform +- **Pricing**: Pay-per-use model with generous free tier (2 million requests/month) +- **Benefits**: + - Scale to zero capability + - Only pay for actual usage time + - Simple deployment +- **Limitations**: + - Stateless design required + - Request timeout limits + +### 4. Render + +- **Description**: Modern cloud provider built for developers +- **Pricing**: Free tier for web services; paid plans start at $7/month +- **Benefits**: + - Simple deployment model + - Automatic SSL certificates + - Git-based deployments +- **Limitations**: + - Free tier has sleep periods after inactivity + +### 5. DigitalOcean App Platform + +- **Description**: PaaS offering from DigitalOcean +- **Pricing**: Starting at $5/month +- **Benefits**: + - Simple UI and deployment + - Global CDN included + - Automatic HTTPS +- **Limitations**: + - Higher cost for scaling compared to some alternatives + +### 6. Heroku + +- **Description**: Platform-as-a-service for easy application deployment +- **Pricing**: Free tier available; paid tiers start at $7/month +- **Benefits**: + - Simple Git-based deployment + - Add-ons ecosystem +- **Limitations**: + - Free dynos sleep after 30 minutes of inactivity + - Not .NET specialized (requires buildpacks) + +### 7. Railway + +- **Description**: Modern deployment platform focused on simplicity +- **Pricing**: Free tier available; $5/month starter plan +- **Benefits**: + - Very simple deployment + - Automatic preview environments + - Integrated database options +- **Limitations**: + - Usage-based pricing can be unpredictable + +## Self-Hosting Options + +### 8. Docker + VPS Solutions + +- **Description**: Deploying containerized applications to virtual private servers +- **Providers**: Linode, DigitalOcean, Vultr, OVH +- **Pricing**: Starting at ~$5/month for basic VPS +- **Benefits**: + - Full control over infrastructure + - Potentially lower costs for stable workloads + - Learning DevOps skills +- **Limitations**: + - Requires manual server management + - Responsibility for security and updates + +### 9. Kubernetes-based Hosting + +- **Description**: Container orchestration for more complex applications +- **Providers**: Self-hosted K8s, managed K8s services (AKS, EKS, GKE) +- **Pricing**: Varies widely based on setup and provider +- **Benefits**: + - Advanced orchestration features + - Scalability and resilience + - Industry-standard deployment +- **Limitations**: + - Steep learning curve + - Complexity overhead + +### 10. Static Site + API Hybrid + +#### GitHub Pages + Serverless API + +- **Description**: Host frontend statically, backend as serverless functions +- **Pricing**: Free tier for GitHub Pages; function costs vary by provider +- **Benefits**: + - Cost-effective for many use cases + - Scales automatically + - Simple deployment +- **Limitations**: + - Requires separating frontend/backend + - Cold start times for functions + +## Comparison Factors + +When choosing a hosting option, consider: + +1. **Cost**: Monthly fees vs. pay-per-use models +2. **Scalability**: Automatic scaling capabilities +3. **Ease of deployment**: CI/CD integration +4. **Management overhead**: Managed vs. self-managed +5. **Performance**: Geographic distribution, cold starts +6. **Development experience**: Local to production parity +7. **Database integration**: Managed database options +8. **Learning curve**: Required knowledge to deploy diff --git a/docs/deployment/azure-deployment-guide.md b/docs/deployment/azure-deployment-guide.md new file mode 100644 index 0000000..62c76e5 --- /dev/null +++ b/docs/deployment/azure-deployment-guide.md @@ -0,0 +1,338 @@ +# Docker + Azure App Service Deployment Guide + +This document provides a comprehensive guide for containerizing an ASP.NET Core application with Docker +and deploying it to Azure App Service. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Benefits of This Approach](#benefits-of-this-approach) +- [Prerequisites](#prerequisites) +- [Azure Account Setup](#azure-account-setup) + - [Account Requirements](#account-requirements) + - [Registration Process](#registration-process) + - [Regional Considerations](#regional-considerations) +- [Step 1: Containerize Your ASP.NET Core Application](#step-1-containerize-your-aspnet-core-application) + - [Create a Dockerfile](#create-a-dockerfile) + - [Create .dockerignore](#create-dockerignore) + - [Build and Test Locally](#build-and-test-locally) + - [Building for Different Purposes](#building-for-different-purposes) + - [Production Build (Default)](#production-build-default) + - [Testing Build](#testing-build) +- [Step 2: Set Up Azure Resources](#step-2-set-up-azure-resources) + - [Create Azure App Service Plan and Web App](#create-azure-app-service-plan-and-web-app) +- [Step 3: Set Up GitHub Actions for CI/CD](#step-3-set-up-github-actions-for-cicd) +- [Step 4: Configure Azure Container Registry](#step-4-configure-azure-container-registry) +- [Step 5: Create Azure Service Principal for GitHub Actions](#step-5-create-azure-service-principal-for-github-actions) +- [Step 6: Configure Application Settings](#step-6-configure-application-settings) +- [Future Database Integration](#future-database-integration) +- [Troubleshooting](#troubleshooting) +- [Resources](#resources) + +## Benefits of This Approach + +- **Containerization**: Learn Docker, an essential skill for modern development +- **Portability**: Deploy the same container to any environment +- **CI/CD Integration**: Automate builds and deployments with GitHub Actions +- **Azure Ecosystem**: Learn Microsoft's cloud platform (valuable for .NET developers) +- **Cost-Effective**: Use Azure's free tier for learning +- **Database Integration**: Easy connection to Azure SQL Database +- **Scalability**: Simple path to scale as your application grows + +## Prerequisites + +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed +- [.NET SDK](https://dotnet.microsoft.com/download) (matching your project version) +- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) installed +- An [Azure account](https://azure.microsoft.com/free/) (free tier available) +- [GitHub account](https://github.com/) for source control and GitHub Actions + +## Azure Account Setup + +### Account Requirements + +To set up an Azure account, you will need: + +- A valid email address +- A phone number for verification +- A credit or debit card (for identity verification only, no charges unless you upgrade) +- Your address details + +The free tier includes 12 months of popular services plus $200 credit for 30 days, which is sufficient +for a training project. + +### Registration Process + +1. Visit [Azure's free account page](https://azure.microsoft.com/free/) +2. Click the "Start free" button +3. Sign in with your Microsoft account or create a new one +4. Complete the identity verification process: + - Verify your email address + - Verify your phone number via text or call + - Enter your payment card details (for verification only) +5. Accept the subscription agreement and privacy statement +6. Your free Azure subscription will be automatically created + +After registration, you can access the Azure Portal at [portal.azure.com](https://portal.azure.com) to +manage your resources. + +### Regional Considerations + +**For German Users**: + +- A German-specific registration page is available at [azure.microsoft.com/de-de/free/](https://azure.microsoft.com/de-de/free/) +- This provides the same services but with: + - German language interface + - Pricing in Euros + - EU data center options for GDPR compliance + - German-specific support resources + - Documentation in German at [docs.microsoft.com/de-de/azure](https://docs.microsoft.com/de-de/azure) + +## Step 1: Containerize Your ASP.NET Core Application + +### Create a Dockerfile + +Create a `Dockerfile` in your project root: + +```dockerfile +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy csproj and restore dependencies +COPY *.sln . +COPY ClarusMensAPI/*.csproj ./ClarusMensAPI/ +RUN dotnet restore + +# Copy all files and build the app +COPY . . +WORKDIR /src/ClarusMensAPI +RUN dotnet build -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Set environment variables +ENV ASPNETCORE_URLS=http://+:80 +ENV ASPNETCORE_ENVIRONMENT=Production + +# Create non-root user for security +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +EXPOSE 80 +ENTRYPOINT ["dotnet", "ClarusMensAPI.dll"] +``` + +### Create .dockerignore + +Create a `.dockerignore` file to exclude unnecessary files: + +```dockerignore +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +``` + +### Build and Test Locally + +```powershell +docker build -t clarusmens-api . +docker run -p 5000:80 clarusmens-api +``` + +Visit `http://localhost:5000/swagger` to verify the application works. + +### Building for Different Purposes + +The Dockerfile supports conditional inclusion of test projects using build arguments: + +#### Production Build (Default) + +```powershell +docker build -t clarusmens-api . +``` + +This builds a production-optimized image without test projects. + +#### Testing Build + +```powershell +docker build -t clarusmens-api-test --build-arg INCLUDE_TESTS=true . +``` + +This includes test projects in the image, useful for: + +- Running tests in containerized environments +- Testing container-specific behaviors +- CI/CD pipelines that require containerized testing + +## Step 2: Set Up Azure Resources + +### Create Azure App Service Plan and Web App + +```powershell +# Login to Azure +az login + +# Create a resource group +az group create --name ClarusMensRG --location eastus + +# Create an App Service plan (Free tier) +az appservice plan create --name ClarusMensPlan --resource-group ClarusMensRG --sku F1 --is-linux + +# Create a Web App configured for container deployment +az webapp create --resource-group ClarusMensRG --plan ClarusMensPlan --name clarusmens-api --deployment-container-image-name mcr.microsoft.com/appsvc/staticsite:latest +``` + +## Step 3: Set Up GitHub Actions for CI/CD + +Create a GitHub Actions workflow file at `.github/workflows/azure-deploy.yml`: + +```yaml +name: Build and Deploy to Azure + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} + + - name: Login to Azure + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy to Azure App Service + uses: azure/webapps-deploy@v3 + with: + app-name: 'clarusmens-api' + images: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} +``` + +## Step 4: Configure Azure Container Registry + +```powershell +# Create Azure Container Registry +az acr create --resource-group ClarusMensRG --name clarusmensregistry --sku Basic + +# Enable admin user for the registry +az acr update --name clarusmensregistry --admin-enabled true + +# Get the credentials +az acr credential show --name clarusmensregistry +``` + +Save the username and password as GitHub repository secrets: + +- `REGISTRY_URL` = clarusmensregistry.azurecr.io +- `REGISTRY_USERNAME` = (admin username) +- `REGISTRY_PASSWORD` = (admin password) + +## Step 5: Create Azure Service Principal for GitHub Actions + +```powershell +# Create a service principal and get credentials +az ad sp create-for-rbac --name "ClarusMensGitHubAction" --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/ClarusMensRG --sdk-auth +``` + +Save the JSON output as the `AZURE_CREDENTIALS` secret in your GitHub repository. + +## Step 6: Configure Application Settings + +```powershell +# Set application settings (example for database connection) +az webapp config appsettings set --resource-group ClarusMensRG --name clarusmens-api --settings CONNECTION_STRING="your-connection-string" +``` + +## Future Database Integration + +When you're ready to add a database: + +1. Create an Azure SQL Database: + + ```powershell + az sql server create --name clarusmens-sql --resource-group ClarusMensRG --location eastus --admin-user adminuser --admin-password "ComplexPassword123!" + + az sql db create --resource-group ClarusMensRG --server clarusmens-sql --name ClarusMensDB --service-objective Basic + ``` + +2. Configure firewall rules: + + ```powershell + # Allow Azure services + az sql server firewall-rule create --resource-group ClarusMensRG --server clarusmens-sql --name AllowAzureServices --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0 + ``` + +3. Add the connection string to your Web App: + + ```powershell + az webapp config connection-string set --resource-group ClarusMensRG --name clarusmens-api --connection-string-type SQLAzure --settings DefaultConnection="Server=tcp:clarusmens-sql.database.windows.net,1433;Database=ClarusMensDB;User ID=adminuser;Password=ComplexPassword123!;Encrypt=true;Connection Timeout=30;" + ``` + +## Troubleshooting + +- **Container fails to start**: Check the logs with `az webapp log tail --name clarusmens-api --resource-group ClarusMensRG` +- **Database connection issues**: Verify firewall rules and connection strings +- **GitHub Actions failures**: Check the workflow run logs in GitHub + +## Resources + +- [Azure App Service Documentation](https://docs.microsoft.com/en-us/azure/app-service/) +- [Docker Documentation](https://docs.docker.com/) +- [GitHub Actions for Azure](https://github.com/Azure/actions) +- [.NET Containerization Guide](https://docs.microsoft.com/en-us/dotnet/core/docker/build-container) diff --git a/docs/deployment/docker-azure-deployment.md b/docs/deployment/docker-azure-deployment.md new file mode 100644 index 0000000..62c76e5 --- /dev/null +++ b/docs/deployment/docker-azure-deployment.md @@ -0,0 +1,338 @@ +# Docker + Azure App Service Deployment Guide + +This document provides a comprehensive guide for containerizing an ASP.NET Core application with Docker +and deploying it to Azure App Service. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Benefits of This Approach](#benefits-of-this-approach) +- [Prerequisites](#prerequisites) +- [Azure Account Setup](#azure-account-setup) + - [Account Requirements](#account-requirements) + - [Registration Process](#registration-process) + - [Regional Considerations](#regional-considerations) +- [Step 1: Containerize Your ASP.NET Core Application](#step-1-containerize-your-aspnet-core-application) + - [Create a Dockerfile](#create-a-dockerfile) + - [Create .dockerignore](#create-dockerignore) + - [Build and Test Locally](#build-and-test-locally) + - [Building for Different Purposes](#building-for-different-purposes) + - [Production Build (Default)](#production-build-default) + - [Testing Build](#testing-build) +- [Step 2: Set Up Azure Resources](#step-2-set-up-azure-resources) + - [Create Azure App Service Plan and Web App](#create-azure-app-service-plan-and-web-app) +- [Step 3: Set Up GitHub Actions for CI/CD](#step-3-set-up-github-actions-for-cicd) +- [Step 4: Configure Azure Container Registry](#step-4-configure-azure-container-registry) +- [Step 5: Create Azure Service Principal for GitHub Actions](#step-5-create-azure-service-principal-for-github-actions) +- [Step 6: Configure Application Settings](#step-6-configure-application-settings) +- [Future Database Integration](#future-database-integration) +- [Troubleshooting](#troubleshooting) +- [Resources](#resources) + +## Benefits of This Approach + +- **Containerization**: Learn Docker, an essential skill for modern development +- **Portability**: Deploy the same container to any environment +- **CI/CD Integration**: Automate builds and deployments with GitHub Actions +- **Azure Ecosystem**: Learn Microsoft's cloud platform (valuable for .NET developers) +- **Cost-Effective**: Use Azure's free tier for learning +- **Database Integration**: Easy connection to Azure SQL Database +- **Scalability**: Simple path to scale as your application grows + +## Prerequisites + +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed +- [.NET SDK](https://dotnet.microsoft.com/download) (matching your project version) +- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) installed +- An [Azure account](https://azure.microsoft.com/free/) (free tier available) +- [GitHub account](https://github.com/) for source control and GitHub Actions + +## Azure Account Setup + +### Account Requirements + +To set up an Azure account, you will need: + +- A valid email address +- A phone number for verification +- A credit or debit card (for identity verification only, no charges unless you upgrade) +- Your address details + +The free tier includes 12 months of popular services plus $200 credit for 30 days, which is sufficient +for a training project. + +### Registration Process + +1. Visit [Azure's free account page](https://azure.microsoft.com/free/) +2. Click the "Start free" button +3. Sign in with your Microsoft account or create a new one +4. Complete the identity verification process: + - Verify your email address + - Verify your phone number via text or call + - Enter your payment card details (for verification only) +5. Accept the subscription agreement and privacy statement +6. Your free Azure subscription will be automatically created + +After registration, you can access the Azure Portal at [portal.azure.com](https://portal.azure.com) to +manage your resources. + +### Regional Considerations + +**For German Users**: + +- A German-specific registration page is available at [azure.microsoft.com/de-de/free/](https://azure.microsoft.com/de-de/free/) +- This provides the same services but with: + - German language interface + - Pricing in Euros + - EU data center options for GDPR compliance + - German-specific support resources + - Documentation in German at [docs.microsoft.com/de-de/azure](https://docs.microsoft.com/de-de/azure) + +## Step 1: Containerize Your ASP.NET Core Application + +### Create a Dockerfile + +Create a `Dockerfile` in your project root: + +```dockerfile +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /src + +# Copy csproj and restore dependencies +COPY *.sln . +COPY ClarusMensAPI/*.csproj ./ClarusMensAPI/ +RUN dotnet restore + +# Copy all files and build the app +COPY . . +WORKDIR /src/ClarusMensAPI +RUN dotnet build -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Set environment variables +ENV ASPNETCORE_URLS=http://+:80 +ENV ASPNETCORE_ENVIRONMENT=Production + +# Create non-root user for security +RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app +USER appuser + +EXPOSE 80 +ENTRYPOINT ["dotnet", "ClarusMensAPI.dll"] +``` + +### Create .dockerignore + +Create a `.dockerignore` file to exclude unnecessary files: + +```dockerignore +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +``` + +### Build and Test Locally + +```powershell +docker build -t clarusmens-api . +docker run -p 5000:80 clarusmens-api +``` + +Visit `http://localhost:5000/swagger` to verify the application works. + +### Building for Different Purposes + +The Dockerfile supports conditional inclusion of test projects using build arguments: + +#### Production Build (Default) + +```powershell +docker build -t clarusmens-api . +``` + +This builds a production-optimized image without test projects. + +#### Testing Build + +```powershell +docker build -t clarusmens-api-test --build-arg INCLUDE_TESTS=true . +``` + +This includes test projects in the image, useful for: + +- Running tests in containerized environments +- Testing container-specific behaviors +- CI/CD pipelines that require containerized testing + +## Step 2: Set Up Azure Resources + +### Create Azure App Service Plan and Web App + +```powershell +# Login to Azure +az login + +# Create a resource group +az group create --name ClarusMensRG --location eastus + +# Create an App Service plan (Free tier) +az appservice plan create --name ClarusMensPlan --resource-group ClarusMensRG --sku F1 --is-linux + +# Create a Web App configured for container deployment +az webapp create --resource-group ClarusMensRG --plan ClarusMensPlan --name clarusmens-api --deployment-container-image-name mcr.microsoft.com/appsvc/staticsite:latest +``` + +## Step 3: Set Up GitHub Actions for CI/CD + +Create a GitHub Actions workflow file at `.github/workflows/azure-deploy.yml`: + +```yaml +name: Build and Deploy to Azure + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ secrets.REGISTRY_URL }} + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} + + - name: Login to Azure + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy to Azure App Service + uses: azure/webapps-deploy@v3 + with: + app-name: 'clarusmens-api' + images: ${{ secrets.REGISTRY_URL }}/clarusmens-api:${{ github.sha }} +``` + +## Step 4: Configure Azure Container Registry + +```powershell +# Create Azure Container Registry +az acr create --resource-group ClarusMensRG --name clarusmensregistry --sku Basic + +# Enable admin user for the registry +az acr update --name clarusmensregistry --admin-enabled true + +# Get the credentials +az acr credential show --name clarusmensregistry +``` + +Save the username and password as GitHub repository secrets: + +- `REGISTRY_URL` = clarusmensregistry.azurecr.io +- `REGISTRY_USERNAME` = (admin username) +- `REGISTRY_PASSWORD` = (admin password) + +## Step 5: Create Azure Service Principal for GitHub Actions + +```powershell +# Create a service principal and get credentials +az ad sp create-for-rbac --name "ClarusMensGitHubAction" --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/ClarusMensRG --sdk-auth +``` + +Save the JSON output as the `AZURE_CREDENTIALS` secret in your GitHub repository. + +## Step 6: Configure Application Settings + +```powershell +# Set application settings (example for database connection) +az webapp config appsettings set --resource-group ClarusMensRG --name clarusmens-api --settings CONNECTION_STRING="your-connection-string" +``` + +## Future Database Integration + +When you're ready to add a database: + +1. Create an Azure SQL Database: + + ```powershell + az sql server create --name clarusmens-sql --resource-group ClarusMensRG --location eastus --admin-user adminuser --admin-password "ComplexPassword123!" + + az sql db create --resource-group ClarusMensRG --server clarusmens-sql --name ClarusMensDB --service-objective Basic + ``` + +2. Configure firewall rules: + + ```powershell + # Allow Azure services + az sql server firewall-rule create --resource-group ClarusMensRG --server clarusmens-sql --name AllowAzureServices --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0 + ``` + +3. Add the connection string to your Web App: + + ```powershell + az webapp config connection-string set --resource-group ClarusMensRG --name clarusmens-api --connection-string-type SQLAzure --settings DefaultConnection="Server=tcp:clarusmens-sql.database.windows.net,1433;Database=ClarusMensDB;User ID=adminuser;Password=ComplexPassword123!;Encrypt=true;Connection Timeout=30;" + ``` + +## Troubleshooting + +- **Container fails to start**: Check the logs with `az webapp log tail --name clarusmens-api --resource-group ClarusMensRG` +- **Database connection issues**: Verify firewall rules and connection strings +- **GitHub Actions failures**: Check the workflow run logs in GitHub + +## Resources + +- [Azure App Service Documentation](https://docs.microsoft.com/en-us/azure/app-service/) +- [Docker Documentation](https://docs.docker.com/) +- [GitHub Actions for Azure](https://github.com/Azure/actions) +- [.NET Containerization Guide](https://docs.microsoft.com/en-us/dotnet/core/docker/build-container) diff --git a/docs/deployment/docker-build-documentation.md b/docs/deployment/docker-build-documentation.md new file mode 100644 index 0000000..17d86b6 --- /dev/null +++ b/docs/deployment/docker-build-documentation.md @@ -0,0 +1,110 @@ +# Docker Build Configuration + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Multi-stage Dockerfile Structure](#multi-stage-dockerfile-structure) +- [Building for Production](#building-for-production) +- [Building for Testing](#building-for-testing) +- [Running Tests in Container](#running-tests-in-container) +- [Key Improvements](#key-improvements) +- [Lessons Learned](#lessons-learned) + - [Docker Configuration](#docker-configuration) + - [Testing in Containers](#testing-in-containers) + - [Command Execution](#command-execution) +- [Future Improvements](#future-improvements) + +This document outlines the Docker build configuration for the Clarus Mens API, +including how to build for both production and testing environments. + +## Multi-stage Dockerfile Structure + +Our Dockerfile uses a multi-stage build pattern with separate targets: + +1. **Build stage**: Restores dependencies and builds the application +2. **Publish stage**: Creates a production-ready version +3. **Test stage**: Creates an image for running tests +4. **Final stage**: Creates a minimal runtime image + +This approach allows us to generate different images for different purposes from a single Dockerfile. + +## Building for Production + +To build the production image: + +```powershell +docker build -t clarusmens-api . +``` + +This creates a minimal image with: + +- Only the published application (no source code) +- ASP.NET Core runtime (not SDK) +- Health check configured +- Non-root user for security + +## Building for Testing + +To build the testing image: + +```powershell +docker build -t clarusmens-api-test --target=test . +``` + +This creates a testing-focused image with: + +- Full source code +- .NET SDK for building and testing +- Environment configured for testing +- Test entrypoint + +## Running Tests in Container + +```powershell +# Run all tests with default settings +docker run --rm clarusmens-api-test + +# Run with verbose output +docker run --rm clarusmens-api-test -- --verbosity normal + +# Run specific test project +docker run --rm clarusmens-api-test -- tests/ClarusMensAPI.UnitTests --verbosity detailed + +# Save test results to file +docker run --rm clarusmens-api-test -- --verbosity detailed > test-results.txt +``` + +## Key Improvements + +1. **Separate Test Target**: Created a dedicated test image configuration +2. **Environment Variables**: Configured environment to avoid web startup in tests +3. **Apt-Get Error Handling**: Added fallbacks for package installation +4. **Build Simplification**: Removed conditional logic from build stages + +## Lessons Learned + +### Docker Configuration + +1. **Multi-stage Builds**: Using the `--target` flag is cleaner than build args for different configurations +2. **Dockerfile Clarity**: Explicit stages with clear responsibilities improve maintainability +3. **Error Resilience**: Adding error handling to shell commands prevents build failures + +### Testing in Containers + +1. **Environment Variables**: Setting `ASPNETCORE_URLS=` and other + environment variables prevents the web server from starting during tests +2. **Test Entry Point**: Using `ENTRYPOINT ["dotnet", "test"]` makes the container test-focused +3. **Test Output**: Redirecting test output to files helps analyze complex test results + +### Command Execution + +1. **Terminal Output Visibility**: Avoid using "Pop out" when analyzing test output +2. **Redirecting Output**: Use `> file.txt` for capturing detailed output +3. **Container Logs**: Always check Docker logs for container startup errors + +## Future Improvements + +1. Test reporting and artifact collection +2. Integration with CI/CD pipelines +3. Volume mounting for test results and coverage reports +4. Optimization of container size and build times diff --git a/docs/deployment/future-reference/full-prepublishing-checklist.md b/docs/deployment/future-reference/full-prepublishing-checklist.md new file mode 100644 index 0000000..0cf4ee4 --- /dev/null +++ b/docs/deployment/future-reference/full-prepublishing-checklist.md @@ -0,0 +1,124 @@ +# Pre-Publishing Checklist for ClarusMens API + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Configuration and Environment Settings](#configuration-and-environment-settings) +- [Security Considerations](#security-considerations) +- [Application Health and Monitoring](#application-health-and-monitoring) +- [Performance Optimization](#performance-optimization) +- [Docker Configuration](#docker-configuration) +- [Infrastructure as Code](#infrastructure-as-code) +- [Database Considerations (Future)](#database-considerations-future) +- [Specific Recommendations for Current API](#specific-recommendations-for-current-api) +- [Testing Checklist](#testing-checklist) +- [Documentation](#documentation) + +Before deploying your ASP.NET Core application to Azure using Docker, + ensure that you've completed the following preparation steps: + +## Configuration and Environment Settings + +- [ ] Ensure `appsettings.json` has appropriate production configuration +- [ ] Remove any hardcoded development connection strings or secrets +- [ ] Set up proper environment-specific settings using `appsettings.Production.json` +- [ ] Configure logging levels appropriately for production + +## Security Considerations + +- [ ] Implement HTTPS redirection in production environment +- [ ] Ensure no sensitive information is exposed in API responses or logs +- [ ] Configure CORS policies if the API will be accessed from different origins +- [ ] Review and secure any sensitive API endpoints +- [ ] Add rate limiting for public endpoints (if applicable) + +## Application Health and Monitoring + +- [x] Implement health checks endpoint (already added in Program.cs) +- [ ] Set up appropriate exception handling and logging +- [ ] Consider implementing Application Insights for monitoring +- [ ] Add necessary telemetry for production diagnostics + +## Performance Optimization + +- [ ] Enable response compression for API responses +- [ ] Configure appropriate caching strategies where applicable +- [ ] Review and optimize database queries (future consideration) +- [ ] Consider implementing minimal API endpoints for performance-critical paths + +## Docker Configuration + +- [ ] Ensure Docker image is optimized for production +- [ ] Set appropriate memory limits in container configuration +- [ ] Configure container health checks +- [ ] Use multi-stage builds to minimize image size (already implemented) + +## Infrastructure as Code + +- [ ] Set up Azure resources using infrastructure as code (ARM templates or Terraform) +- [ ] Define environment variables in Azure App Service settings +- [ ] Configure CI/CD pipeline with appropriate approval gates + +## Database Considerations (Future) + +- [ ] Prepare database migration scripts +- [ ] Test migrations in a staging environment +- [ ] Back up any existing data +- [ ] Configure connection pooling appropriately + +## Specific Recommendations for Current API + +Based on the current state of your application, here are specific actions to take: + +1. **Create appsettings.Production.json**: + - [x] done + + ```json + { + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" + } + ``` + +2. **Update Program.cs to ensure HTTPS redirection in production**: + - [x] This is already implemented correctly in your Program.cs + +3. **Consider adding Application Insights**: + - [x] Add the Application Insights NuGet package + - [ ] Configure it in Program.cs + - [ ] connection string to be set + +4. **Review OpenAPI/Swagger Configuration**: + - [x] done + Ensure Swagger is configured properly for production. + (Swagger will be active in production so interviewers will be able to test the API.) + +5. **Add container health checks to Dockerfile**: + Add a HEALTHCHECK instruction to your Dockerfile + - [x] done + +6. **Set up Azure Monitor alerts**: + Configure alerts for application performance and availability + +## Testing Checklist + +- [x] Run all unit and integration tests +- [x] Test the Docker container locally before deployment +- [x] Verify API endpoints with Postman or similar tool +- [ ] Load test critical endpoints if expecting high traffic + +## Documentation + +- [x] Update API documentation if needed +- [ ] Document deployment process +- [ ] Document rollback procedures +- [ ] Update README with production usage instructions + +Remember that while your application is currently minimal, implementing +these best practices from the start will establish good patterns for +future development. diff --git a/docs/deployment/future-reference/full-publishing-checklist.md b/docs/deployment/future-reference/full-publishing-checklist.md new file mode 100644 index 0000000..b765dcb --- /dev/null +++ b/docs/deployment/future-reference/full-publishing-checklist.md @@ -0,0 +1,182 @@ +# Publishing Process Checklist + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Local Environment Setup](#local-environment-setup) +- [Local Testing](#local-testing) +- [Azure Setup](#azure-setup) +- [Container Registry Setup](#container-registry-setup) +- [GitHub Configuration](#github-configuration) +- [Application Configuration](#application-configuration) +- [Deployment Verification](#deployment-verification) +- [Post-Deployment](#post-deployment) +- [Rollback Plan](#rollback-plan) +- [Final Checks](#final-checks) + +This checklist guides you through the actual process of publishing your ASP.NET Core application to Azure. Complete the +[Pre-Publishing Checklist](PREPUBLISHING_CHECKLIST.md) before starting this process. + +## Local Environment Setup + +- [ ] Install required tools: + - [ ] [Docker Desktop](https://www.docker.com/products/docker-desktop/) + - [ ] [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) + - [ ] [.NET SDK 9.0](https://dotnet.microsoft.com/download) + +- [ ] Docker configuration: + - [ ] Start Docker Desktop + - [ ] Verify Docker is running: `docker --version` + - [ ] Clean up any old containers/images if needed + +## Local Testing + +- [ ] Build and test Docker image locally: + + ```powershell + docker build -t clarusmens-api . + docker run -p 5000:80 clarusmens-api + ``` + +- [ ] Verify endpoints: + - [ ] Check health endpoint: + - [ ] Access Swagger UI: + - [ ] Test API endpoints using Swagger UI + - [ ] Verify OpenAPI spec: + +- [ ] Run automated tests: + + ```powershell + dotnet test -c Testing + ``` + +## Azure Setup + +- [ ] Azure account preparation: + - [ ] Create Azure account if needed + - [ ] Install Azure CLI + - [ ] Login to Azure: `az login` + - [ ] Select appropriate subscription: + + ```powershell + az account list + az account set --subscription + ``` + +- [ ] Resource creation: + - [ ] Create resource group: + + ```powershell + az group create --name ClarusMensRG --location eastus + ``` + + - [ ] Create App Service plan: + + ```powershell + az appservice plan create --name ClarusMensPlan --resource-group ClarusMensRG --sku F1 --is-linux + ``` + + - [ ] Create Web App: + + ```powershell + az webapp create --resource-group ClarusMensRG --plan ClarusMensPlan --name clarusmens-api --deployment-container-image-name mcr.microsoft.com/appsvc/staticsite:latest + ``` + +## Container Registry Setup + +- [ ] Create and configure Azure Container Registry: + + ```powershell + az acr create --resource-group ClarusMensRG --name clarusmensregistry --sku Basic + az acr update --name clarusmensregistry --admin-enabled true + ``` + +- [ ] Get registry credentials: + + ```powershell + az acr credential show --name clarusmensregistry + ``` + +## GitHub Configuration + +- [ ] Repository setup: + - [ ] Push code to GitHub if not already done + - [ ] Configure GitHub repository secrets: + - [ ] `REGISTRY_URL` + - [ ] `REGISTRY_USERNAME` + - [ ] `REGISTRY_PASSWORD` + - [ ] `AZURE_CREDENTIALS` + +- [ ] Create service principal for GitHub Actions: + + ```powershell + az ad sp create-for-rbac --name "ClarusMensGitHubAction" --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/ClarusMensRG --sdk-auth + ``` + +## Application Configuration + +- [ ] Configure application settings: + + ```powershell + az webapp config appsettings set --resource-group ClarusMensRG --name clarusmens-api --settings WEBSITES_PORT=80 + ``` + +- [ ] Set up Application Insights: + + ```powershell + az monitor app-insights component create --app ClarusMensInsights --location eastus --resource-group ClarusMensRG --application-type web + ``` + +## Deployment Verification + +- [ ] Monitor deployment: + - [ ] Check GitHub Actions workflow progress + - [ ] Monitor container startup in Azure portal + - [ ] Check application logs: + + ```powershell + az webapp log tail --name clarusmens-api --resource-group ClarusMensRG + ``` + +- [ ] Verify production deployment: + - [ ] Check health endpoint + - [ ] Verify Swagger UI access + - [ ] Test all API endpoints + - [ ] Monitor Application Insights for any issues + +## Post-Deployment + +- [ ] Set up monitoring: + - [ ] Configure Azure Monitor alerts + - [ ] Set up email notifications for critical issues + - [ ] Review Application Insights data + +- [ ] Documentation: + - [ ] Update README with production URL + - [ ] Document any deployment-specific configurations + - [ ] Record any issues and solutions encountered + +## Rollback Plan + +- [ ] Document rollback procedure: + + ```powershell + # Example: Revert to previous deployment + az webapp deployment slot swap --resource-group ClarusMensRG --name clarusmens-api --slot staging --target-slot production + ``` + +- [ ] Test rollback procedure in staging environment + +## Final Checks + +- [ ] Security: + - [ ] Verify HTTPS is enforced + - [ ] Check all endpoints require appropriate authentication + - [ ] Verify no sensitive data in logs + +- [ ] Performance: + - [ ] Check response times + - [ ] Monitor resource usage + - [ ] Verify auto-scaling settings (if configured) + +Remember to maintain this checklist for future deployments and update it based on lessons learned from each deployment. diff --git a/docs/deployment/prepublishing-checklist.md b/docs/deployment/prepublishing-checklist.md new file mode 100644 index 0000000..421c118 --- /dev/null +++ b/docs/deployment/prepublishing-checklist.md @@ -0,0 +1,215 @@ +# Pre-Publishing Checklist (Training Project) + +> **Focus**: This checklist covers essential steps for publishing a training/template .NET API project. +> It focuses on demonstrating deployment practices while keeping things simple. +> +> **Maintenance**: This document follows the checklist maintenance rules maintained by Cursor AI. +> Updates follow the standard status indicators: āœ… (completed), - [ ] (todo), šŸ”„ (in-progress), āš ļø (blocked). +> +> **Guidelines for maintaining this document:** +> +> - Review and update at the end of each sprint/start of merge +> - Mark items with āœ… when fully completed +> - Add new requirements as they're identified +> - Keep sections organized by logical grouping +> - Ensure priority items are at the top of their sections + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Code Preparation](#code-preparation) +- [Docker Setup](#docker-setup) +- [Documentation](#documentation) +- [Testing](#testing) +- [Configuration Management](#configuration-management) +- [Security](#security) +- [Monitoring and Health Checks](#monitoring-and-health-checks) +- [Performance](#performance) +- [Version Management](#version-management) +- [Future-Proofing](#future-proofing) +- [Detailed Security Review](#detailed-security-review) + - [Configuration Files](#configuration-files) + - [Source Code](#source-code) + - [Docker Configuration](#docker-configuration) + - [CI/CD Configuration](#cicd-configuration) + - [Environment Variables](#environment-variables) + - [Authentication/Authorization](#authenticationauthorization) + +## Code Preparation + +Essential steps to prepare your code: + +āœ… Update project purpose in documentation to reflect training/template focus +āœ… Ensure basic API endpoint works (`/api/question`) +āœ… Configure proper error handling +āœ… Set up health check endpoint +āœ… Ensure proper logging is configured + +- [ ] Review and remove any hardcoded values or secrets + +## Docker Setup + +Docker configuration for deployment: + +āœ… Create multi-stage Dockerfile with build, publish, test and runtime stages +āœ… Configure container health checks +āœ… Set up proper base images +āœ… Add `.dockerignore` file +āœ… Implement security best practices (non-root user, minimal dependencies) +āœ… Optimize layer caching for faster builds +āœ… Fixed Dockerfile linting issues (exec form for healthcheck, layer reduction) + +- [ ] Test Docker build locally +- [ ] Test Docker container locally + +## Documentation + +Essential documentation updates: + +āœ… Update README with project purpose +āœ… Document API endpoints +āœ… Add deployment documentation +āœ… Create well-structured markdown files +āœ… Document environment-specific configuration approaches + +- [ ] Add deployment URLs once available +- [ ] Add API versioning documentation + +## Testing + +Verify everything works: + +āœ… Unit tests passing +āœ… Functional tests passing +āœ… Test configuration properly separated +āœ… Docker test stage available (`--target=test`) + +- [ ] Test Docker container locally: + +```bash +docker build -t clarusmens-api . +docker run -p 5000:80 clarusmens-api +``` + +- [ ] Test endpoints in container: + - [ ] Health check endpoint + - [ ] Main API endpoint +- [ ] Add load testing configuration + +## Configuration Management + +Configuration setup: + +āœ… Well-structured configuration files with environment-specific settings +āœ… Application Insights integration properly configured +āœ… Implementation of layered configuration approach (appsettings → env-specific → secrets/key vault) +āœ… Configuration for different environments (development, staging, production) + +- [ ] Complete implementation of API contact information via environment variables or Azure Key Vault + +## Security + +Security measures: + +āœ… HTTPS redirection properly configured for non-development environments +āœ… `.dockerignore` properly excludes sensitive files +āœ… `.gitignore` properly excludes secrets and environment files +āœ… Non-root user configured in container +āœ… User Secrets configured for local development + +- [ ] Add rate limiting for API endpoints (after first publish possible) +- [ ] Implement CORS policy configuration (after first publish possible) + +## Monitoring and Health Checks + +Monitoring setup: + +āœ… Health checks implemented and exposed via endpoint +āœ… Application Insights properly configured +āœ… Container health check configured in Dockerfile +āœ… Health check script implemented with proper permissions + +- [ ] Add custom telemetry for business-critical operations (after first publish possible) + +## Performance + +Performance optimizations: + +āœ… Invariant globalization enabled for better performance + +- [ ] Implement response compression (after first publish possible) +- [ ] Add caching strategy for appropriate endpoints (after first publish possible) + +## Version Management + +Version tracking: + +āœ… Version information properly tracked +āœ… Add API versioning strategy (to be extended after first publish) + +- [ ] Add staging environment deployment step + +## Future-Proofing + +Plan for the future: + +- [ ] Prepare database migration strategy +- [ ] Add scalability considerations documentation + +## Detailed Security Review + +### Configuration Files + +āœ… Configuration structured to avoid exposing secrets + +- [ ] Review remaining items in appsettings.json + - [ ] Connection strings + - [ ] API endpoints + - [ ] Service URLs + - [ ] Default credentials +- [ ] Review appsettings.Development.json + - [ ] Development-specific secrets + - [ ] Test credentials +- [ ] Check launchSettings.json for exposed values +āœ… Secrets.json is properly excluded from source control + +### Source Code + +- [ ] Check Controllers/Endpoints for hardcoded values +- [ ] Review Service classes for embedded credentials +- [ ] Check initialization code in Program.cs +- [ ] Review middleware configuration for sensitive data +- [ ] Check test files for exposed test credentials + +### Docker Configuration + +āœ… Review Dockerfile for exposed values +āœ… Check .dockerignore excludes sensitive files (secrets.json, appsettings.*.json,*.pfx certificates, .env files) +āœ… Docker-compose environment variables properly configured + +### CI/CD Configuration + +- [ ] Review GitHub Actions workflows + - [ ] All secrets use GitHub Secrets + - [ ] No hardcoded connection strings + - [ ] No exposed access tokens +- [ ] Check Azure deployment settings + - [ ] Connection strings use Azure Key Vault + - [ ] App settings use proper configuration + +### Environment Variables + +āœ… Environment variables set up with proper prefix (CLARUSMENS_) + +- [ ] Complete documentation of required environment variables +- [ ] Remove any hardcoded fallback values +- [ ] Verify development values are not used in production + +### Authentication/Authorization + +- [ ] Check for hardcoded API keys +- [ ] Review JWT configuration +- [ ] Check OAuth client secrets +- [ ] Verify certificate paths are configurable + +[↑ Back to Code Preparation](#code-preparation) diff --git a/docs/deployment/publishing-checklist.md b/docs/deployment/publishing-checklist.md new file mode 100644 index 0000000..af4d2c0 --- /dev/null +++ b/docs/deployment/publishing-checklist.md @@ -0,0 +1,104 @@ +# Publishing Checklist (Training Project) + +> **Focus**: Simple steps to publish the training/template API project using GitHub and Azure. +> This checklist focuses on the essential deployment steps for demonstration purposes. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Prerequisites](#prerequisites) +- [Local Testing](#local-testing) +- [GitHub Setup](#github-setup) +- [Azure Setup](#azure-setup) +- [Deployment](#deployment) +- [Verification](#verification) + +## Prerequisites + +Before starting deployment: + +- [ ] Complete all items in [Pre-Publishing Checklist](prepublishing-checklist.md) +- [ ] Install Docker Desktop +- [ ] Create a GitHub account (if not already done) +- [ ] Create an Azure account (free tier is sufficient) + +## Local Testing + +Verify everything works locally: + +- [ ] Build Docker image: + + ```bash + docker build -t clarusmens-api . + ``` + +- [ ] Run container: + + ```bash + docker run -p 5000:80 clarusmens-api + ``` + +- [ ] Test endpoints: + - [ ] + - [ ] + - [ ] Test main API endpoint + +## GitHub Setup + +1. [ ] Push your code to GitHub: + + ```bash + git push origin main + ``` + +2. [ ] Create `.github/workflows` directory: + + ```bash + mkdir -p .github/workflows + ``` + +3. [ ] Create deployment workflow file (will be provided in next step) + +## Azure Setup + +1. [ ] Sign up for Azure (free tier): + - Go to + - Create account with Microsoft credentials + - No credit card required for free tier + +2. [ ] Install Azure CLI: + - Download from: + - Verify installation: `az --version` + +3. [ ] Login to Azure: + + ```bash + az login + ``` + +## Deployment + +1. [ ] Create Azure Web App (through portal): + - Choose "Create a resource" + - Select "Web App" + - Choose Free tier (F1) + - Enable Docker + +2. [ ] Configure GitHub Actions: + - In your GitHub repository: + - Go to "Settings" > "Secrets" + - Add Azure deployment credentials (will be provided) + - Add workflow file (template will be provided) + +## Verification + +After deployment: + +- [ ] Check deployment status in GitHub Actions +- [ ] Verify app is running: + - [ ] Health endpoint + - [ ] Swagger UI + - [ ] Test main API endpoint +- [ ] Update documentation with production URLs + +Note: Detailed Azure setup and GitHub Actions workflow files will be provided in separate guides. diff --git a/ARCHITECTURE.md b/docs/development/architecture.md similarity index 67% rename from ARCHITECTURE.md rename to docs/development/architecture.md index 10fa072..f6888a4 100644 --- a/ARCHITECTURE.md +++ b/docs/development/architecture.md @@ -1,4 +1,23 @@ -# API Architecture +# Architecture Overview + +> **Training Project**: This architecture demonstrates best practices for .NET API development. +> While the API functionality is basic, the infrastructure and patterns shown here are +> production-ready and can be used as a template for more complex APIs. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [API Architecture](#api-architecture) +- [Overview](#overview) +- [API Structure](#api-structure) + - [Service Registration](#service-registration) + - [Application Lifecycle Events](#application-lifecycle-events) + - [Middleware Configuration](#middleware-configuration) + - [Endpoint Registration](#endpoint-registration) +- [API Contract Versioning](#api-contract-versioning) +- [Testability](#testability) + +## API Architecture This document describes the recommended API setup pattern for .NET 9 minimal APIs in the Clarus Mens project. For detailed information about the project's folder structure, please refer to [PROJECT_STRUCTURE.md](PROJECT_STRUCTURE.md). diff --git a/MVP-SPEC.md b/docs/development/mvp-spec.md similarity index 64% rename from MVP-SPEC.md rename to docs/development/mvp-spec.md index aa98e49..ced58cc 100644 --- a/MVP-SPEC.md +++ b/docs/development/mvp-spec.md @@ -1,6 +1,19 @@ -# ClarusMensAPI MVP Scope +# MVP Specification -## Goal +> **Project Focus**: ClarusMens MVP demonstrates building a production-ready .NET API infrastructure. +> The API functionality is intentionally simple to allow focus on deployment, testing, and best practices. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [ClarusMensAPI MVP Scope](#clarusmensapi-mvp-scope) + - [Goal](#goal) +- [Core Features](#core-features) +- [Deployment](#deployment) + +## ClarusMensAPI MVP Scope + +### Goal ClarusMensAPI is an ASP.NET Minimal API designed to process user questions and generate structured answers using AI. @@ -28,4 +41,5 @@ ClarusMensAPI is an ASP.NET Minimal API designed to process user questions and g - Host the API on **Railway** for automated deployments. - Use **GitHub** for version control and collaboration. -This MVP focuses on ensuring the API reliably processes valid questions and returns meaningful AI-generated responses while maintaining security and usability. +This MVP focuses on ensuring the API reliably processes valid questions and returns meaningful AI-generated responses +while maintaining security and usability. diff --git a/PROJECT_STRUCTURE.md b/docs/development/project-structure.md similarity index 100% rename from PROJECT_STRUCTURE.md rename to docs/development/project-structure.md diff --git a/docs/TESTTING-GUIDE.md b/docs/development/testing-guide.md similarity index 95% rename from docs/TESTTING-GUIDE.md rename to docs/development/testing-guide.md index 731aa03..357dac0 100644 --- a/docs/TESTTING-GUIDE.md +++ b/docs/development/testing-guide.md @@ -12,7 +12,7 @@ The project includes `.http` files for testing API endpoints: -- `ClarusMensAPI/ClarusMensAPI.http` - Basic API endpoints +- `ClarusMensAPI/api-manual-endpoint-requests.http` - Basic API endpoints ### Running Requests diff --git a/VERSIONING.md b/docs/development/versioning.md similarity index 76% rename from VERSIONING.md rename to docs/development/versioning.md index 07237fc..eb3f978 100644 --- a/VERSIONING.md +++ b/docs/development/versioning.md @@ -12,7 +12,8 @@ We follow [Semantic Versioning 2.0.0](https://semver.org/) (`MAJOR.MINOR.PATCH`) ## Current Setup -Version information is centralized in the `Directory.Build.props` file in the solution root. This approach ensures that all projects in the solution share the same version number. +Version information is centralized in the `Directory.Build.props` file in the solution root. +This approach ensures that all projects in the solution share the same version number. ### Version Properties @@ -57,14 +58,14 @@ The `Update-Version.ps1` script maintains consistency across all these propertie To update the version number, run the `Update-Version.ps1` script from the solution root: ```powershell -# Bump patch version (e.g., 0.5.0 → 0.5.1) -.\Update-Version.ps1 -VersionType patch +# Increment patch version (1.2.3 → 1.2.4) +.\scripts\Update-Version.ps1 -VersionType patch -# Bump minor version (e.g., 0.5.0 → 0.6.0) -.\Update-Version.ps1 -VersionType minor +# Increment minor version (1.2.3 → 1.3.0) +.\scripts\Update-Version.ps1 -VersionType minor -# Bump major version (e.g., 0.5.0 → 1.0.0) -.\Update-Version.ps1 -VersionType major +# Increment major version (1.2.3 → 2.0.0) +.\scripts\Update-Version.ps1 -VersionType major ``` ### Using VS Code Tasks @@ -89,10 +90,15 @@ For convenience, VS Code tasks have been configured to run the script: 1. Determine the appropriate version increment (major, minor, patch) 2. Run the version update script 3. Commit the updated `Directory.Build.props` file -4. Create a tag for the release with a descriptive message: `git tag -a v{VERSION} -m "Version {VERSION}: [Brief description of changes]"` +4. Create a tag for the release with a descriptive message: + + ```bash + git tag -a v{VERSION} -m "Version {VERSION}: [Brief description]" + ``` + - Include a short summary of key changes, new features, or bug fixes - - Example: `git tag -a v0.5.1 -m "Version 0.5.1: Fixed API response formatting and improved error handling"` - - Including the version number in both the tag and message ensures consistency and makes it easier to identify the version when viewing git logs or browsing repository interfaces + - Example: `git tag -a v0.5.1 -m "Version 0.5.1: Fixed API formatting"` + - Including version in tag and message ensures consistency 5. Push the changes and tags: `git push && git push --tags` ## Viewing Version Information @@ -123,14 +129,16 @@ GET /api/version } ``` -Note: In .NET, the `System.Version` class uses four components (Major.Minor.Build.Revision), where the SemVer "patch" component maps to .NET's "build" component. +Note: In .NET, the `System.Version` class uses four components (Major.Minor.Build.Revision). +The SemVer "patch" component maps to .NET's "build" component. ### Display Versions and Pre-release Identifiers The `VersionService` provides methods to generate different version formats: - `GetVersionString()` - Returns the three-part version number (e.g., "0.5.0") -- `GetDisplayVersion(suffix)` - Returns a display-friendly version with optional suffix and environment name (e.g., "0.5.0-beta (Development)") +- `GetDisplayVersion(suffix)` - Returns a display-friendly version with suffix and environment + (e.g., "0.5.0-beta (Development)") ## Adding Version Display to Your Application @@ -148,13 +156,16 @@ The version defined in `Directory.Build.props` is used during build time to set: - `AssemblyVersion` (e.g., "0.5.0.0") - Used by .NET for assembly identity - `FileVersion` (e.g., "0.5.0.0") - Used by Windows for file properties -The `Update-Version.ps1` script sets AssemblyVersion and FileVersion to "$newVersion.0", adding a fourth component (revision) set to 0. +The `Update-Version.ps1` script sets `AssemblyVersion` and `FileVersion` to "$newVersion.0", +adding a fourth component (revision) set to 0. -At runtime, the application accesses this information through `Assembly.GetName().Version` or via the custom `VersionService`. +At runtime, the application accesses version info through `Assembly.GetName().Version` +or via the custom `VersionService`. ## Fixing Versioning with SemVer in .NET -Yes, Semantic Versioning (SemVer) is very useful for your .NET project and I recommend implementing it more consistently. SemVer provides clear communication about compatibility and is widely used in the .NET ecosystem, especially for NuGet packages. +SemVer is very useful for your .NET project and I recommend implementing it more consistently. +It provides clear communication about compatibility and is widely used in the .NET ecosystem. ## Current Issues @@ -240,8 +251,8 @@ Add proper support for pre-release versions: ```powershell # Examples: -.\Update-Version.ps1 -VersionType minor -PreRelease "beta1" # 0.5.0 → 0.6.0-beta1 -.\Update-Version.ps1 -VersionType patch # 0.6.0-beta1 → 0.6.0 +.\scripts\Update-Version.ps1 -VersionType minor -PreRelease "beta1" # 0.5.0 → 0.6.0-beta1 +.\scripts\Update-Version.ps1 -VersionType patch # 0.6.0-beta1 → 0.6.0 ``` ## Benefits of Consistent SemVer in .NET @@ -250,12 +261,3 @@ Add proper support for pre-release versions: 2. **Dependency Management**: Clearer compatibility expectations 3. **Release Management**: Easier to automate and understand release processes 4. **Industry Standards**: Following best practices used across .NET ecosystem - -## Implementation Plan - -1. Update `Directory.Build.props` to use the recommended version properties -2. Enhance `Update-Version.ps1` to support pre-release identifiers -3. Modify `VersionService` to better align with SemVer -4. Update documentation to clearly explain the SemVer approach - -Would you like me to create specific code examples for any of these components to implement a more consistent SemVer approach? diff --git a/docs/getting-started/azure-training-guide.md b/docs/getting-started/azure-training-guide.md new file mode 100644 index 0000000..682ed7b --- /dev/null +++ b/docs/getting-started/azure-training-guide.md @@ -0,0 +1,71 @@ +# Azure Training Guide + +> This document recommends specific Microsoft Azure training resources that are most relevant +> for deploying the Clarus Mens API project to Azure App Service using Docker containers. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Recommended Training Resources](#recommended-training-resources) + - [Essential Training (2-3 hours)](#essential-training-2-3-hours) + - [Quick Start Option (30-45 minutes)](#quick-start-option-30-45-minutes) +- [Why These Resources](#why-these-resources) +- [Additional Learning Paths](#additional-learning-paths) + +## Recommended Training Resources + +### Essential Training (2-3 hours) + +**"Deploy and manage containers with Azure App Service"** +[https://learn.microsoft.com/en-us/training/paths/deploy-manage-containers-app-service/](https://learn.microsoft.com/en-us/training/paths/deploy-manage-containers-app-service/) + +This learning path covers: + +- Deploying containerized applications to Azure App Service +- Configuring continuous deployment from GitHub and container registries +- Managing application settings and configuration +- Scaling and monitoring container deployments +- Implementing staging environments with deployment slots + +### Quick Start Option (30-45 minutes) + +If you need just the essentials to get started quickly: + +**"Deploy a container instance in Azure"** +[https://learn.microsoft.com/en-us/training/modules/deploy-run-container-app-service/](https://learn.microsoft.com/en-us/training/modules/deploy-run-container-app-service/) + +This focused module covers: + +- Basic container deployment to App Service +- Command-line deployment using Azure CLI +- Essential configuration options + +## Why These Resources + +These specific training resources were selected because they: + +1. **Directly align with our deployment approach** using Docker containers and Azure App Service +2. **Focus on practical implementation** rather than theoretical concepts +3. **Include hands-on exercises** that mirror our actual deployment steps +4. **Are officially maintained by Microsoft** and kept up-to-date +5. **Can be completed in a short timeframe** (under 3 hours) + +The deployment process in these trainings closely matches our +[Azure Deployment Guide](../deployment/azure-deployment-guide.md), making them +ideal preparation before performing the actual deployment. + +## Additional Learning Paths + +For team members who want to deepen their Azure knowledge further: + +- **"Implement containerized solutions"** (6-8 hours) + [https://learn.microsoft.com/en-us/training/paths/implement-containerized-solutions/](https://learn.microsoft.com/en-us/training/paths/implement-containerized-solutions/) + +- **"Implement continuous integration and continuous delivery"** (4-5 hours) + [https://learn.microsoft.com/en-us/training/paths/implement-ci-cd-azure-devops/](https://learn.microsoft.com/en-us/training/paths/implement-ci-cd-azure-devops/) + +- **"AZ-204: Developing Solutions for Microsoft Azure"** (Certification preparation) + [https://learn.microsoft.com/en-us/training/paths/create-azure-app-service-web-apps/](https://learn.microsoft.com/en-us/training/paths/create-azure-app-service-web-apps/) + +These additional resources go beyond the immediate needs of our project but provide valuable +knowledge for future Azure-based development. diff --git a/docs/TROUBLESHOOTING.md b/docs/getting-started/troubleshooting.md similarity index 68% rename from docs/TROUBLESHOOTING.md rename to docs/getting-started/troubleshooting.md index 84d574c..44c5538 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/getting-started/troubleshooting.md @@ -4,8 +4,14 @@ This document contains solutions to common issues encountered during development ## Table of Contents +- [Table of Contents](#table-of-contents) - [.NET 9 Serialization Issues](#net-9-serialization-issues) + - [PipeWriter UnflushedBytes Exception](#pipewriter-unflushedbytes-exception) - [Testing Issues](#testing-issues) +- ["No test is available in +ClarusMensAPI.IntegrationTests.dll"](#no-test-is-available-in-clarusmensapiintegrationtestsdll) + - [Tests Pass Locally But Fail in CI Pipeline](#tests-pass-locally-but-fail-in-ci-pipeline) + - [Tests Are Too Slow](#tests-are-too-slow) - [Common Development Environment Issues](#common-development-environment-issues) ## .NET 9 Serialization Issues @@ -14,23 +20,31 @@ This document contains solutions to common issues encountered during development **Problem:** -When using `Results.Json()` or `Results.Ok()` with serialized objects in ASP.NET Core minimal APIs on .NET 9, you may encounter the following exception: +When using `Results.Json()` or `Results.Ok()` with serialized objects in ASP.NET Core minimal APIs on .NET 9, you may +encounter the following exception: ```powershell System.InvalidOperationException: The PipeWriter 'ResponseBodyPipeWriter' does not implement PipeWriter.UnflushedBytes. - at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(PipeWriter pipeWriter) - at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(PipeWriter pipeWriter, T rootValue, Int32 flushThreshold, CancellationToken cancellationToken, Object rootValueBoxed) - at Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.g__WriteAsJsonAsyncSlow|5_0[TValue](HttpResponse response, TValue value, JsonTypeInfo`1 jsonTypeInfo, CancellationToken cancellationToken) +at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(PipeWriter +pipeWriter) +at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(PipeWriter pipeWriter, T rootValue, Int32 +flushThreshold, CancellationToken cancellationToken, Object rootValueBoxed) +at +Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.g__WriteAsJsonAsyncSlow|5_0[TValue](HttpResponse +response, TValue value, JsonTypeInfo`1 jsonTypeInfo, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteTaskResult[T](Task`1 task, HttpContext httpContext) ``` **Cause:** -In .NET 9, there appears to be an incompatibility between System.Text.Json serialization and the ASP.NET Core HTTP response pipeline. The `PipeWriter` implementation in .NET 9 does not provide the `UnflushedBytes` property expected by the serializer. +In .NET 9, there appears to be an incompatibility between System.Text.Json serialization and the ASP.NET Core HTTP +response pipeline. The `PipeWriter` implementation in .NET 9 does not provide the `UnflushedBytes` property expected by +the serializer. **Solution:** -Instead of using `Results.Json()` or `Results.Ok()` with objects that need serialization, use our extension methods that safely handle JSON serialization: +Instead of using `Results.Json()` or `Results.Ok()` with objects that need serialization, use our extension methods that +safely handle JSON serialization: ```csharp // DO NOT use this (will throw exception in .NET 9): @@ -47,11 +61,13 @@ return response.JsonSafeOk(); return new { error = "Invalid input" }.JsonSafeWithStatus(400); ``` -These extension methods work by manually serializing to a JSON string and returning it with the appropriate content type, avoiding the problematic serialization path. +These extension methods work by manually serializing to a JSON string and returning it with the appropriate content +type, avoiding the problematic serialization path. **Test Verification:** -The issue was identified during functional testing with endpoint tests that verify HTTP status codes and response payloads. The fix was confirmed by running the test suite and verifying all tests pass. +The issue was identified during functional testing with endpoint tests that verify HTTP status codes and response +payloads. The fix was confirmed by running the test suite and verifying all tests pass. **References:** @@ -73,7 +89,8 @@ No test is available in [...]\ClarusMensAPI.IntegrationTests.dll **Cause:** -The IntegrationTests project is currently set up as a placeholder for future integration tests but doesn't contain any test methods yet. +The IntegrationTests project is currently set up as a placeholder for future integration tests but doesn't contain any +test methods yet. **Solution:** diff --git a/docs/powershell-execution-policy.md b/docs/powershell-execution-policy.md deleted file mode 100644 index 903eff1..0000000 --- a/docs/powershell-execution-policy.md +++ /dev/null @@ -1,93 +0,0 @@ -# PowerShell Execution Policy Guide - -## Running Update-Version.ps1 - -The `Update-Version.ps1` script updates version numbers in `Directory.Build.props` -following Semantic Versioning (SemVer), but requires appropriate PowerShell -execution policy settings to run successfully. - -## Execution Options - -### Option 1: Temporary Bypass (Session Only) - -This option applies only to the current PowerShell session and is useful for -one-time executions: - -```powershell -Set-ExecutionPolicy Bypass -Scope Process -.\Update-Version.ps1 -VersionType minor -``` - -### Option 2: Development Environment Setup - -For development environments where you'll frequently run local scripts: - -```powershell -Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -``` - -This allows unsigned local scripts to run while still requiring signatures for -downloaded scripts. After setting this once, you can run scripts normally: - -```powershell -.\Update-Version.ps1 -VersionType minor -``` - -### Option 3: One-line Execution Bypass - -Run the script directly with execution policy bypass: - -```powershell -powershell.exe -ExecutionPolicy Bypass -File .\Update-Version.ps1 -VersionType minor -``` - -### Option 4: Script Signing (Production Environments) - -For production environments, consider signing your scripts with a code-signing -certificate: - -1. Obtain a code-signing certificate -2. Sign the script using: - - ```powershell - Set-AuthenticodeSignature -FilePath .\Update-Version.ps1 -Certificate (Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert) - ``` - -## Current Project Practice - -This project currently uses the temporary bypass approach (Option 1) during -development: - -```powershell -Set-ExecutionPolicy Bypass -Scope Process -``` - -## Understanding Execution Policies - -PowerShell execution policies are security features that control the conditions -under which PowerShell loads configuration files and runs scripts. - -Common execution policy settings: - -| Policy | Description | -| ------------ | ---------------------------------------------------------- | -| Restricted | Doesn't load configuration files or run scripts | -| AllSigned | Requires all scripts to be signed by a trusted publisher | -| RemoteSigned | Requires scripts downloaded from the internet to be signed | -| Bypass | Nothing is blocked and there are no warnings or prompts | -| Unrestricted | Loads all configuration files and runs all scripts | - -Check your current policy settings with: - -```powershell -Get-ExecutionPolicy -List -``` - -## Security Considerations - -- **RemoteSigned** provides a good balance of security and usability for most - development environments -- **Bypass** should be used cautiously and only for trusted scripts -- Consider the security implications before changing execution policies -- When possible, use the most restrictive policy that still allows you to work - efficiently diff --git a/docs/shell/shell-usage-guidelines.md b/docs/shell/shell-usage-guidelines.md new file mode 100644 index 0000000..1191762 --- /dev/null +++ b/docs/shell/shell-usage-guidelines.md @@ -0,0 +1,243 @@ +# Shell Usage Guidelines + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Overview](#overview) +- [Why Two Shells?](#why-two-shells) +- [Known Issues with PowerShell](#known-issues-with-powershell) + - [Git Hooks in PowerShell](#git-hooks-in-powershell) + - [Project-Specific Examples](#project-specific-examples) +- [Recommended Shell Usage](#recommended-shell-usage) + - [Use PowerShell For](#use-powershell-for) + - [Use Git Bash For](#use-git-bash-for) +- [Script Conversion Guidelines](#script-conversion-guidelines) + - [Converting PowerShell Git Hooks to Bash](#converting-powershell-git-hooks-to-bash) +- [Best Practices](#best-practices) +- [Example: Calling Git Bash from PowerShell](#example-calling-git-bash-from-powershell) +- [Troubleshooting](#troubleshooting) +- [Integrating Git Bash with PowerShell](#integrating-git-bash-with-powershell) + - [Using Invoke-GitBash Helper](#using-invoke-gitbash-helper) + - [Lessons Learned](#lessons-learned) +- [Example: Real-World Usage](#example-real-world-usage) + +## Overview + +This document outlines our recommended approach for using PowerShell and Git Bash in parallel +on Windows development environments. While PowerShell is our primary shell for Windows-specific +tasks, certain Git operations (especially hooks) require Git Bash for reliable execution. + +## Why Two Shells? + +The need to use both shells stems from fundamental differences in how Windows and Unix-like +systems handle certain operations: + +1. **Path Handling**: Windows uses backslashes and drive letters, while Unix-like systems + use forward slashes and mount points. +2. **Script Execution**: Windows relies on file extensions (`.exe`, `.bat`, `.ps1`) to + identify executables, while Unix-like systems use file permissions. +3. **Environment Variables**: Windows and Unix-like systems handle environment variables + differently, affecting script behavior. + +## Known Issues with PowerShell + +### Git Hooks in PowerShell + +We encountered specific issues with Git hooks in PowerShell: + +- Git expects hook files without extensions, but Windows requires them +- PowerShell scripts (`.ps1`) are not natively recognized as executable by Git +- Path handling in hook scripts can be problematic due to different path formats +- Our markdown linting pre-commit hook failed to execute properly in PowerShell + +### Project-Specific Examples + +In our project, we initially implemented Git hooks using PowerShell: + +1. **Markdown Linting Hook** (`scripts/lint-markdown.ps1`) + - Originally implemented as a PowerShell script + - Required complex wrapper scripts to work as a Git hook + - Faced execution and path handling issues + - Should be converted to a bash script + +2. **Hook Installation** (`scripts/install-markdown-lint-hook.ps1`) + - Created multiple files to work around PowerShell limitations: + - A cmd wrapper script (pre-commit) + - A PowerShell script (pre-commit.ps1) + - Complex error-prone setup + - Should be simplified as a bash script + +## Recommended Shell Usage + +### Use PowerShell For + +- Day-to-day development tasks +- Windows system administration +- .NET development and debugging +- Windows-specific automation +- Scripts that interact with Windows services +- Package management (NuGet, etc.) + +### Use Git Bash For + +- Git hooks (especially pre-commit hooks) +- Shell scripts that expect Unix-style commands +- Operations requiring Unix-style path handling +- Cross-platform shell scripts +- When following Git-related tutorials that assume a Unix-like environment + +## Script Conversion Guidelines + +### Converting PowerShell Git Hooks to Bash + +When converting Git hook scripts from PowerShell to Bash: + +1. **File Naming** + - Remove `.ps1` extension for hook scripts + - Use `.sh` extension for supporting scripts + - Example: `lint-markdown.ps1` → `lint-markdown.sh` + +2. **Path Handling** + - Replace Windows paths with Unix-style paths + - Use relative paths when possible + - Use `$(dirname "$0")` for script location + - Example: + + ```powershell + # PowerShell + $scriptPath = Split-Path $MyInvocation.MyCommand.Path -Parent + ``` + + ```bash + # Bash + scriptPath="$(dirname "$0")" + ``` + +3. **Command Conversion** + - Replace PowerShell cmdlets with bash commands: + + ```powershell + # PowerShell + Get-ChildItem -Filter "*.md" -Recurse + ``` + + ```bash + # Bash + find . -name "*.md" + ``` + +4. **Error Handling** + - Replace PowerShell error handling: + + ```powershell + # PowerShell + $ErrorActionPreference = "Stop" + ``` + + ```bash + # Bash + set -e + ``` + +## Best Practices + +1. **Script Organization** + - Keep PowerShell scripts in `.ps1` files + - Keep Bash scripts in `.sh` files + - Use appropriate shebang lines in scripts + - Document which shell is required for each script + +2. **Path Handling** + - Use forward slashes in Git-related paths + - Use environment variables when possible + - Consider using relative paths in Git hooks + +3. **Git Hook Development** + - Write Git hooks assuming Git Bash execution + - Test hooks in Git Bash environment + - Document hook requirements clearly + +## Example: Calling Git Bash from PowerShell + +When needed, you can call Git Bash from PowerShell using: + +```powershell +& 'C:\Program Files\Git\bin\bash.exe' -c "your-command-here" +``` + +## Troubleshooting + +If you encounter issues: + +1. Check which shell is executing the script +2. Verify path formats match the executing shell +3. Ensure scripts have correct line endings (CRLF for Windows, LF for Bash) +4. Check file permissions and execution policies + +## Integrating Git Bash with PowerShell + +### Using Invoke-GitBash Helper + +TODO: This contains detail information about the helper function. We should hold it in the helper or in a specific md +file. + +We've created a PowerShell helper function to properly invoke Git Bash scripts from PowerShell. +This solves common issues with path conversion and script execution: + +```powershell +# First, load the helper function +. ./scripts/Invoke-GitBash.ps1 + +# Run a bash script +Invoke-GitBash "./scripts/your-script.sh" + +# Run with arguments +Invoke-GitBash "./scripts/your-script.sh" "arg1" "arg2" +``` + +The helper provides: + +- Automatic path conversion between Windows and Unix formats +- Proper script execution in Git Bash environment +- Error handling and exit code propagation +- Clear execution feedback + +### Lessons Learned + +Through our experience with Git hooks and shell integration, we discovered: + +1. **Direct PowerShell Execution Issues** + - Running Git Bash commands directly in PowerShell often fails + - Path formats and environment differences cause problems + - Simple `& 'C:\Program Files\Git\bin\bash.exe'` calls can be unreliable + +2. **Path Handling Complexity** + - Windows and Unix paths need careful conversion + - Environment variables may need translation + - Relative paths are more reliable across environments + +3. **Script Execution Context** + - Git Bash scripts need proper environment setup + - Some tools (like Node.js) behave differently in different shells + - Exit codes must be properly propagated + +4. **Best Practices for Integration** + - Use the `Invoke-GitBash` helper for all Git Bash script execution + - Keep Git Bash scripts focused on Git/Unix-style operations + - Document shell requirements in script headers + - Test scripts in both environments + +## Example: Real-World Usage + +Our markdown linting setup demonstrates these principles: + +```powershell +# Install the pre-commit hook +Invoke-GitBash "./scripts/install-markdown-lint-hook.sh" + +# Run linting manually +Invoke-GitBash "./scripts/lint-markdown.sh" +``` + +The actual scripts remain pure bash scripts, but we can reliably execute them from PowerShell +using our helper function. diff --git a/docs/testing/web-server-suppression.md b/docs/testing/web-server-suppression.md new file mode 100644 index 0000000..bf97ebc --- /dev/null +++ b/docs/testing/web-server-suppression.md @@ -0,0 +1,95 @@ +# Web Server Suppression in Test Containers + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [The Issue](#the-issue) +- [Why We Suppressed the Web Server](#why-we-suppressed-the-web-server) +- [How ASP.NET Core Testing Works](#how-aspnet-core-testing-works) +- [Implementation in Docker](#implementation-in-docker) +- [Benefits of This Approach](#benefits-of-this-approach) +- [Alternative Approaches](#alternative-approaches) + +## The Issue + +When running tests in Docker containers, we found that the web server was +automatically starting and causing an error because of a duplicate endpoint. + +## Why We Suppressed the Web Server + +We suppressed the web server in the test container for the following reasons: + +1. **Avoiding Routing Conflicts**: The duplicate health check endpoints were causing routing conflicts +2. **Unnecessary for Tests**: Most tests don't need a real HTTP server +3. **Resource Efficiency**: Running a web server consumes unnecessary resources during testing +4. **Isolation**: Tests should run in isolation without depending on network services + +## How ASP.NET Core Testing Works + +ASP.NET Core provides specific tools for testing web applications without starting a real web server: + +1. **TestServer**: Creates an in-memory server that processes HTTP requests without binding to network ports +2. **WebApplicationFactory**: Configures and sets up TestServer with your application's startup code +3. **HttpClient**: Used to make requests to the TestServer + +```csharp +// Example of ASP.NET Core test using WebApplicationFactory +public class ApiTests +{ + private readonly WebApplicationFactory _factory; + private readonly HttpClient _client; + + public ApiTests() + { + _factory = new WebApplicationFactory(); + _client = _factory.CreateClient(); + } + + [Fact] + public async Task Get_EndpointsReturnSuccessAndCorrectContentType() + { + // Arrange & Act + var response = await _client.GetAsync("/api/data"); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); + } +} +``` + +## Implementation in Docker + +In our Docker test image, we suppressed the web server by: + +1. Setting `ASPNETCORE_URLS=` (empty string) +2. Setting `ASPNETCORE_ENVIRONMENT=Development` +3. Creating a dedicated test stage with its own entrypoint + +```dockerfile +# Test stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS test +WORKDIR /src +COPY --from=build /src . +ENV ASPNETCORE_URLS= +ENV ASPNETCORE_ENVIRONMENT=Development +ENV DOTNET_RUNNING_IN_CONTAINER=true +ENTRYPOINT ["dotnet", "test"] +``` + +## Benefits of This Approach + +1. **Faster Tests**: No overhead of starting a real web server +2. **No Port Conflicts**: Tests can run in parallel without port binding conflicts +3. **More Reliable**: Tests don't depend on network configuration +4. **Cleaner Output**: No web server logs cluttering test output +5. **Consistent Environment**: Tests run the same way locally and in CI + +## Alternative Approaches + +If tests need to interact with a real web server, consider: + +1. **Dynamic Port Assignment**: Configure the server to use any available port +2. **Containerized Dependencies**: Use test containers for external dependencies +3. **Network Isolation**: Use Docker networks to isolate the test environment +4. **Controlled Startup**: Start the web server only when needed for specific tests diff --git a/docs/tools/documentation-guide.md b/docs/tools/documentation-guide.md new file mode 100644 index 0000000..69df380 --- /dev/null +++ b/docs/tools/documentation-guide.md @@ -0,0 +1,120 @@ +# Documentation Guide + +> This document defines the file naming and formatting standards for project documentation to ensure +> consistency and maintainability across all documentation files. + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Document Type Definitions](#document-type-definitions) +- [File Naming Conventions](#file-naming-conventions) + - [Markdown Files](#markdown-files) + - [Strategy Documents](#strategy-documents) + - [Guide Documents](#guide-documents) + - [Specification Documents](#specification-documents) + - [Checklists](#checklists) + - [File Extensions](#file-extensions) +- [Checklist Definition and Purpose](#checklist-definition-and-purpose) +- [Handling Checklist Content](#handling-checklist-content) + - [Dedicated Checklist Files](#dedicated-checklist-files) + - [Embedded Checklists](#embedded-checklists) +- [Benefits of Consistent Naming](#benefits-of-consistent-naming) + +## Document Type Definitions + +- **Strategy Document**: Outlines an approach or plan for implementing a feature or achieving a goal. + Includes rationale, steps, considerations, and timeline. + +- **Guide Document**: Provides instructional content with step-by-step procedures or best practices + for performing specific tasks. + +- **Specification Document**: Defines requirements, behaviors, and technical details for a feature + or system. Describes what should be built rather than how. + +- **Checklist Document**: Tracks task completion with status indicators for verifying processes. + Focuses on verification rather than detailed explanation. + +## File Naming Conventions + +### Markdown Files + +- Use **kebab-case** for all markdown files (lowercase with hyphens between words) + - Example: `shell-usage-guidelines.md` + - Example: `troubleshooting.md` + +- **Exception**: Keep `README.md` in uppercase + - This follows the universal convention that makes entry point documentation instantly recognizable + +### Strategy Documents + +- Name strategy documents with the pattern **"feature-strategy.md"** + - Example: `api-versioning-strategy.md` + - Example: `deployment-strategy.md` +- This makes strategy documents easily identifiable and consistent +- Always use feature-first naming (feature comes before "strategy" in the name) + +### Guide Documents + +- Name guide documents with the pattern **"topic-guide.md"** + - Example: `deployment-guide.md` + - Example: `testing-guide.md` +- Use when providing instructional content or best practices +- Always use topic-first naming (topic comes before "guide" in the name) + +### Specification Documents + +- Name specification documents with the pattern **"feature-spec.md"** + - Example: `api-versioning-spec.md` + - Example: `authentication-spec.md` +- Use for defining requirements and technical details +- Always use feature-first naming (feature comes before "spec" in the name) + +### Checklists + +- Name checklist files with **kebab-case** and the suffix **"-checklist.md"** + - Example: `publishing-checklist.md` + - Example: `deployment-checklist.md` +- This naming convention ensures proper application of checklist maintenance rules + +### File Extensions + +- Use `.md` for all Markdown files + +## Checklist Definition and Purpose + +A checklist is a document that tracks task completion using status indicators. Its primary features: + +- Status markers (āœ…, - [ ], šŸ”„, āš ļø) tracking completion state +- Discrete, actionable tasks organized by category +- Focus on verification rather than detailed instructions + +Checklists help ensure consistent, complete execution of complex processes while reducing errors. + +## Handling Checklist Content + +### Dedicated Checklist Files + +- Create standalone checklist files for significant operational procedures +- Follow the naming convention with "-checklist.md" suffix +- Apply full checklist formatting rules + +### Embedded Checklists + +For documents that contain checklist sections but serve a broader purpose: + +- Do not rename the main document to include "checklist" +- Add the following comment above the checklist section: + + ```markdown + + ``` + +- Apply checklist formatting standards only to that section +- Consider extracting to a dedicated checklist file if the section grows large + +## Benefits of Consistent Naming + +- Improved readability and organization +- URL-friendly (no spaces or special characters) +- Consistent with modern web development practices +- Works reliably across different operating systems and file systems diff --git a/docs/tools/markdown-guidelines.md b/docs/tools/markdown-guidelines.md new file mode 100644 index 0000000..5975636 --- /dev/null +++ b/docs/tools/markdown-guidelines.md @@ -0,0 +1,171 @@ +# Markdown Guidelines and Quality Control + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Tools and Setup](#tools-and-setup) + - [Required Extensions](#required-extensions) + - [Automated Linting](#automated-linting) +- [Automated Processing](#automated-processing) + - [Bulk Fix Command](#bulk-fix-command) + - [GitHub Actions Integration](#github-actions-integration) +- [VS Code Configuration](#vs-code-configuration) + - [Settings](#settings) + - [Extension Recommendations](#extension-recommendations) +- [Measurements and Status](#measurements-and-status) + - [Status Legend](#status-legend) +- [Common Pitfalls and Solutions](#common-pitfalls-and-solutions) + - [Line Length (MD013)](#line-length-md013) + +## Tools and Setup + +### Required Extensions + +- `DavidAnson.vscode-markdownlint`: Core linting functionality +- `yzhang.markdown-all-in-one`: Enhanced markdown support +- `streetsidesoftware.code-spell-checker`: Spell checking + +### Automated Linting + +The project includes automated markdown linting that: + +- Checks and fixes common markdown formatting issues +- Runs automatically as a pre-commit hook +- Can be run manually for immediate feedback + +To set up the pre-commit hook: + +```bash +./scripts/install-markdown-lint-hook.sh +``` + +For manual linting: + +```bash +./scripts/lint-markdown.sh +``` + +## Automated Processing + +### Bulk Fix Command + +```bash +# Fix all markdown files in the repository +find . -name "*.md" -type f -exec markdownlint-cli2 --fix {} \; +``` + +### GitHub Actions Integration + +Create `.github/workflows/markdown-lint.yml`: + +```yaml +name: Markdown Lint + +on: + pull_request: + paths: + - '**.md' + push: + paths: + - '**.md' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: DavidAnson/markdownlint-cli2-action@v15 + with: + globs: "**/*.md" + config: '.markdownlint.json' +``` + +## VS Code Configuration + +### Settings + +The `.vscode/settings.json` includes: + +```json +{ + "markdownlint.config": { + "extends": "${workspaceFolder}/.markdownlint.json" + }, + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", + "editor.formatOnSave": true, + "editor.rulers": [120], + "files.trimTrailingWhitespace": false + } +} +``` + +### Extension Recommendations + +Add to `.vscode/extensions.json`: + +```json +{ + "recommendations": [ + "DavidAnson.vscode-markdownlint", + "yzhang.markdown-all-in-one", + "streetsidesoftware.code-spell-checker" + ] +} +``` + +## Measurements and Status + +| Measurement | Status | Description | +| ------------------- | ---------- | --------------------------------------- | +| Line Length | āœ… Active | Max 120 characters (except code/tables) | +| HTML Elements | āœ… Active | Only `
`, ``, `` allowed | +| Header Duplicates | āœ… Active | Allowed within different sections | +| Header Punctuation | āœ… Active | No punctuation except .,;:! | +| Top Level Header | āœ… Active | Not required | +| Trailing Spaces | āš ļø Mixed | Preserved in MD, trimmed elsewhere | +| Table Formatting | šŸ”„ Planned | Consistent column alignment | +| Code Block Language | šŸ”„ Planned | Require language specification | +| Link Validation | šŸ“ Proposed | Check for broken internal links | +| Image References | šŸ“ Proposed | Verify image file existence | +| Spell Checking | šŸ”„ Planned | US English dictionary | +| Frontmatter | šŸ“ Proposed | Validate required metadata | + +### Status Legend + +- āœ… Active: Currently enforced +- āš ļø Mixed: Partially enforced +- šŸ”„ Planned: Implementation in progress +- šŸ“ Proposed: Under consideration + +## Common Pitfalls and Solutions + +### Line Length (MD013) + +- Keep lines under 120 characters +- Break long lines at natural points: + - After punctuation + - Before conjunctions (and, or, but) + - Between list items +- For links, use reference style: + + ```markdown + Check the [pre-publishing checklist][pre-pub] first. + + [pre-pub]: PREPUBLISHING_CHECKLIST.md + ``` + +- For long code blocks, use line breaks and proper indentation +- Tables can be formatted across multiple lines: + + ```markdown + | Column A + | Column with a very long header + | Column C | + | -------------------------------- | + | -------------------------------- | + | -------------------------------- | + | Data | + | This is a very long cell content | + | Short | + ``` diff --git a/docs/tools/mdc-guidelines.md b/docs/tools/mdc-guidelines.md new file mode 100644 index 0000000..d7609f3 --- /dev/null +++ b/docs/tools/mdc-guidelines.md @@ -0,0 +1,109 @@ +# MDC Files: Guidelines for Creation + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [What are MDC Files?](#what-are-mdc-files) +- [Core Purpose](#core-purpose) +- [Basic Structure](#basic-structure) +- [Rule Types](#rule-types) +- [How Rules Work](#how-rules-work) + - [Stage 1: Injection](#stage-1-injection) + - [Stage 2: Activation](#stage-2-activation) + - [Rule Types and Properties](#rule-types-and-properties) +- [Best Practices](#best-practices) +- [When to Create MDC Files](#when-to-create-mdc-files) +- [References](#references) + +## What are MDC Files? + +MDC (Markdown Documentation Cursor) files are specialized documentation files used in Cursor AI to provide +context-aware coding guidelines and rules. +They help the AI understand project conventions, coding standards, and best practices when assisting you. + +## Core Purpose + +- Provide AI with structured knowledge about your codebase +- Enforce consistent coding standards and practices +- Create project-specific documentation that influences AI suggestions +- Improve code quality through consistent rule application + +## Basic Structure + +```mdc +--- +description: Brief description of this rule set +globs: "*.js", "*.ts", "*.jsx" +alwaysApply: false +--- + +# Main Title + +Content organized in sections... +``` + +## Rule Types + +Cursor supports four rule types, each with a different activation behavior: + +- **Manual**: Only applied when explicitly requested +- **Always**: Applied across all matching files automatically +- **Auto Attached**: Automatically attaches to relevant files based on content +- **Agent Requested**: Applied when the AI determines they're relevant + +## How Rules Work + +Rules in Cursor operate through a two-stage process: + +### Stage 1: Injection + +Rules are injected into the system prompt but aren't yet active. Injection depends on: + +- **`alwaysApply`**: Controls unconditional injection into context + - `true` - Always injected into every prompt + - `false` - Only injected when relevant by other criteria + +- **`globs`**: File pattern matching for context-based injection + - Matches files based on patterns (filenames, extensions) + - If a file matches, the rule is injected into context + +### Stage 2: Activation + +Whether an injected rule takes effect depends on: + +- **`description`**: Determines the scenarios where the rule should be activated + - The AI uses this to decide if the rule is relevant to the current task + +### Rule Types and Properties + +Here's how the frontmatter properties relate to rule types: + +| Rule Type | alwaysApply | Notes | +| --------------- | ----------- | ---------------------------------------------- | +| Manual | `false` | Requires explicit request to activate | +| Always | `true` | Automatically applied to all matching files | +| Auto Attached | `false` | System decides based on file content relevance | +| Agent Requested | `false` | AI decides based on context relevance | + +## Best Practices + +1. **Be concise**: Keep rules clear and to the point +2. **Use proper formatting**: Utilize markdown headings, lists, and code blocks +3. **Target appropriate files**: Set precise glob patterns to apply rules only where needed +4. **Choose the right rule type**: Consider when you want your rules applied +5. **Organize logically**: Group related guidelines under clear headings +6. **Include examples**: Show correct and incorrect usage where helpful +7. **Keep updated**: Review and revise rules as your project evolves + +## When to Create MDC Files + +- For project-specific conventions +- When establishing team coding standards +- To document architectural decisions +- For special handling of framework-specific code +- To ensure consistency across your codebase + +## References + +- [A Deep Dive into Cursor Rules](https://forum.cursor.com/t/a-deep-dive-into-cursor-rules-0-45/60721) - Comprehensive +explanation of how Cursor rules work diff --git a/docs/tools/powershell-execution-policy.md b/docs/tools/powershell-execution-policy.md new file mode 100644 index 0000000..c60855f --- /dev/null +++ b/docs/tools/powershell-execution-policy.md @@ -0,0 +1,35 @@ +# PowerShell Execution Policy Guide + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Purpose](#purpose) +- [Running Update-Version.ps1](#running-update-versionps1) +- [Temporary Bypass (Development)](#temporary-bypass-development) + +## Purpose + +This guide explains the role of PowerShell +Execution Policies in the project. It offers basic +guidance on running essential scripts like +Update-Version.ps1 using a temporary bypass, +suitable for development. + +## Running Update-Version.ps1 + +The `Update-Version.ps1` script updates version +numbers in `Directory.Build.props` per Semantic +Versioning (SemVer). It requires proper execution +policy settings to run. + +## Temporary Bypass (Development) + +For development, this project uses the temporary +bypass approach, allowing unsigned scripts to run +in the current session: + +```powershell +Set-ExecutionPolicy Bypass -Scope Process +# Run from solution root +.\scripts\Update-Version.ps1 -VersionType minor +``` diff --git a/docs/TEMPLATE_USAGE.md b/docs/tools/template-usage.md similarity index 95% rename from docs/TEMPLATE_USAGE.md rename to docs/tools/template-usage.md index 11c3fda..8935a0d 100644 --- a/docs/TEMPLATE_USAGE.md +++ b/docs/tools/template-usage.md @@ -1,6 +1,8 @@ # Using ClarusMens as a Template -This document provides guidance on how to use the ClarusMens API project as a template for creating new .NET API projects. The template includes best practices, reusable patterns, and solutions to common issues that will accelerate your development process. +This document provides guidance on how to use the ClarusMens API project as a template for creating new .NET API +projects. The template includes best practices, reusable patterns, and solutions to common issues that will accelerate +your development process. ## Why Use This Template? @@ -166,6 +168,7 @@ If you encounter compiler errors after renaming: ### Issue: Tests failing after customization Check: + 1. Whether test configuration still matches your updated API 2. If test dependencies are properly registered 3. If any hardcoded values in tests need updating @@ -173,8 +176,10 @@ Check: ## Template Versioning This template is based on: + - .NET 9.0 - ASP.NET Core 9.0 - MSTest 3.x -When .NET or its packages receive major updates, consider updating the template to benefit from new features and patterns. \ No newline at end of file +When .NET or its packages receive major updates, consider updating the template to benefit from new features and +patterns. diff --git a/scripts/Invoke-GitBash.ps1 b/scripts/Invoke-GitBash.ps1 new file mode 100644 index 0000000..ae3a400 --- /dev/null +++ b/scripts/Invoke-GitBash.ps1 @@ -0,0 +1,88 @@ +# Invoke-GitBash.ps1 +# ================= +# +# Purpose: +# Provides functions to properly invoke Git Bash commands from PowerShell +# while handling path conversions and environment setup. +# +# Functions: +# - Convert-ToGitBashPath: Converts Windows paths to Git Bash format +# - Invoke-GitBash: Executes scripts in Git Bash environment +# +# Usage: +# ```powershell +# # Load the module +# . ./scripts/Invoke-GitBash.ps1 +# +# # Convert a path +# $gitBashPath = Convert-ToGitBashPath "C:\Users\you\project\script.sh" +# +# # Run a script +# Invoke-GitBash "./scripts/your-script.sh" +# +# # Run with arguments +# Invoke-GitBash "./scripts/your-script.sh" "arg1" "arg2" +# ``` +# +# Requirements: +# - Git for Windows installed (default location: C:\Program Files\Git) +# - PowerShell 5.1 or later +# +# Notes: +# - Paths are automatically converted to Git Bash format +# - Exit codes are properly propagated +# - Error handling is included +# - Execution feedback is provided + +function Convert-ToGitBashPath { + param ( + [Parameter(Mandatory = $true)] + [string]$WindowsPath + ) + + # Convert to forward slashes + $path = $WindowsPath -replace '\\', '/' + + # Convert drive letter format + if ($path -match '^[A-Za-z]:') { + $driveLetter = $path.Substring(0, 1).ToLower() + $path = "/c$($path.Substring(2))" + } + + return $path +} + +function Invoke-GitBash { + param ( + [Parameter(Mandatory = $true)] + [string]$ScriptPath, + + [Parameter(Mandatory = $false)] + [string[]]$Arguments + ) + + $gitBashExe = 'C:\Program Files\Git\bin\bash.exe' + if (-not (Test-Path $gitBashExe)) { + throw "Git Bash not found at: $gitBashExe" + } + + # Convert the script path to Git Bash format + $gitBashPath = Convert-ToGitBashPath (Resolve-Path $ScriptPath) + + Write-Host "šŸš€ Executing Git Bash script: $gitBashPath" + + # Build the command array + $cmdArgs = @() + if ($Arguments) { + $cmdArgs = @($gitBashPath) + $Arguments + } + else { + $cmdArgs = @($gitBashPath) + } + + # Execute the script + & $gitBashExe $cmdArgs + + # Return the exit code + return $LASTEXITCODE +} \ No newline at end of file diff --git a/Update-Version.ps1 b/scripts/Update-Version.ps1 similarity index 100% rename from Update-Version.ps1 rename to scripts/Update-Version.ps1 diff --git a/scripts/create-azure-service-principal b/scripts/create-azure-service-principal new file mode 100644 index 0000000..422a075 --- /dev/null +++ b/scripts/create-azure-service-principal @@ -0,0 +1,45 @@ +#!/bin/bash + +# This script creates a service principal and assigns it the acrpull role for the +# specified Azure Container Registry. +# copied from https://learn.microsoft.com/en-us/azure/container-registry/container-registry-auth-aci + +# Display usage if no parameters are provided +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + echo "Example: $0 acihellocontreg 12345678-90ab-cdef-1234-567890abcdef" + exit 1 +fi + +# Get parameters +ACR_NAME=$1 +SERVICE_PRINCIPAL_ID=$2 + +# Verify Azure CLI is logged in +if ! az account show >/dev/null 2>&1; then + echo "Error: Not logged into Azure. Please run 'az login' first." + exit 1 +fi + +echo "Getting ACR registry ID..." +# Populate value required for subsequent command args +ACR_REGISTRY_ID=$(az acr show --name $ACR_NAME --query id --output tsv) + +if [ -z "$ACR_REGISTRY_ID" ]; then + echo "Error: Could not find Container Registry '$ACR_NAME'" + exit 1 +fi + +echo "Assigning acrpull role to service principal..." +# Assign the acrpull role to the service principal +az role assignment create \ + --assignee $SERVICE_PRINCIPAL_ID \ + --scope $ACR_REGISTRY_ID \ + --role acrpull + +if [ $? -eq 0 ]; then + echo "Successfully assigned acrpull role to service principal" +else + echo "Error: Failed to assign role. Please check if the service principal ID is correct and you have sufficient permissions." + exit 1 +fi \ No newline at end of file diff --git a/scripts/fix-line-length.ps1 b/scripts/fix-line-length.ps1 new file mode 100644 index 0000000..a411a47 --- /dev/null +++ b/scripts/fix-line-length.ps1 @@ -0,0 +1,91 @@ +# Fix-Line-Length.ps1 +# =================== +# +# PURPOSE: +# Fixes line length issues in markdown files by wrapping lines longer than 120 characters. +# This helps comply with markdownlint rule MD013 (line-length). +# +# USAGE: +# ./scripts/fix-line-length.ps1 # Fixes template-usage.md (default) +# ./scripts/fix-line-length.ps1 -FilePath docs/other-file.md # Fixes a specific file +# ./scripts/fix-line-length.ps1 -FilePath path/to/file.md -MaxLength 80 # Custom length +# +# PARAMETERS: +# -FilePath The path to the markdown file to fix (default: "docs/template-usage.md") +# -MaxLength The maximum allowed line length (default: 120) +# +# NOTES: +# - The script preserves content while splitting long lines at word boundaries +# - Designed to work with markdownlint validation rules +# - Safe to run multiple times on the same file +# - Works well with our project's markdown linting workflow + +[CmdletBinding()] +param ( + [Parameter(Position = 0)] + [string]$FilePath = "docs/template-usage.md", + + [Parameter(Position = 1)] + [int]$MaxLength = 120 +) + +# Verify file exists +if (-not (Test-Path $FilePath)) { + Write-Error "File not found: $FilePath" + exit 1 +} + +# Verify file is markdown +if (-not $FilePath.EndsWith(".md")) { + Write-Warning "The file '$FilePath' doesn't have a .md extension. Continuing anyway..." +} + +Write-Host "šŸ“ Processing '$FilePath' (max length: $MaxLength characters)..." + +# Read and process the file +$content = Get-Content $FilePath -Raw +$lines = $content -split "`n" +$newLines = @() +$changedLines = 0 + +# Process each line +foreach ($line in $lines) { + if ($line.Length -gt $MaxLength) { + # Split long lines at word boundaries + $words = $line -split ' ' + $currentLine = "" + foreach ($word in $words) { + if (($currentLine.Length + $word.Length + 1) -le $MaxLength) { + if ($currentLine -eq "") { + $currentLine = $word + } + else { + $currentLine = "$currentLine $word" + } + } + else { + $newLines += $currentLine + $currentLine = $word + $changedLines++ + } + } + if ($currentLine -ne "") { + $newLines += $currentLine + } + } + else { + $newLines += $line + } +} + +# Save the fixed content +$newContent = $newLines -join "`n" +Set-Content -Path $FilePath -Value $newContent -Encoding UTF8 + +# Report results +if ($changedLines -gt 0) { + Write-Host "āœ… Fixed $changedLines line length issues in '$FilePath'" +} +else { + Write-Host "āœ“ No line length issues found in '$FilePath'" +} \ No newline at end of file diff --git a/scripts/fix-markdown.ps1 b/scripts/fix-markdown.ps1 new file mode 100644 index 0000000..16c45ea --- /dev/null +++ b/scripts/fix-markdown.ps1 @@ -0,0 +1,62 @@ +# Fix-Markdown.ps1 +# ================ +# +# PURPOSE: +# Runs multiple markdown fixing scripts to correct common issues like line length and blank lines. +# +# USAGE: +# ./scripts/fix-markdown.ps1 # Fixes all markdown files +# ./scripts/fix-markdown.ps1 -FilePath docs/file.md # Fixes a specific file +# +# PARAMETERS: +# -FilePath The path to the markdown file to fix (default: processes all .md files) +# -FixTypes Types of fixes to apply ("all", "line-length", "multiple-blanks") + +[CmdletBinding()] +param ( + [Parameter(Position = 0)] + [string]$FilePath, + + [Parameter(Position = 1)] + [ValidateSet("all", "line-length", "multiple-blanks")] + [string[]]$FixTypes = @("all") +) + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + +function Invoke-Fix { + param ( + [string]$FixScript, + [string]$Path + ) + + $scriptPath = Join-Path $scriptDir $FixScript + if (-not (Test-Path $scriptPath)) { + Write-Error "Fix script not found: $scriptPath" + return + } + + Write-Host "šŸ”§ Running $FixScript on '$Path'..." -ForegroundColor Cyan + + if ($Path) { + & $scriptPath -FilePath $Path + } + else { + & $scriptPath + } +} + +# Determine which fixes to apply +$shouldFixLineLength = $FixTypes -contains "all" -or $FixTypes -contains "line-length" +$shouldFixMultipleBlanks = $FixTypes -contains "all" -or $FixTypes -contains "multiple-blanks" + +# Apply fixes +if ($shouldFixMultipleBlanks) { + Invoke-Fix -FixScript "fix-multiple-blanks.ps1" -Path $FilePath +} + +if ($shouldFixLineLength) { + Invoke-Fix -FixScript "fix-line-length.ps1" -Path $FilePath +} + +Write-Host "✨ Markdown fixes completed!" -ForegroundColor Green \ No newline at end of file diff --git a/scripts/fix-multiple-blanks.ps1 b/scripts/fix-multiple-blanks.ps1 new file mode 100644 index 0000000..81349a7 --- /dev/null +++ b/scripts/fix-multiple-blanks.ps1 @@ -0,0 +1,85 @@ +# Fix-Multiple-Blanks.ps1 +# ==================== +# +# PURPOSE: +# Fixes multiple consecutive blank lines in markdown files (MD012). +# Also ensures file ends with a single newline (MD047). +# +# USAGE: +# ./scripts/fix-multiple-blanks.ps1 # Fixes all markdown files +# ./scripts/fix-multiple-blanks.ps1 -FilePath docs/file.md # Fixes a specific file +# ./scripts/fix-multiple-blanks.ps1 -FilePath docs/file.md -Verbose # With detailed output +# +# PARAMETERS: +# -FilePath The path to the markdown file to fix (default: processes all .md files) + +[CmdletBinding()] +param ( + [Parameter(Position = 0)] + [string]$FilePath +) + +function Repair-MultipleBlankLines { + param ( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + Write-Host "šŸ“„ Processing '$Path'..." + + try { + # Read the file content as raw text to preserve all original formatting + $content = Get-Content -Path $Path -Raw + + if (-not $content) { + Write-Warning "File is empty: $Path" + return + } + + # Make a backup of the original file (just in case) + $backupPath = "$Path.bak" + Copy-Item -Path $Path -Destination $backupPath -Force + Write-Verbose "Created backup at $backupPath" + + # Step 1: Fix trailing newlines - ensure exactly one newline at the end + $fixedContent = $content.TrimEnd("`r", "`n") + "`n" + Write-Verbose "Fixed trailing newlines" + + # Step 2: Fix consecutive blank lines (3+ newlines → 2 newlines) + # This preserves a single blank line between content blocks + $pattern = '(\r?\n){3,}' + $replacement = "`$1`$1" + $fixedContent = [regex]::Replace($fixedContent, $pattern, $replacement) + Write-Verbose "Fixed consecutive blank lines" + + # Write the fixed content back to the file + Set-Content -Path $Path -Value $fixedContent -NoNewline + + Write-Host "āœ… Fixed markdown formatting issues in '$Path'" + Write-Verbose " (Backup created at $backupPath)" + } + catch { + Write-Error "Error processing file: $_" + # Try to restore from backup if available + if (Test-Path $backupPath) { + Write-Warning "Restoring from backup after error" + Copy-Item -Path $backupPath -Destination $Path -Force + } + } +} + +# Process all markdown files or a specific file +if ($FilePath) { + if (-not (Test-Path $FilePath)) { + Write-Error "File not found: $FilePath" + exit 1 + } + + Repair-MultipleBlankLines -Path $FilePath +} +else { + $mdFiles = Get-ChildItem -Path . -Filter "*.md" -Recurse + foreach ($file in $mdFiles) { + Repair-MultipleBlankLines -Path $file.FullName + } +} \ No newline at end of file diff --git a/scripts/install-markdown-lint-hook.sh b/scripts/install-markdown-lint-hook.sh new file mode 100644 index 0000000..458a440 --- /dev/null +++ b/scripts/install-markdown-lint-hook.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Install Markdown Lint Hook +# ========================= +# +# Purpose: +# Installs a markdown linting pre-commit hook for Git. +# This is a simpler bash version that replaces a PowerShell implementation. +# +# Usage: +# ./scripts/install-markdown-lint-hook.sh + +set -e # Stop on any error + +HOOK_DIR=".git/hooks" +SCRIPTS_DIR="$(dirname "$0")" +HOOK_SCRIPT="$SCRIPTS_DIR/lint-markdown.sh" +HOOK_TARGET="$HOOK_DIR/pre-commit" + +# Ensure hooks directory exists +mkdir -p "$HOOK_DIR" + +# Remove existing hook if present +if [ -f "$HOOK_TARGET" ]; then + echo "šŸ—‘ļø Removing existing pre-commit hook..." + rm "$HOOK_TARGET" +fi + +# Create the pre-commit hook +echo "#!/bin/bash +exec '$HOOK_SCRIPT' \"\$@\"" > "$HOOK_TARGET" + +# Make both scripts executable +chmod +x "$HOOK_TARGET" +chmod +x "$HOOK_SCRIPT" + +# Verify installation +if [ -x "$HOOK_TARGET" ] && [ -x "$HOOK_SCRIPT" ]; then + echo "āœ… Markdown lint hook installed successfully!" + echo " Pre-commit hook location: $HOOK_TARGET" + echo " Hook script location: $HOOK_SCRIPT" +else + echo "Error: Failed to install markdown lint hook." >&2 + exit 1 +fi + +echo -e "\nšŸ“˜ Usage:" +echo "The markdown lint hook will automatically run when you commit changes." +echo "To run the markdown linter manually:" +echo " ./scripts/lint-markdown.sh" \ No newline at end of file diff --git a/scripts/lint-markdown.sh b/scripts/lint-markdown.sh new file mode 100644 index 0000000..a833da2 --- /dev/null +++ b/scripts/lint-markdown.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# Markdown Linting Pre-commit Hook +# ================================ +# +# Purpose: +# This script automatically lints and fixes markdown files that are staged for commit. +# It ensures consistent markdown formatting across the project by applying rules defined +# in .markdownlint.json before code is committed. +# +# Usage: +# 1. Manual run: +# ./scripts/lint-markdown.sh +# +# 2. As Git pre-commit hook: +# Installed automatically by install-markdown-lint-hook.sh +# +# Requirements: +# - Node.js and npm installed +# - Git Bash +# - markdownlint-cli2 + +set -e # Stop on any error + +# Function to convert Windows path to Unix path +win_to_unix_path() { + echo "$1" | sed 's/\\/\//g' | sed 's/^\([A-Za-z]\):/\/\1/' +} + +# Function to find Node.js executable +find_node() { + # Try standard locations + if command -v node &> /dev/null; then + command -v node + return + fi + + # Try Windows npm location + local npm_node="/c/Users/$USER/AppData/Roaming/npm/node.exe" + if [ -f "$npm_node" ]; then + echo "$npm_node" + return + fi + + # Try program files locations + local prog_files_node="/c/Program Files/nodejs/node.exe" + if [ -f "$prog_files_node" ]; then + echo "$prog_files_node" + return + fi + + echo "Error: Could not find Node.js installation" >&2 + exit 1 +} + +echo "šŸ” Checking for Node.js..." +NODE_EXE=$(find_node) +echo "Using Node.js at: $NODE_EXE" + +echo "šŸ” Checking for npm..." +if ! command -v npm &> /dev/null; then + echo "Error: npm is not installed or not in PATH" >&2 + exit 1 +fi + +echo "šŸ” Checking for markdownlint-cli2..." +if ! npm list -g markdownlint-cli2 &> /dev/null; then + echo "šŸ“¦ Installing markdownlint-cli2 globally..." + npm install -g markdownlint-cli2 + + if [ $? -ne 0 ]; then + echo "Error: Failed to install markdownlint-cli2. Please install it manually: npm install -g markdownlint-cli2" >&2 + exit 1 + fi +fi + +echo "šŸ”Ž Finding staged markdown files..." + +# Get all staged markdown files +files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.md$' || true) + +if [ -z "$files" ]; then + echo "No markdown files to check." + exit 0 +fi + +# Run markdownlint on staged files +echo "$files" | while read -r file; do + if [ -f "$file" ]; then + echo "šŸ“ Checking $file..." + npx markdownlint-cli2 --fix "$file" + git add "$file" # Re-stage the file if it was modified + fi +done \ No newline at end of file diff --git a/tests/PowerShell.Tests/Update-Version.Tests.ps1 b/tests/PowerShell.Tests/Update-Version.Tests.ps1 index 4cbb59b..34261e6 100644 --- a/tests/PowerShell.Tests/Update-Version.Tests.ps1 +++ b/tests/PowerShell.Tests/Update-Version.Tests.ps1 @@ -3,7 +3,7 @@ BeforeAll { $ProjectRoot = (Split-Path -Parent (Split-Path -Parent $PSScriptRoot)) # Import the script to test - $ScriptPath = Join-Path $ProjectRoot "Update-Version.ps1" + $ScriptPath = Join-Path $ProjectRoot "scripts/Update-Version.ps1" . $ScriptPath # Dot-source the script to import its functions # Create a temporary directory for test files diff --git a/tests/README.md b/tests/README.md index ed6cc74..45d3ce7 100644 --- a/tests/README.md +++ b/tests/README.md @@ -72,11 +72,12 @@ dotnet test -c Testing -v detailed When running tests, you may see this warning: -``` +```text No test is available in [...]\ClarusMensAPI.IntegrationTests.dll ``` This is **expected behavior** because: + - The IntegrationTests project is set up as a placeholder for future integration tests - It currently has no test classes or methods implemented - The warning can be safely ignored @@ -158,7 +159,7 @@ Our tests are integrated into our CI/CD pipeline as follows: 1. **Test Discovery**: The pipeline automatically discovers all tests using MSTest's discovery mechanism 2. **Parallel Execution**: Tests are executed in parallel, with unit tests running first, followed by functional tests -3. **Test Reports**: +3. **Test Reports**: - Results are published to Azure DevOps/GitHub Actions dashboard - JUnit-compatible test result files are generated - Code coverage reports in Cobertura format are available @@ -176,3 +177,27 @@ To reproduce CI test execution locally: # Same configuration used in CI dotnet test -c Testing --blame-hang-timeout 5m --logger "trx;LogFileName=test-results.trx" ``` + +## Docker-Based Testing + +This project supports building Docker images with or without tests: + +### Including Tests in Docker Image + +```powershell +docker build -t clarusmens-api-test --build-arg INCLUDE_TESTS=true . +``` + +### When to Use Docker for Testing + +- Container-specific functionality testing +- CI/CD pipelines in containerized environments +- Testing in environments identical to production +- Functional tests that require specific container configuration + +### When to Use Direct Testing (Non-Docker) + +- Local development for faster test cycles +- Normal CI/CD workflows (our GitHub Actions workflow) +- Code coverage analysis +- Running individual test categories or projects