From 75e91e2b351a0eef9aeb6d1f61c87c3e66d1772d Mon Sep 17 00:00:00 2001 From: PM Palles Date: Mon, 8 Dec 2025 18:41:25 -0500 Subject: [PATCH 1/5] feat: first pass by claude sonnet 4.5 --- .commitlintrc.json | 46 +++++++ .dockerignore | 65 ++++++++++ .github/workflows/ci.yml | 75 +++++++++-- CHANGELOG.md | 53 ++++++++ COMMIT_CONVENTION.md | 146 +++++++++++++++++++++ Directory.Build.props | 5 + LunarDoggo.Tests/LunarDoggo.Tests.csproj | 12 +- README.md | 153 +++++++++++++++++++++-- docker-compose.yml | 23 ++++ lefthook.yml | 18 +++ scripts/coverage.ps1 | 15 +++ scripts/format.ps1 | 1 + scripts/test.ps1 | 3 +- scripts/typecheck.ps1 | 1 + 14 files changed, 595 insertions(+), 21 deletions(-) create mode 100644 .commitlintrc.json create mode 100644 .dockerignore create mode 100644 CHANGELOG.md create mode 100644 COMMIT_CONVENTION.md create mode 100644 docker-compose.yml create mode 100644 lefthook.yml create mode 100644 scripts/coverage.ps1 create mode 100644 scripts/format.ps1 create mode 100644 scripts/typecheck.ps1 diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 0000000..00a6d75 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,46 @@ +{ + "extends": [ + "@commitlint/config-conventional" + ], + "rules": { + "type-enum": [ + 2, + "always", + [ + "feat", + "fix", + "docs", + "style", + "refactor", + "test", + "chore", + "ci", + "perf", + "revert" + ] + ], + "type-case": [ + 2, + "always", + "lower-case" + ], + "type-empty": [ + 2, + "never" + ], + "subject-empty": [ + 2, + "never" + ], + "subject-case": [ + 2, + "always", + "lower-case" + ], + "header-max-length": [ + 2, + "always", + 100 + ] + } +} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a4c851c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,65 @@ +# Build artifacts +bin/ +obj/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio +.vs/ +.vscode/ +*.user +*.suo +*.userosscache +*.sln.docstates + +# Test Results +TestResults/ +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +*.trx +*.coverage +*.coveragexml + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# NuGet +*.nupkg +*.snupkg +packages/ +*.nuget.props +*.nuget.targets + +# Git +.git/ +.gitignore +.gitattributes + +# Documentation +*.md +docs/ + +# CI/CD +.github/ +.dockerignore + +# Scripts +scripts/ + +# Logs +*.log +[Ll]og/ +[Ll]ogs/ + +# OS files +.DS_Store +Thumbs.db + +# IDE +*.swp +*.swo +*~ + +# Temporary files +*.tmp +*.temp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab7c0aa..392a362 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,19 +7,76 @@ on: branches: [ main ] jobs: - build: - runs-on: ubuntu-latest + build-and-test: + name: Build, Test & Quality Gates + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: - - uses: actions/checkout@v3 + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for commit validation + - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 7.0.x + - name: Restore dependencies run: dotnet restore + - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal --collect:"XPlat Code Coverage" - - name: Lint - run: dotnet format --verify-no-changes + run: dotnet build --no-restore --configuration Release + + - name: Type Check (Build with warnings as errors) + run: dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true + + - name: Lint (Format verification) + run: dotnet format --verify-no-changes --no-restore + + - name: Run Tests with Coverage + run: dotnet test --no-build --configuration Release --collect:"XPlat Code Coverage" --results-directory ./TestResults /p:Threshold=80 /p:ThresholdType=line,branch + + - name: Upload Coverage Report + if: matrix.os == 'ubuntu-latest' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: ./TestResults/**/coverage.cobertura.xml + retention-days: 30 + + - name: Vulnerability Scan + run: dotnet list package --vulnerable --include-transitive + continue-on-error: false + + - name: Verify Docker Build + if: matrix.os == 'ubuntu-latest' + run: docker build -t csharp-beginner-projects:ci . + + commit-lint: + name: Validate Commit Messages + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install commitlint + run: | + npm install -g @commitlint/cli @commitlint/config-conventional + + - name: Validate commit messages + run: | + npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6c2577d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Repository standards compliance implementation +- `.dockerignore` file for optimized Docker builds +- `docker-compose.yml` for local development environment +- Pre-commit hooks using Lefthook +- Commit message linting with Conventional Commits +- Code coverage threshold enforcement (80%) +- Enhanced CI pipeline with quality gates +- Vulnerability scanning in CI +- Comprehensive development scripts (`format.ps1`, `typecheck.ps1`, `coverage.ps1`) +- Documentation: `COMMIT_CONVENTION.md` and `CHANGELOG.md` + +### Changed +- Enhanced `Directory.Build.props` with coverage settings and stricter analysis +- Updated test project with latest coverlet packages and threshold configuration +- Enhanced `scripts/test.ps1` to include coverage collection +- Improved README with comprehensive development workflow documentation + +## [1.0.0] - Initial Release + +### Added +- Example applications: + - Console input/output + - Console input validation + - File system access + - Simple Quiz Game + - WPF TicTacToe (Windows only) +- Data structures: + - LinkedList, ArrayList, Queue, Stack + - Graphs (directed and undirected) +- Algorithms: + - Sorting: InsertionSort + - Pathfinding: BFS, DFS +- Basic CI pipeline with build, test, and lint +- Unit test infrastructure with xUnit +- Code formatting with dotnet-format +- `.editorconfig` for consistent code style +- `Directory.Build.props` for shared project properties +- `global.json` for .NET SDK version specification +- Dockerfile for containerization +- Repository governance files: LICENSE, CODE_OF_CONDUCT.md, CONTRIBUTING.md + +[Unreleased]: https://github.com/lunardoggo/CSharpBeginnerProjects/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/lunardoggo/CSharpBeginnerProjects/releases/tag/v1.0.0 diff --git a/COMMIT_CONVENTION.md b/COMMIT_CONVENTION.md new file mode 100644 index 0000000..3faec0c --- /dev/null +++ b/COMMIT_CONVENTION.md @@ -0,0 +1,146 @@ +# Conventional Commits Guide + +This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages. This ensures a consistent commit history and enables automated changelog generation and semantic versioning. + +## Commit Message Format + +Each commit message must follow this format: + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +### Type + +The type must be one of the following: + +- **feat**: A new feature +- **fix**: A bug fix +- **docs**: Documentation only changes +- **style**: Changes that do not affect the meaning of the code (white-space, formatting, etc) +- **refactor**: A code change that neither fixes a bug nor adds a feature +- **test**: Adding missing tests or correcting existing tests +- **chore**: Changes to the build process or auxiliary tools and libraries +- **ci**: Changes to CI configuration files and scripts +- **perf**: A code change that improves performance +- **revert**: Reverts a previous commit + +### Scope (Optional) + +The scope provides additional contextual information and is contained within parentheses: + +- `feat(algorithms)`: A new feature in the algorithms module +- `fix(quiz-game)`: A bug fix in the quiz game +- `docs(readme)`: Documentation update to README + +### Description + +The description is a short summary of the code changes: + +- Use the imperative, present tense: "change" not "changed" nor "changes" +- Don't capitalize the first letter +- No period (.) at the end +- Keep it under 100 characters + +## Examples + +### Good Commit Messages + +``` +feat: add binary search algorithm implementation +``` + +``` +fix(quiz-game): resolve null reference exception on startup +``` + +``` +docs: update contributing guidelines with commit convention +``` + +``` +refactor(datastructures): simplify linked list node traversal +``` + +``` +test: add unit tests for insertion sort algorithm +``` + +``` +ci: add coverage threshold enforcement to pipeline +``` + +### Bad Commit Messages + +``` +❌ Added new feature +❌ Fixed bug +❌ Update README +❌ WIP +❌ asdf +``` + +## Breaking Changes + +Breaking changes should be indicated by a `!` after the type/scope and explained in the footer: + +``` +feat!: remove deprecated sorting methods + +BREAKING CHANGE: The legacy QuickSort method has been removed. Use the new QuickSort implementation instead. +``` + +## Setting Up Commit Linting Locally + +### Prerequisites + +1. Install [Node.js](https://nodejs.org/) (includes npm) +2. Install [Lefthook](https://github.com/evilmartians/lefthook): + ```powershell + npm install -g @evilmartians/lefthook + ``` + Or using Go: + ```powershell + go install github.com/evilmartians/lefthook@latest + ``` + +### Installation + +1. Install commitlint globally: + ```powershell + npm install -g @commitlint/cli @commitlint/config-conventional + ``` + +2. Install Lefthook hooks in the repository: + ```powershell + lefthook install + ``` + +### Usage + +Once installed, Lefthook will automatically: + +1. **Before commit**: Run code formatting checks on staged files +2. **On commit message**: Validate that your commit message follows the Conventional Commits format + +If your commit message doesn't follow the format, the commit will be rejected with an error message explaining what's wrong. + +## Bypassing Hooks (Not Recommended) + +In rare cases where you need to bypass the hooks, you can use: + +```powershell +git commit --no-verify -m "your message" +``` + +**Warning**: This should only be used in exceptional circumstances, as it bypasses all quality checks. + +## Additional Resources + +- [Conventional Commits Specification](https://www.conventionalcommits.org/) +- [Commitlint Documentation](https://commitlint.js.org/) +- [Lefthook Documentation](https://github.com/evilmartians/lefthook) diff --git a/Directory.Build.props b/Directory.Build.props index 2e58eb9..a41e7ea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,11 @@ true true latest-recommended + true + + + cobertura,json + ./TestResults/ diff --git a/LunarDoggo.Tests/LunarDoggo.Tests.csproj b/LunarDoggo.Tests/LunarDoggo.Tests.csproj index 5d7b27f..731194b 100644 --- a/LunarDoggo.Tests/LunarDoggo.Tests.csproj +++ b/LunarDoggo.Tests/LunarDoggo.Tests.csproj @@ -14,12 +14,22 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + ../TestResults/ + 80 + line,branch + + diff --git a/README.md b/README.md index 24a1ee3..c88ac3c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,161 @@ # CSharpBeginnerProjects +![Build Status](https://github.com/lunardoggo/CSharpBeginnerProjects/workflows/CI/badge.svg) +![License](https://img.shields.io/badge/license-Unlicense-blue.svg) +![.NET Version](https://img.shields.io/badge/.NET-7.0-purple.svg) + In this repository you'll find some basic example projects showcasing some important features of C#, as well as some basic algorithms and datastructures that are a must know in the field of computer science. The project is provided _without_ any warranty under the Unilicense, so that the code can be used in any private or commercial project with or without modifications. +## Table of Contents + +- [Getting Started](#getting-started) +- [Development Workflow](#development-workflow) +- [Code Quality Standards](#code-quality-standards) +- [CI/CD Pipeline](#cicd-pipeline) +- [Example Applications](#example-applications) +- [Data Structures](#datastructures) +- [Algorithms](#algorithms) +- [Contributing](#contributing) +- [License](#license) + ## Getting Started ### Prerequisites - [.NET 7.0 SDK](https://dotnet.microsoft.com/download/dotnet/7.0) - [Docker](https://www.docker.com/) (Optional, for containerization) +- [Node.js](https://nodejs.org/) (Optional, for commit linting) +- [Lefthook](https://github.com/evilmartians/lefthook) (Optional, for pre-commit hooks) + +### Quick Start + +1. **Clone the repository**: + ```bash + git clone https://github.com/lunardoggo/CSharpBeginnerProjects.git + cd CSharpBeginnerProjects + ``` + +2. **Restore dependencies**: + ```powershell + dotnet restore + ``` + +3. **Build the solution**: + ```powershell + dotnet build + ``` + +4. **Run tests**: + ```powershell + ./scripts/test.ps1 + ``` + +### Docker Setup + +Run the Quiz Game application using Docker Compose: + +```powershell +docker-compose up +``` + +Or build and run manually: + +```powershell +docker build -t csharp-beginner-projects . +docker run -it csharp-beginner-projects +``` + +## Development Workflow + +### Available Scripts + +All scripts are located in the `scripts/` directory: + +- **Build**: `dotnet build` +- **Test**: `./scripts/test.ps1` - Run all tests with coverage collection +- **Coverage**: `./scripts/coverage.ps1` - Generate HTML coverage report +- **Lint**: `./scripts/lint.ps1` - Verify code formatting +- **Format**: `./scripts/format.ps1` - Auto-format code +- **Type Check**: `./scripts/typecheck.ps1` - Build with warnings as errors + +### Pre-Commit Hooks + +This project uses [Lefthook](https://github.com/evilmartians/lefthook) for pre-commit hooks. To set up: + +1. **Install Lefthook**: + ```powershell + npm install -g @evilmartians/lefthook + # or using Go + go install github.com/evilmartians/lefthook@latest + ``` + +2. **Install hooks**: + ```powershell + lefthook install + ``` + +3. **Install commitlint** (for commit message validation): + ```powershell + npm install -g @commitlint/cli @commitlint/config-conventional + ``` + +Once installed, hooks will automatically: +- Run code formatting checks before commits +- Validate commit messages follow [Conventional Commits](COMMIT_CONVENTION.md) + +## Code Quality Standards + +### Linting & Formatting + +- Code style is enforced via `.editorconfig` +- Use `dotnet format` for automatic formatting +- CI will fail if code is not properly formatted + +### Type Checking + +- Nullable reference types are enabled +- Warnings are treated as errors in CI +- Latest C# language version is used + +### Code Coverage + +- **Minimum coverage**: 80% (line and branch) +- Coverage is collected using Coverlet +- CI enforces coverage thresholds +- View coverage reports: `./scripts/coverage.ps1` + +### Commit Guidelines + +This project follows [Conventional Commits](https://www.conventionalcommits.org/). See [COMMIT_CONVENTION.md](COMMIT_CONVENTION.md) for details. + +**Format**: `(): ` + +**Examples**: +- `feat: add binary search algorithm` +- `fix(quiz-game): resolve null reference exception` +- `docs: update README with coverage requirements` + +### Dependency Management + +- Package lock files are enabled (`packages.lock.json`) +- Dependencies are scanned for vulnerabilities in CI +- Use `dotnet list package --vulnerable` to check locally + +## CI/CD Pipeline -### Building and Testing +The CI pipeline runs on every push and pull request, executing: -We have provided helper scripts to make your life easier: +1. **Build** - Compile the solution on Ubuntu and Windows +2. **Type Check** - Build with warnings as errors +3. **Lint** - Verify code formatting +4. **Test** - Run all tests with 80% coverage requirement +5. **Vulnerability Scan** - Check for vulnerable dependencies +6. **Docker Build** - Verify containerization (Ubuntu only) +7. **Commit Validation** - Ensure commit messages follow conventions (PRs only) -- **Run Tests**: - ```powershell - ./scripts/test.ps1 - ``` -- **Lint/Format Code**: - ```powershell - ./scripts/lint.ps1 - ``` +All checks must pass before code can be merged. ## Example applications * [Console input output](https://github.com/lunardoggo/CSharpBeginnerProjects/tree/main/Projects/LunarDoggo.Beginners.ConsoleIO) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0de2484 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3.8' + +services: + quiz-game: + build: + context: . + dockerfile: Dockerfile + image: csharp-beginner-projects:latest + container_name: quiz-game + environment: + - DOTNET_ENVIRONMENT=Development + volumes: + # Mount source code for development + - ./Projects/LunarDoggo.QuizGame:/src/Projects/LunarDoggo.QuizGame:ro + stdin_open: true + tty: true + restart: unless-stopped + healthcheck: + test: ["CMD", "dotnet", "--version"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..232087e --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,18 @@ +pre-commit: + parallel: true + commands: + format: + glob: "*.cs" + run: dotnet format --verify-no-changes --include {staged_files} + stage_fixed: true + +commit-msg: + commands: + commitlint: + run: | + if command -v npx &> /dev/null; then + npx --no-install commitlint --edit {1} + else + echo "Warning: commitlint not available. Install Node.js and run: npm install -g @commitlint/cli @commitlint/config-conventional" + echo "Skipping commit message validation..." + fi diff --git a/scripts/coverage.ps1 b/scripts/coverage.ps1 new file mode 100644 index 0000000..3a9be2c --- /dev/null +++ b/scripts/coverage.ps1 @@ -0,0 +1,15 @@ +# Run tests with coverage collection +dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults + +# Check if reportgenerator is installed +if (-not (Get-Command reportgenerator -ErrorAction SilentlyContinue)) { + Write-Host "Installing dotnet-reportgenerator-globaltool..." + dotnet tool install -g dotnet-reportgenerator-globaltool +} + +# Generate HTML coverage report +Write-Host "Generating coverage report..." +reportgenerator -reports:"./TestResults/**/coverage.cobertura.xml" -targetdir:"./TestResults/CoverageReport" -reporttypes:Html + +Write-Host "" +Write-Host "Coverage report generated at: ./TestResults/CoverageReport/index.html" diff --git a/scripts/format.ps1 b/scripts/format.ps1 new file mode 100644 index 0000000..e07cac8 --- /dev/null +++ b/scripts/format.ps1 @@ -0,0 +1 @@ +dotnet format diff --git a/scripts/test.ps1 b/scripts/test.ps1 index 04162e9..0bd8088 100644 --- a/scripts/test.ps1 +++ b/scripts/test.ps1 @@ -1 +1,2 @@ -dotnet test +dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults /p:Threshold=80 /p:ThresholdType=line,branch + diff --git a/scripts/typecheck.ps1 b/scripts/typecheck.ps1 new file mode 100644 index 0000000..2494206 --- /dev/null +++ b/scripts/typecheck.ps1 @@ -0,0 +1 @@ +dotnet build --no-incremental /p:TreatWarningsAsErrors=true From 18e3beb3a8f7f628bb9a1ef4d18ca33d7a44f83d Mon Sep 17 00:00:00 2001 From: PM Palles Date: Mon, 8 Dec 2025 21:14:35 -0500 Subject: [PATCH 2/5] fix: code analyzer issues --- .github/workflows/ci.yml | 4 + CODE_ANALYZER_FIXES.md | 150 ++++++++++++++++++ DOCKER_TESTING.md | 46 ++++++ Datastructures/Collections/Queue.cs | 6 +- Datastructures/Collections/Stack.cs | 2 + Datastructures/Graphs/Edge.cs | 2 +- Datastructures/Graphs/IGraph.cs | 6 +- .../Graphs/UndirectedUnweightedGraph.cs | 18 +-- .../LunarDoggo.Beginners.ConsoleIO/Program.cs | 2 +- .../Program.cs | 2 +- Projects/LunarDoggo.FileSystemTree/Program.cs | 4 +- .../IO/FileQuizQuestionSerializer.cs | 12 +- .../Visuals/ConsoleVisualizer.cs | 4 +- 13 files changed, 231 insertions(+), 27 deletions(-) create mode 100644 CODE_ANALYZER_FIXES.md create mode 100644 DOCKER_TESTING.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 392a362..db29ac7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,10 @@ jobs: - name: Restore dependencies run: dotnet restore + - name: Remove TicTacToe from solution (Linux only) + if: matrix.os == 'ubuntu-latest' + run: dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj + - name: Build run: dotnet build --no-restore --configuration Release diff --git a/CODE_ANALYZER_FIXES.md b/CODE_ANALYZER_FIXES.md new file mode 100644 index 0000000..b79032b --- /dev/null +++ b/CODE_ANALYZER_FIXES.md @@ -0,0 +1,150 @@ +# Code Analyzer Fixes Summary + +## Fixed Errors (18 total) + +### CA1852: Type should be sealed (3 fixes) +**Fixed in:** +- `Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs` - Sealed Program class +- `Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs` - Sealed Program class +- `Projects/LunarDoggo.FileSystemTree/Program.cs` - Sealed Program class + +**Reason:** These Program classes have no subtypes and are not externally visible, so sealing them improves performance. + +--- + +### CA1822: Member can be marked as static (4 fixes) +**Fixed in:** +- `Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs`: + - `SetGuids()` - Marked as static + - `DeserializeJson()` - Marked as static + - `GetFileContent()` - Marked as static +- `Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs`: + - `DrawQuizQuestionAnswer()` - Marked as static + +**Reason:** These methods don't access instance data, so marking them static improves clarity and performance. + +**Additional fix:** Updated method calls to use class name instead of `this` for static methods. + +--- + +### CA1827: Use Any() instead of Count() (1 fix) +**Fixed in:** +- `Projects/LunarDoggo.FileSystemTree/Program.cs` - Changed `Count() > 0` to `Any()` + +**Reason:** `Any()` is more performant than `Count()` when only checking for existence. + +--- + +### CA1805: Remove explicit initialization to default value (3 fixes) +**Fixed in:** +- `Datastructures/Collections/Queue.cs` - Removed `= 0` from `headIndex` and `tailIndex` +- `Datastructures/Graphs/UndirectedUnweightedGraph.cs` - Removed `= 0` from `lastId` + +**Reason:** Fields are automatically initialized to their default values, so explicit initialization is redundant. + +--- + +### CA2208: ArgumentNullException paramName should match parameter (1 fix) +**Fixed in:** +- `Datastructures/Graphs/Edge.cs` - Changed from message-only to proper paramName + message + +**Before:** +```csharp +throw new ArgumentNullException("None of the vertices of an edge can be null!"); +``` + +**After:** +```csharp +throw new ArgumentNullException(from is null ? nameof(from) : nameof(to), "None of the vertices of an edge can be null!"); +``` + +**Reason:** ArgumentNullException should specify which parameter is null for better debugging. + +--- + +### CA1716: Rename parameter to avoid reserved keyword (3 fixes) +**Fixed in:** +- `Datastructures/Graphs/IGraph.cs` - Renamed `to` parameter to `toVertex` in: + - `IGraph.RemoveEdge()` + - `IUnweightedGraph.AddEdge()` + - `IWeightedGraph.AddEdge()` +- `Datastructures/Graphs/UndirectedUnweightedGraph.cs` - Updated implementation to match interface + +**Reason:** Using reserved keywords as parameter names makes it harder for consumers in other languages to use the API. + +--- + +### CA1711: Identifiers should not have incorrect suffix (2 suppressions) +**Suppressed in:** +- `Datastructures/Collections/Queue.cs` - Added `#pragma warning disable CA1711` +- `Datastructures/Collections/Stack.cs` - Added `#pragma warning disable CA1711` + +**Reason:** These are educational implementations of the Queue and Stack data structures. The naming is intentional and correct for teaching purposes. Suppression is appropriate here. + +--- + +### MSB3644: .NET Framework 4.5.2 not available on Linux (1 fix) +**Fixed in:** +- `.github/workflows/ci.yml` - Added step to remove TicTacToe project from solution on Linux + +**Solution:** The TicTacToe project targets .NET Framework 4.5.2 which is Windows-only. The CI pipeline now excludes it on Linux runners using `dotnet sln remove`. + +--- + +## Testing Locally with Docker + +Created `DOCKER_TESTING.md` with instructions for testing locally before pushing to CI: + +### Quick Test +```powershell +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c "dotnet restore && dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true && dotnet test --no-build --configuration Release" +``` + +### Interactive Testing +```powershell +docker run --rm -it -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash +``` + +Then inside the container: +```bash +dotnet restore +dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true +dotnet format --verify-no-changes +dotnet test --no-build --configuration Release +dotnet list package --vulnerable --include-transitive +``` + +--- + +## Files Modified + +1. `Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs` +2. `Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs` +3. `Projects/LunarDoggo.FileSystemTree/Program.cs` +4. `Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs` +5. `Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs` +6. `Datastructures/Collections/Queue.cs` +7. `Datastructures/Collections/Stack.cs` +8. `Datastructures/Graphs/Edge.cs` +9. `Datastructures/Graphs/IGraph.cs` +10. `Datastructures/Graphs/UndirectedUnweightedGraph.cs` +11. `.github/workflows/ci.yml` + +## Files Created + +1. `DOCKER_TESTING.md` - Local Docker testing guide + +--- + +## Verification + +All code analyzer errors should now be resolved. The build should pass on both Ubuntu and Windows CI runners. + +To verify locally: +```powershell +# On Windows +dotnet build --configuration Release /p:TreatWarningsAsErrors=true + +# On Linux/Docker (see DOCKER_TESTING.md) +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 dotnet build --configuration Release /p:TreatWarningsAsErrors=true +``` diff --git a/DOCKER_TESTING.md b/DOCKER_TESTING.md new file mode 100644 index 0000000..14fe051 --- /dev/null +++ b/DOCKER_TESTING.md @@ -0,0 +1,46 @@ +# Test locally with Docker before pushing to CI + +## Prerequisites +- Docker installed on your system + +## Build and Test with Docker + +### Option 1: Quick Test (Recommended) +Run this command to build and test in a container similar to CI: + +```powershell +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c "dotnet restore && dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true && dotnet test --no-build --configuration Release" +``` + +### Option 2: Interactive Testing +For more control, start an interactive container: + +```powershell +# Start container +docker run --rm -it -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash + +# Inside the container, run: +dotnet restore +dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true +dotnet format --verify-no-changes +dotnet test --no-build --configuration Release +dotnet list package --vulnerable --include-transitive +``` + +### Option 3: Test with Docker Compose +Use the provided docker-compose.yml: + +```powershell +docker-compose build +docker-compose run quiz-game +``` + +## Note on TicTacToe Project +The `LunarDoggo.TicTacToe` project targets .NET Framework 4.5.2 which is Windows-only. When testing on Linux (including Docker), this project will fail to build. The CI pipeline automatically excludes it on Linux runners. + +To test locally on Linux/Docker while excluding TicTacToe: +```bash +dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj +dotnet build --configuration Release /p:TreatWarningsAsErrors=true +dotnet sln add Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj +``` diff --git a/Datastructures/Collections/Queue.cs b/Datastructures/Collections/Queue.cs index 0c70264..fb4870b 100644 --- a/Datastructures/Collections/Queue.cs +++ b/Datastructures/Collections/Queue.cs @@ -11,11 +11,13 @@ namespace LunarDoggo.Datastructures.Collections //runs full and another object is enqueued, an exception is thrown. Additionally, as the oldest items are //dequeued first, one must keep track of the current head and tail of the queue, as it will no longer be //guaranteed that the item at index 0 is the next item to be dequeued +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix - This is an educational implementation of the Queue data structure public class Queue +#pragma warning restore CA1711 { private readonly T[] cache; - private int headIndex = 0; - private int tailIndex = 0; + private int headIndex; + private int tailIndex; public Queue(int capacity) { diff --git a/Datastructures/Collections/Stack.cs b/Datastructures/Collections/Stack.cs index 09423f1..b10307e 100644 --- a/Datastructures/Collections/Stack.cs +++ b/Datastructures/Collections/Stack.cs @@ -9,7 +9,9 @@ namespace LunarDoggo.Datastructures.Collections //The most primitive implementation uses an array that's not resized over the stack's lifetime. If the stack //runs full and another object is added, an exception is thrown signifying that no more items can be pushed //onto the stack +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix - This is an educational implementation of the Stack data structure public class Stack +#pragma warning restore CA1711 { //-1 means that there are no items in the cache yet private int currentIndex = -1; diff --git a/Datastructures/Graphs/Edge.cs b/Datastructures/Graphs/Edge.cs index 85273bb..49ccfb9 100644 --- a/Datastructures/Graphs/Edge.cs +++ b/Datastructures/Graphs/Edge.cs @@ -10,7 +10,7 @@ public Edge(Vertex from, Vertex to, bool bidirectional) //this check helps to avoid NullReferenceExceptions down the line if (from is null || to is null) { - throw new ArgumentNullException("None of the vertices of an edge can be null!"); + throw new ArgumentNullException(from is null ? nameof(from) : nameof(to), "None of the vertices of an edge can be null!"); } this.IsBidirectional = bidirectional; diff --git a/Datastructures/Graphs/IGraph.cs b/Datastructures/Graphs/IGraph.cs index 4a49877..b22df8e 100644 --- a/Datastructures/Graphs/IGraph.cs +++ b/Datastructures/Graphs/IGraph.cs @@ -24,7 +24,7 @@ public interface IGraph /// /// Removes an existing Edge from to from the graph /// - void RemoveEdge(Vertex from, Vertex to); + void RemoveEdge(Vertex from, Vertex toVertex); } //Unweighted graphs can also be described as graphs where every edge has the same weight of a constant c. @@ -42,7 +42,7 @@ public interface IUnweightedGraph : IGraph /// directed or undirected depends on the implementation used /// /// - Edge AddEdge(Vertex from, Vertex to); + Edge AddEdge(Vertex from, Vertex toVertex); } /// @@ -55,7 +55,7 @@ public interface IWeightedGraph : IGraph /// to the graph. If the edge is directed or undirected depends on the implementation used /// /// - Edge AddEdge(Vertex from, Vertex to, float weight); + Edge AddEdge(Vertex from, Vertex toVertex, float weight); /// /// Returns the weight of the provided /// diff --git a/Datastructures/Graphs/UndirectedUnweightedGraph.cs b/Datastructures/Graphs/UndirectedUnweightedGraph.cs index 9d0cb27..94c62a9 100644 --- a/Datastructures/Graphs/UndirectedUnweightedGraph.cs +++ b/Datastructures/Graphs/UndirectedUnweightedGraph.cs @@ -12,9 +12,9 @@ public class UndirectedUnweightedGraph : IUnweightedGraph public IEnumerable> Vertices { get => this.vertices.ToArray(); } public IEnumerable> Edges { get => this.edges.ToArray(); } - private int lastId = 0; + private int lastId; - public Edge AddEdge(Vertex from, Vertex to) + public Edge AddEdge(Vertex from, Vertex toVertex) { //this lock ensures thread safety when adding edges, as multithreaded Add operations without locks can lead to null //entries in the collection @@ -22,12 +22,12 @@ public Edge AddEdge(Vertex from, Vertex to) { //The ArgumentNullException advertised in the definition of AddEdge in IUnweightedGraph will be thrown by the //constructor of Edge, therefore it isn't necessary to check from and to for null values here - Edge edge = new Edge(from, to, true); + Edge edge = new Edge(from, toVertex, true); //As the edge and adjacencies are stored in HashSets, it isn't necessary to check if it already exists this.edges.Add(edge); - from.AddAdjacency(to); - to.AddAdjacency(from); + from.AddAdjacency(toVertex); + toVertex.AddAdjacency(from); return edge; } @@ -47,17 +47,17 @@ public Vertex AddVertex(T value) } } - public void RemoveEdge(Vertex from, Vertex to) + public void RemoveEdge(Vertex from, Vertex toVertex) { lock (this.edges) { //As edges that contain the same values are considered equal and all edges are stored in a HashSet, //edges can be removed by removing a new and equal edge from edges - Edge edge = new Edge(from, to, true); + Edge edge = new Edge(from, toVertex, true); this.edges.Remove(edge); //The adjacency lists of the vertices also have to be updated - from.RemoveAdjacency(to); - to.RemoveAdjacency(from); + from.RemoveAdjacency(toVertex); + toVertex.RemoveAdjacency(from); } } diff --git a/Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs b/Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs index 021635d..4870e44 100644 --- a/Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs +++ b/Projects/LunarDoggo.Beginners.ConsoleIO/Program.cs @@ -4,7 +4,7 @@ namespace LunarDoggo.ConsoleIO { - class Program + sealed class Program { /// /// This is the entrypoint to your application. When you execute your application, this method is called with the parameters diff --git a/Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs b/Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs index 3720373..7a77b33 100644 --- a/Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs +++ b/Projects/LunarDoggo.Beginners.ConsoleIOValidation/Program.cs @@ -3,7 +3,7 @@ namespace LunarDoggo.ConsoleIOValidation { - class Program + sealed class Program { /* * you can declare and initialize variables outside of methods. Note, that a variable must be static diff --git a/Projects/LunarDoggo.FileSystemTree/Program.cs b/Projects/LunarDoggo.FileSystemTree/Program.cs index 193af99..f8852d5 100644 --- a/Projects/LunarDoggo.FileSystemTree/Program.cs +++ b/Projects/LunarDoggo.FileSystemTree/Program.cs @@ -6,7 +6,7 @@ namespace LunarDoggo.FileSystemTree { - class Program + sealed class Program { static void Main(string[] args) { @@ -28,7 +28,7 @@ private static void OutputFileSystemTreeLevel(int indentationLevel, FileSystemTr //if the current tree item has any children, recursively print them and //their children to the console with the corresponding indentatino level - if (item.Children != null && item.Children.Count() > 0) + if (item.Children != null && item.Children.Any()) { foreach (FileSystemTreeItem child in item.Children) { diff --git a/Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs b/Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs index 0284556..e1df9dc 100644 --- a/Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs +++ b/Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs @@ -22,9 +22,9 @@ public FileQuizQuestionSerializer(string filePath) public IEnumerable DeserializeQuestions() { - string content = this.GetFileContent(this.filePath); - IEnumerable questions = this.DeserializeJson(content); - this.SetGuids(questions); + string content = FileQuizQuestionSerializer.GetFileContent(this.filePath); + IEnumerable questions = FileQuizQuestionSerializer.DeserializeJson(content); + FileQuizQuestionSerializer.SetGuids(questions); return questions; } @@ -32,7 +32,7 @@ public IEnumerable DeserializeQuestions() ///We don't trust the user to correctly set the Ids of the and . In order to prevent duplicate Ids ///this method assigns a unique to every and its s /// - private void SetGuids(IEnumerable questions) + private static void SetGuids(IEnumerable questions) { foreach (QuizQuestion question in questions) { @@ -45,7 +45,7 @@ private void SetGuids(IEnumerable questions) } } - private IEnumerable DeserializeJson(string content) + private static IEnumerable DeserializeJson(string content) { JsonSerializerOptions options = new JsonSerializerOptions() { @@ -59,7 +59,7 @@ private IEnumerable DeserializeJson(string content) return JsonSerializer.Deserialize(content, options); } - private string GetFileContent(string filePath) + private static string GetFileContent(string filePath) { //Check if the file exists, if not, return an empty string to prevent a FileNotFoundException, otherwise return the files content //Note that file access violations (e. g. another application has the file locked) are not handled and will lead to an exception diff --git a/Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs b/Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs index 2021f0d..cfdedec 100644 --- a/Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs +++ b/Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs @@ -34,13 +34,13 @@ public void DrawQuizQuestion(QuizQuestion question, Guid highlitedAnswerId) Console.WriteLine(); foreach (QuizQuestionAnswer answer in question.Answers) { - this.DrawQuizQuestionAnswer(answer, answer.Id == highlitedAnswerId); + ConsoleVisualizer.DrawQuizQuestionAnswer(answer, answer.Id == highlitedAnswerId); } Console.WriteLine(); Console.WriteLine(); } - private void DrawQuizQuestionAnswer(QuizQuestionAnswer answer, bool highlited) + private static void DrawQuizQuestionAnswer(QuizQuestionAnswer answer, bool highlited) { Console.SetCursorPosition(1, Console.CursorTop); //you can write on line conditional statements in the format: {boolean expression} ? {action when the condition is met} : {action when the condition isn't met} From fff0e8f9b5b12386d6b1cf98d20bd10a4a2c3d81 Mon Sep 17 00:00:00 2001 From: PM Palles Date: Mon, 8 Dec 2025 21:18:26 -0500 Subject: [PATCH 3/5] fix: ci errors --- .../Graphs/Pathfinding/BreadthFirstSearch.cs | 6 ++-- .../Graphs/Pathfinding/DepthFirstSearch.cs | 6 ++-- CODE_ANALYZER_FIXES.md | 30 +++++++++++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs b/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs index ef5e420..d2598fe 100644 --- a/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs +++ b/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs @@ -11,10 +11,10 @@ public void Run(IGraph graph, Vertex start) { if (graph is null) { - throw new ArgumentNullException("The graph the DFS should be run on cannot be null"); + throw new ArgumentNullException(nameof(graph), "The graph the BFS should be run on cannot be null"); } - this.Initialize(graph, start); + BreadthFirstSearch.Initialize(graph, start); //BFS is usually implemented in a way that doesn't work on disconnected graphs. BFS can therefore be //used to find all vertices contained in a connected component of the graph. @@ -38,7 +38,7 @@ public void Run(IGraph graph, Vertex start) } } - private void Initialize(IGraph graph, Vertex start) + private static void Initialize(IGraph graph, Vertex start) { //Every vertex is initialized with the maximum possible distance to signify that the distance is invalid. foreach (Vertex vertex in graph.Vertices) diff --git a/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs b/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs index 977ebb9..6a4f0f5 100644 --- a/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs +++ b/Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs @@ -13,10 +13,10 @@ public void Run(IGraph graph) { if (graph is null) { - throw new ArgumentNullException("The graph the DFS should be run on cannot be null"); + throw new ArgumentNullException(nameof(graph), "The graph the DFS should be run on cannot be null"); } - this.Initialize(graph); + DepthFirstSearch.Initialize(graph); //Current processing time int time = 0; @@ -55,7 +55,7 @@ private int RunRecursively(IGraph graph, Vertex start, int return startTime; } - private void Initialize(IGraph graph) + private static void Initialize(IGraph graph) { foreach (Vertex vertex in graph.Vertices) { diff --git a/CODE_ANALYZER_FIXES.md b/CODE_ANALYZER_FIXES.md index b79032b..230fed9 100644 --- a/CODE_ANALYZER_FIXES.md +++ b/CODE_ANALYZER_FIXES.md @@ -1,6 +1,6 @@ # Code Analyzer Fixes Summary -## Fixed Errors (18 total) +## Fixed Errors (22 total) ### CA1852: Type should be sealed (3 fixes) **Fixed in:** @@ -12,7 +12,7 @@ --- -### CA1822: Member can be marked as static (4 fixes) +### CA1822: Member can be marked as static (6 fixes) **Fixed in:** - `Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs`: - `SetGuids()` - Marked as static @@ -20,6 +20,10 @@ - `GetFileContent()` - Marked as static - `Projects/LunarDoggo.QuizGame/Visuals/ConsoleVisualizer.cs`: - `DrawQuizQuestionAnswer()` - Marked as static +- `Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs`: + - `Initialize()` - Marked as static +- `Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs`: + - `Initialize()` - Marked as static **Reason:** These methods don't access instance data, so marking them static improves clarity and performance. @@ -44,20 +48,32 @@ --- -### CA2208: ArgumentNullException paramName should match parameter (1 fix) +### CA2208: ArgumentNullException paramName should match parameter (3 fixes) **Fixed in:** - `Datastructures/Graphs/Edge.cs` - Changed from message-only to proper paramName + message +- `Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs` - Changed to use `nameof(graph)` +- `Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs` - Changed to use `nameof(graph)` -**Before:** +**Edge.cs Before:** ```csharp throw new ArgumentNullException("None of the vertices of an edge can be null!"); ``` -**After:** +**Edge.cs After:** ```csharp throw new ArgumentNullException(from is null ? nameof(from) : nameof(to), "None of the vertices of an edge can be null!"); ``` +**BFS/DFS Before:** +```csharp +throw new ArgumentNullException("The graph the DFS should be run on cannot be null"); +``` + +**BFS/DFS After:** +```csharp +throw new ArgumentNullException(nameof(graph), "The graph the BFS/DFS should be run on cannot be null"); +``` + **Reason:** ArgumentNullException should specify which parameter is null for better debugging. --- @@ -128,7 +144,9 @@ dotnet list package --vulnerable --include-transitive 8. `Datastructures/Graphs/Edge.cs` 9. `Datastructures/Graphs/IGraph.cs` 10. `Datastructures/Graphs/UndirectedUnweightedGraph.cs` -11. `.github/workflows/ci.yml` +11. `Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs` +12. `Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs` +13. `.github/workflows/ci.yml` ## Files Created From 97acb779272f1d5add3eb22b3efb5c9ef66de92e Mon Sep 17 00:00:00 2001 From: PM Palles Date: Mon, 8 Dec 2025 21:22:16 -0500 Subject: [PATCH 4/5] fix: ci errors --- Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs | 2 +- CODE_ANALYZER_FIXES.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs b/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs index d2598fe..50428eb 100644 --- a/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs +++ b/Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs @@ -7,7 +7,7 @@ namespace LunarDoggo.Algorithms.Graphs.Pathfinding //In simple graphs |E| is bounded by O(|V|^2), so in such cases, one could use O(|V|^2) as the time complexity public class BreadthFirstSearch { - public void Run(IGraph graph, Vertex start) + public static void Run(IGraph graph, Vertex start) { if (graph is null) { diff --git a/CODE_ANALYZER_FIXES.md b/CODE_ANALYZER_FIXES.md index 230fed9..37f28dc 100644 --- a/CODE_ANALYZER_FIXES.md +++ b/CODE_ANALYZER_FIXES.md @@ -1,6 +1,6 @@ # Code Analyzer Fixes Summary -## Fixed Errors (22 total) +## Fixed Errors (23 total) ### CA1852: Type should be sealed (3 fixes) **Fixed in:** @@ -12,7 +12,7 @@ --- -### CA1822: Member can be marked as static (6 fixes) +### CA1822: Member can be marked as static (7 fixes) **Fixed in:** - `Projects/LunarDoggo.QuizGame/IO/FileQuizQuestionSerializer.cs`: - `SetGuids()` - Marked as static @@ -22,6 +22,7 @@ - `DrawQuizQuestionAnswer()` - Marked as static - `Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs`: - `Initialize()` - Marked as static + - `Run()` - Marked as static - `Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs`: - `Initialize()` - Marked as static From b62096f9acdf70794e2f3315692b934436b91b54 Mon Sep 17 00:00:00 2001 From: PM Palles Date: Mon, 8 Dec 2025 21:26:08 -0500 Subject: [PATCH 5/5] fix: ci errors --- CODE_ANALYZER_FIXES.md | 42 ++++++++- DOCKER_TESTING.md | 99 +++++++++++++++++----- LunarDoggo.Benchmarks/SortingBenchmarks.cs | 5 +- 3 files changed, 120 insertions(+), 26 deletions(-) diff --git a/CODE_ANALYZER_FIXES.md b/CODE_ANALYZER_FIXES.md index 37f28dc..e58548a 100644 --- a/CODE_ANALYZER_FIXES.md +++ b/CODE_ANALYZER_FIXES.md @@ -1,6 +1,6 @@ # Code Analyzer Fixes Summary -## Fixed Errors (23 total) +## Fixed Errors (25 total) ### CA1852: Type should be sealed (3 fixes) **Fixed in:** @@ -108,6 +108,43 @@ throw new ArgumentNullException(nameof(graph), "The graph the BFS/DFS should be --- +### CS0305: Generic type requires type arguments (1 fix) +**Fixed in:** +- `LunarDoggo.Benchmarks/SortingBenchmarks.cs` - Added type argument `` and instantiated properly + +**Before:** +```csharp +InsertionSort.Sort(copy); +``` + +**After:** +```csharp +var sorter = new InsertionSort(); +sorter.Sort(copy); +``` + +**Reason:** `InsertionSort` is a generic class that requires a type argument. + +--- + +### CA1051: Do not declare visible instance fields (1 fix) +**Fixed in:** +- `LunarDoggo.Benchmarks/SortingBenchmarks.cs` - Converted public field `N` to property + +**Before:** +```csharp +public int N; +``` + +**After:** +```csharp +public int N { get; set; } +``` + +**Reason:** Public fields should be exposed as properties for better encapsulation and flexibility. + +--- + ## Testing Locally with Docker Created `DOCKER_TESTING.md` with instructions for testing locally before pushing to CI: @@ -147,7 +184,8 @@ dotnet list package --vulnerable --include-transitive 10. `Datastructures/Graphs/UndirectedUnweightedGraph.cs` 11. `Algorithms/Graphs/Pathfinding/BreadthFirstSearch.cs` 12. `Algorithms/Graphs/Pathfinding/DepthFirstSearch.cs` -13. `.github/workflows/ci.yml` +13. `LunarDoggo.Benchmarks/SortingBenchmarks.cs` +14. `.github/workflows/ci.yml` ## Files Created diff --git a/DOCKER_TESTING.md b/DOCKER_TESTING.md index 14fe051..2252cc8 100644 --- a/DOCKER_TESTING.md +++ b/DOCKER_TESTING.md @@ -1,46 +1,101 @@ -# Test locally with Docker before pushing to CI +# Local Docker Testing Guide -## Prerequisites -- Docker installed on your system +## Quick Test (One Command) -## Build and Test with Docker +Test your code exactly as CI will, before pushing: -### Option 1: Quick Test (Recommended) -Run this command to build and test in a container similar to CI: +```powershell +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c "dotnet restore && dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true" +``` + +This command: +- Uses the same .NET SDK 7.0 as CI +- Mounts your current directory into the container +- Runs restore and build with warnings as errors (just like CI) +- Automatically removes the container when done + +## Full CI Simulation + +To run all the same checks as CI: ```powershell -docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c "dotnet restore && dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true && dotnet test --no-build --configuration Release" +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c " + dotnet restore && + dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj && + dotnet build --no-restore --configuration Release && + dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true && + dotnet format --verify-no-changes --no-restore && + dotnet test --no-build --configuration Release && + dotnet list package --vulnerable --include-transitive +" ``` -### Option 2: Interactive Testing -For more control, start an interactive container: +## Interactive Testing + +For debugging or exploring: ```powershell -# Start container +# Start interactive container docker run --rm -it -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash +``` -# Inside the container, run: +Then inside the container: +```bash +# Exclude TicTacToe (Windows-only project) +dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj + +# Restore dependencies dotnet restore + +# Build +dotnet build --no-restore --configuration Release + +# Type check (build with warnings as errors) dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true + +# Format check dotnet format --verify-no-changes + +# Run tests dotnet test --no-build --configuration Release + +# Check for vulnerabilities dotnet list package --vulnerable --include-transitive + +# Exit container +exit ``` -### Option 3: Test with Docker Compose -Use the provided docker-compose.yml: +## Why Docker? -```powershell -docker-compose build -docker-compose run quiz-game -``` +- **Same environment as CI**: Uses exact same .NET SDK version +- **No local SDK needed**: Works even if you don't have .NET installed +- **Clean state**: Each run starts fresh +- **Cross-platform**: Works on Windows, Mac, and Linux -## Note on TicTacToe Project -The `LunarDoggo.TicTacToe` project targets .NET Framework 4.5.2 which is Windows-only. When testing on Linux (including Docker), this project will fail to build. The CI pipeline automatically excludes it on Linux runners. +## Common Issues -To test locally on Linux/Docker while excluding TicTacToe: +### TicTacToe Build Errors +The `LunarDoggo.TicTacToe` project targets .NET Framework 4.5.2 (Windows-only). Always remove it when testing on Linux/Docker: ```bash dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj -dotnet build --configuration Release /p:TreatWarningsAsErrors=true -dotnet sln add Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj ``` + +### Permission Issues +If you get permission errors on Linux/Mac, you may need to adjust the volume mount: +```bash +docker run --rm -v $(pwd):/src -w /src --user $(id -u):$(id -g) mcr.microsoft.com/dotnet/sdk:7.0 bash -c "..." +``` + +## Pro Tip + +Create a PowerShell script `test-ci.ps1`: +```powershell +docker run --rm -v ${PWD}:/src -w /src mcr.microsoft.com/dotnet/sdk:7.0 bash -c " + dotnet restore && + dotnet sln remove Projects/LunarDoggo.TicTacToe/LunarDoggo.TicTacToe.csproj && + dotnet build --no-restore --configuration Release /p:TreatWarningsAsErrors=true +" +``` + +Then just run: `./test-ci.ps1` diff --git a/LunarDoggo.Benchmarks/SortingBenchmarks.cs b/LunarDoggo.Benchmarks/SortingBenchmarks.cs index 0b68c2c..b8c76c4 100644 --- a/LunarDoggo.Benchmarks/SortingBenchmarks.cs +++ b/LunarDoggo.Benchmarks/SortingBenchmarks.cs @@ -9,7 +9,7 @@ public class SortingBenchmarks private int[]? _data; [Params(100, 1000, 10000)] - public int N; + public int N { get; set; } [GlobalSetup] public void Setup() @@ -23,7 +23,8 @@ public void InsertionSortBenchmark() // Note: Assuming InsertionSort is static or we can instantiate it. // Adjust based on actual implementation. var copy = (int[])_data!.Clone(); - InsertionSort.Sort(copy); + var sorter = new InsertionSort(); + sorter.Sort(copy); } }