Browse Source

Fix GPU summary escaping in CLI reports

Use Spectre.Console.Markup.Escape() to properly escape brackets in GPU
model names, preventing markup parser errors when displaying [UHD Graphics]
and similar descriptions.

Fixes: #274
Tim Jones 15 hours ago
parent
commit
240356afe2

+ 90 - 0
AGENTS.md

@@ -0,0 +1,90 @@
+# RackPeek — Agent Quick Reference
+
+## Commands
+
+**Build & Test**
+```bash
+just build              # dotnet build RackPeek.sln
+just test-all           # CLI + E2E tests (rebuilds Web image)
+just ci                 # alias for test-all (matches CI checklist)
+just test-cli           # dotnet test Tests/Tests.csproj
+just test-e2e           # requires just build-web first
+```
+
+**Run Locally**
+```bash
+just run-docker         # builds and starts Docker container on :8080
+just rpk [args]         # run CLI directly from debug build
+```
+
+**E2E Setup** (first time only)
+```bash
+just e2e-setup          # installs Playwright CLI + browsers
+```
+
+**Demo**
+```bash
+just build-cli-demo     # VHS CLI demo (needs: vhs, imagemagick, chrome)
+just build-web-demo     # Web UI demo (needs: Chrome, ImageMagick)
+```
+
+**Release**
+```bash
+just docker-push <ver>  # multi-arch Docker push (e.g., just docker-push 1.3.0)
+```
+
+## Workflow
+
+1. **CI order**: `format → cli-tests → webui-tests`
+2. **PR checklist**:
+   - Linked GitHub issue
+   - Approach validated with maintainers
+   - Small, focused PR
+   - CLI tests passing locally
+   - E2E tests passing locally
+   - YAML migration defined if persisting changes
+
+3. **Draft PR** until:
+   - All tests pass locally
+   - Scope complete
+   - Debug code removed
+
+## Architecture
+
+**Solution structure**:
+- `RackPeek/` — CLI application
+- `RackPeek.Domain/` — shared domain models
+- `RackPeek.Web/` — Web UI (Blazor)
+- `RackPeek.Web.Viewer/` — Web UI viewer
+- `Shared.Rcl/` — shared Blazor components
+- `Tests/` — CLI unit tests
+- `Tests.E2e/` — Playwright E2E tests
+
+**Key files**:
+- `justfile` — developer workflow commands
+- `.github/workflows/test.yml` — CI pipeline
+- `RackPeek.sln` — solution root
+- `docs/development/` — dev guides (dev-cheat-sheet.md, testing-guidelines.md)
+
+## Gotchas
+
+- **E2E tests require Docker image**: run `just build-web` before `just test-e2e`
+- **Playwright browsers** installed via `just e2e-setup` (first time)
+- **CI runs on `ubuntu-latest`** (CLI tests) and `ubuntu-24.04` (WebUI)
+- **Docker image tag**: `rackpeek:ci` used locally, `aptacode/rackpeek` on registry
+- **Format check**: `dotnet format --verify-no-changes` (CI step 1)
+- **Debugging E2E**: Set `Headless = false, SlowMo = 500` in `PlaywrightFixture.cs`, revert before commit
+- **YAML changes**: Always define migration if modifying persisted schema
+
+## Testing
+
+- **CLI tests**: `dotnet test Tests/Tests.csproj` (fast, no Docker)
+- **E2E tests**: `dotnet test Tests.E2e` (requires Docker image, Playwright browsers)
+- **Full suite**: `just ci` or `just test-all`
+
+## Docs References
+
+- `docs/development/contribution-guidelines.md` — PR process
+- `docs/development/dev-cheat-sheet.md` — build/release details
+- `docs/development/testing-guidelines.md` — testing principles
+- `README.md` — overview and Docker usage

+ 1 - 0
RackPeek.Domain/RackPeek.Domain.csproj

@@ -12,6 +12,7 @@
         <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
         <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.3" />
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
         <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
         <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.3" />
         <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.3" />
+        <PackageReference Include="Spectre.Console" Version="0.54.0" />
         <PackageReference Include="YamlDotNet" Version="16.3.0" />
         <PackageReference Include="YamlDotNet" Version="16.3.0" />
     </ItemGroup>
     </ItemGroup>
 
 

+ 2 - 1
RackPeek.Domain/Resources/Desktops/DesktopHardwareReport.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence;
+using Spectre.Console;
 
 
 namespace RackPeek.Domain.Resources.Desktops;
 namespace RackPeek.Domain.Resources.Desktops;
 
 
@@ -60,7 +61,7 @@ public class DesktopHardwareReportUseCase(IResourceCollection repository) : IUse
                 : string.Join(", ",
                 : string.Join(", ",
                     desktop.Gpus
                     desktop.Gpus
                         .GroupBy(g => g.Model)
                         .GroupBy(g => g.Model)
-                        .Select(g => $"{g.Count()}× {g.Key}"));
+                        .Select(g => $"{g.Count()}× {Markup.Escape(g.Key ?? "Unknown")}"));
 
 
             return new DesktopHardwareRow(
             return new DesktopHardwareRow(
                 desktop.Name,
                 desktop.Name,

+ 2 - 1
RackPeek.Domain/Resources/Laptops/LaptopHardwareReportUseCase.cs

@@ -1,4 +1,5 @@
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence;
+using Spectre.Console;
 
 
 namespace RackPeek.Domain.Resources.Laptops;
 namespace RackPeek.Domain.Resources.Laptops;
 
 
@@ -32,7 +33,7 @@ public class LaptopHardwareReportUseCase(IResourceCollection repository) : IUseC
                 : string.Join(", ",
                 : string.Join(", ",
                     laptop.Gpus
                     laptop.Gpus
                         .GroupBy(g => g.Model)
                         .GroupBy(g => g.Model)
-                        .Select(g => $"{g.Count()}× {g.Key}"));
+                        .Select(g => $"{g.Count()}× {Markup.Escape(g.Key ?? "Unknown")}"));
 
 
             return new LaptopHardwareRow(
             return new LaptopHardwareRow(
                 laptop.Name,
                 laptop.Name,

+ 2 - 1
RackPeek.Domain/Resources/Servers/ServerHardwareReport.cs

@@ -1,5 +1,6 @@
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Persistence;
 using RackPeek.Domain.Resources.SubResources;
 using RackPeek.Domain.Resources.SubResources;
+using Spectre.Console;
 
 
 namespace RackPeek.Domain.Resources.Servers;
 namespace RackPeek.Domain.Resources.Servers;
 
 
@@ -77,7 +78,7 @@ public class ServerHardwareReportUseCase(IResourceCollection repository) : IUseC
                 : string.Join(", ",
                 : string.Join(", ",
                     server.Gpus
                     server.Gpus
                         .GroupBy(g => g.Model)
                         .GroupBy(g => g.Model)
-                        .Select(g => $"{g.Count()}× {g.Key}"));
+                        .Select(g => $"{g.Count()}× {Markup.Escape(g.Key ?? "Unknown")}"));
 
 
 
 
             return new ServerHardwareRow(
             return new ServerHardwareRow(