diff --git a/.cursor/rules/csharp-style.md b/.cursor/rules/csharp-style.md index 10f179f..5e11015 100644 --- a/.cursor/rules/csharp-style.md +++ b/.cursor/rules/csharp-style.md @@ -262,5 +262,9 @@ public async Task PostExample_ShouldReturnOk_WhenBodyValid() ## Tooling -- Prefer fixes that satisfy built-in **.NET analyzers** / `dotnet format` (if adopted) rather than fighting IDE warnings without reason. -- Remove **unnecessary `using` directives** (IDE0005 / CS8019) — with **implicit usings** and **global usings**, many `Microsoft.Extensions.*`, `System.Threading`, and `System.Linq` imports are redundant. Run `dotnet format .csproj --diagnostics IDE0005 --severity info` before merge on touched projects. +- **`Directory.Build.props`**: **`TreatWarningsAsErrors`** — any compiler or analyzer **warning** fails **`dotnet build`**. +- **`.editorconfig`**: **`IDE0005`** / **`CS8019`** (unnecessary usings) at **warning** severity — caught by **`dotnet format`**, not duplicated in per-file usings when covered by **`GlobalUsings.cs`** or implicit usings. +- Before commit on touched **`server/**/*.cs`**: pre-commit runs **`scripts/verify-dotnet-format.sh`**; CI runs the same after restore. +- Fix redundant usings locally: `dotnet format server/NeonSprawl.Server/NeonSprawl.Server.csproj --severity warn` (and Tests project when applicable). +- Prefer fixes that satisfy built-in **.NET analyzers** / `dotnet format` rather than fighting IDE warnings without reason. +- When adding **`server/NeonSprawl.Server.Tests/**/*.cs`**, read **`GlobalUsings.cs`** first — do not copy sibling **`using`** blocks blindly; only import namespaces used in that file. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d789d69 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Neon Sprawl — shared editor and analyzer settings (C# server). +# Pair with Directory.Build.props (TreatWarningsAsErrors + EnforceCodeStyleInBuild). +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.cs] +# Redundant usings duplicate GlobalUsings.cs / implicit usings (CS8019 / IDE0005). +dotnet_diagnostic.IDE0005.severity = warning +dotnet_diagnostic.CS8019.severity = warning diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index c5af815..8fb02e3 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -11,6 +11,8 @@ on: branches: [main] paths: - "NeonSprawl.sln" + - "Directory.Build.props" + - ".editorconfig" - "server/**" - "bruno/neon-sprawl-server/**" - ".github/workflows/dotnet.yml" @@ -52,6 +54,8 @@ jobs: filters: | server: - "NeonSprawl.sln" + - "Directory.Build.props" + - ".editorconfig" - "server/**" - "bruno/neon-sprawl-server/**" - ".github/workflows/dotnet.yml" @@ -70,6 +74,10 @@ jobs: if: github.event_name == 'push' || steps.paths.outputs.server == 'true' run: dotnet restore NeonSprawl.sln + - name: Verify dotnet format (IDE0005 / style) + if: github.event_name == 'push' || steps.paths.outputs.server == 'true' + run: ./scripts/verify-dotnet-format.sh + - name: Build if: github.event_name == 'push' || steps.paths.outputs.server == 'true' run: dotnet build NeonSprawl.sln --no-restore --configuration Release diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..2a6c53b --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,7 @@ + + + + true + + + diff --git a/client/README.md b/client/README.md index b18052b..959d9c7 100644 --- a/client/README.md +++ b/client/README.md @@ -501,7 +501,7 @@ python3 -m venv .venv-gd This enables: -- **pre-commit**: blocks commits when `client/scripts/*.gd` changes lack matching `client/test/*_test.gd` updates, or when server route/contract files change without corresponding `server/NeonSprawl.Server.Tests/` and `bruno/neon-sprawl-server/**/*.bru` updates. +- **pre-commit**: blocks commits when `client/scripts/*.gd` changes lack matching `client/test/*_test.gd` updates, when server route/contract files change without corresponding `server/NeonSprawl.Server.Tests/` and `bruno/neon-sprawl-server/**/*.bru` updates, or when staged `server/**/*.cs` fails **`scripts/verify-dotnet-format.sh`** (unnecessary usings / warn-severity style — pair with **`TreatWarningsAsErrors`** in **`Directory.Build.props`**). - **pre-push**: when **`client/scripts/*.gd`** or **`client/test/*.gd`** changed in the commits being pushed, runs **`gdlint client/scripts client/test`** and **`gdformat --check client/scripts client/test`** (same scope as CI). Otherwise skips lint/format. Still requires a **clean working tree** on every push. The pre-push hook prefers **`.venv-gd/bin/`** (Unix) or **`.venv-gd/Scripts/`** (Windows venv) when present; without that venv or a global install, the hook **fails** when GDScript changed — same as CI. Pushes with no client `.gd` changes do not require gdtoolkit installed. diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit index 426303c..c67dc16 100755 --- a/scripts/git-hooks/pre-commit +++ b/scripts/git-hooks/pre-commit @@ -46,3 +46,12 @@ if has_staged_match '^client/scripts/.*\.gd$'; then exit 1 fi fi + +if has_staged_match '^server/.*\.cs$'; then + if ! "$repo_root/scripts/verify-dotnet-format.sh"; then + echo "pre-commit: dotnet format check failed (unnecessary usings / style)." >&2 + echo "Fix with: dotnet format server/NeonSprawl.Server/NeonSprawl.Server.csproj --severity warn" >&2 + echo " dotnet format server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --severity warn" >&2 + exit 1 + fi +fi diff --git a/scripts/verify-dotnet-format.sh b/scripts/verify-dotnet-format.sh new file mode 100755 index 0000000..0d22fe1 --- /dev/null +++ b/scripts/verify-dotnet-format.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Verify dotnet format (IDE0005 unnecessary usings + other warn-severity style) with no auto-fixes. +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" +cd "$repo_root" + +projects=( + server/NeonSprawl.Server/NeonSprawl.Server.csproj + server/NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj +) + +try_format() { + local proj="$1" + dotnet format "$proj" --verify-no-changes --severity warn --no-restore +} + +restore_needed=false +for proj in "${projects[@]}"; do + if ! try_format "$proj" >/dev/null 2>&1; then + restore_needed=true + break + fi +done + +if [[ "$restore_needed" == true ]]; then + dotnet restore NeonSprawl.sln --verbosity quiet + for proj in "${projects[@]}"; do + try_format "$proj" + done +fi