From 8e3b5ebdfc8c6333da33411e2d76351e17dc04a4 Mon Sep 17 00:00:00 2001 From: Alexandros Kritikos Date: Thu, 16 Apr 2026 15:56:15 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Adds=20project=20guidelines=20and?= =?UTF-8?q?=20conventions=20for=20Github=20Copilot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces detailed instructions for ASP.NET API, C#, Docker, MSBuild, testing, and library creation. These guidelines aim to standardize development practices and improve code quality across the project. --- .github/copilot-instructions.md | 12 +++ .../instructions/aspnet-api.instructions.md | 32 +++++++ .github/instructions/csharp.instructions.md | 17 ++++ .github/instructions/docker.instructions.md | 31 +++++++ .github/instructions/msbuild.instructions.md | 39 +++++++++ .github/instructions/testing.instructions.md | 23 +++++ .github/skills/docker-build/SKILL.md | 86 +++++++++++++++++++ .github/skills/new-library/SKILL.md | 48 +++++++++++ .github/skills/new-web-api-project/SKILL.md | 58 +++++++++++++ 9 files changed, 346 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/instructions/aspnet-api.instructions.md create mode 100644 .github/instructions/csharp.instructions.md create mode 100644 .github/instructions/docker.instructions.md create mode 100644 .github/instructions/msbuild.instructions.md create mode 100644 .github/instructions/testing.instructions.md create mode 100644 .github/skills/docker-build/SKILL.md create mode 100644 .github/skills/new-library/SKILL.md create mode 100644 .github/skills/new-web-api-project/SKILL.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..9f6daa7 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,12 @@ +# Project Guidelines + +## Behaviour + +- **Tone**: direct and concise — minimise prose, maximise actionable output. +- **Research first, act second** — always consult official documentation, codebase context, and project conventions **before** proposing or implementing changes. Never guess, assume API shapes, or rely on trial-and-error. If documentation is unclear or unavailable, say so and ask for guidance. +- On ambiguous requests, **ask for clarification** — prefer offering interactive options over waiting for a new prompt. +- Briefly explain every suggestion: what it does, why, and how it fits the architecture. +- Work in parallel whenever possible. +- **Prefer subagents** for multi-step read-only operations (searches, file reads, doc fetches) to conserve main conversation context. +- When goals conflict (e.g., performance vs readability), **favour performance** unless readability is significantly harmed. +- **Persist learnings** — when you discover a pattern, resolve a non-obvious issue, or make a decision worth remembering, save it to memory. Use repo memory for project-specific facts and user memory for general preferences. Keep memory up to date when instructions or project conventions change. diff --git a/.github/instructions/aspnet-api.instructions.md b/.github/instructions/aspnet-api.instructions.md new file mode 100644 index 0000000..5e9e834 --- /dev/null +++ b/.github/instructions/aspnet-api.instructions.md @@ -0,0 +1,32 @@ +--- +description: "Use when writing API endpoints, routing, middleware, request/response handling, OpenAPI configuration, or HTTP pipeline setup in ASP.NET. Covers Minimal API conventions, error handling, and endpoint organisation." +--- +# ASP.NET API Conventions + +## Routing + +- All endpoints live under `/api/v{apiVersion}/{resource}`. +- Default to `/api/v1` for projects without explicit API versioning. + +## Endpoints + +- Prefer **Minimal APIs**; use Controllers only when the added structure is justified. +- Group related endpoints using `MapGroup()` with a shared prefix and tag. +- Use **typed results** (`TypedResults.Ok()`, `TypedResults.NotFound()`) over untyped `Results` for OpenAPI metadata inference. +- Return **`ProblemDetails`** (RFC 9457) for all error responses — configure via `AddProblemDetails()`. + +## Request Handling + +- Bind request data with **`[AsParameters]`** for complex parameter sets. +- Validate input at the endpoint boundary — use **filters** or **`IValidatableObject`** to fail fast. +- Use **`CancellationToken`** on all async endpoints. + +## Endpoint Organisation + +- One file per resource or feature area (e.g., `UserEndpoints.cs`, `OrderEndpoints.cs`). +- Register via extension methods on `IEndpointRouteBuilder`: `MapUserEndpoints()`. +- Keep endpoint lambdas thin — delegate business logic to injected services. + +## OpenAPI + +- Annotate endpoints with `.WithTags()` for grouping in the generated spec. diff --git a/.github/instructions/csharp.instructions.md b/.github/instructions/csharp.instructions.md new file mode 100644 index 0000000..389d5d6 --- /dev/null +++ b/.github/instructions/csharp.instructions.md @@ -0,0 +1,17 @@ +--- +description: "Use when writing or modifying C# source code. Covers non-obvious defaults not caught by analyzers." +applyTo: "**/*.cs" +--- +# C# Conventions + +Code style is enforced by `.editorconfig`, `.globalconfig`, and StyleCop analyzers — do not restate their rules. + +## Non-Obvious Defaults + +- XML doc comments required on public API in `src/` (suppressed in `tests/` and `samples/`). +- Avoid `null!` (null-forgiving operator) — fix the nullability flow instead of suppressing the warning. + +## Patterns + +- Prefer **records** for DTOs, value objects, and other immutable data carriers. +- Use `LoggerMessage`-generated `partial` methods on a `partial class` — never log inline (e.g., `logger.LogInformation(…)`). diff --git a/.github/instructions/docker.instructions.md b/.github/instructions/docker.instructions.md new file mode 100644 index 0000000..744bb79 --- /dev/null +++ b/.github/instructions/docker.instructions.md @@ -0,0 +1,31 @@ +--- +description: "Use when editing Dockerfile, compose files, or Docker-related configuration. Covers guardrails for the multi-stage build and runtime image selection." +applyTo: "docker/**,compose*.yaml" +--- +# Docker Conventions + +`docker/Dockerfile` is a multi-stage pipeline — stage names and purposes are visible in the file itself. + +## Guardrails + +- **Build context** is always the repo root, not `docker/`. +- **`COPY --link`** is used intentionally for layer cache — do not replace with plain `COPY`. +- **Cache mounts** (`--mount=type=cache,target=/root/.nuget/packages`) must stay on restore, build, test, and publish steps. +- **Stage dependency chain** must be preserved (e.g., `build` → `restore`, `test` → `build`, `publish` → `test`). +- New stages should follow the existing pattern: `FROM AS `. + +## Build Args + +- `RUNTIME_BASE`: `web` (ASP.NET), `app` (console), or `self-contained` +- `CONFIGURATION`: `Release` (default) or `Debug` +- `PUBLISHED_PROJECT`: Project name to publish (without path or extension) +- `DATABASE_PROJECT`: EF Core project for migration bundles +- `DBCONTEXT`: Optional DbContext name for migrations + +## Image Selection + +| Use Case | `RUNTIME_BASE` | Base Image | +|----------|----------------|------------| +| ASP.NET web apps | `web` | `aspnet:*-alpine-composite-extra` | +| Console / worker | `app` | `runtime:*-alpine-extra` | +| Self-contained | `self-contained` | `runtime-deps:*-alpine-extra` | diff --git a/.github/instructions/msbuild.instructions.md b/.github/instructions/msbuild.instructions.md new file mode 100644 index 0000000..e9729b1 --- /dev/null +++ b/.github/instructions/msbuild.instructions.md @@ -0,0 +1,39 @@ +--- +description: "Use when editing MSBuild files (.csproj, .props, .targets). Covers central package management, artifacts layout, and property inheritance." +applyTo: "**/*.{csproj,props,targets}" +--- +# MSBuild Conventions + +## Central Package Management + +All package versions are declared in `Directory.Packages.props` at the repo root. In `.csproj` files: + +```xml + + + + + +``` + +To add or update a version, edit `Directory.Packages.props`: + +```xml + +``` + +## Properties Set Centrally (Do Not Override) + +These are configured in the root `Directory.Build.props` — do **not** set them in individual projects: + +`LangVersion`, `Nullable`, `ImplicitUsings`. + +## InternalsVisibleTo + +Handled automatically via `` in the root props. Default suffixes are `.Tests` and `Tests`. To add custom visibility: + +```xml + + + +``` diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md new file mode 100644 index 0000000..45a4a5c --- /dev/null +++ b/.github/instructions/testing.instructions.md @@ -0,0 +1,23 @@ +--- +description: "Use when creating or modifying test projects under the tests/ folder. Covers naming conventions and structural guardrails." +applyTo: "tests/**" +--- +# Test Project Conventions + +Build configuration is handled by `tests/Directory.Build.props` — do not restate its settings. + +## Framework + +- **Testing**: TUnit. +- **Mocking**: NSubstitute (analyzers auto-added when referenced). + +## Naming + +- Test project: `.Tests` (matches InternalsVisibleTo default). +- Test classes: `Tests`. +- Test methods: descriptive names — `MethodName_Condition_ExpectedResult` or similar. + +## Structure + +- Follow **Arrange / Act / Assert**. +- Always propose a set of basic tests for new or changed code. diff --git a/.github/skills/docker-build/SKILL.md b/.github/skills/docker-build/SKILL.md new file mode 100644 index 0000000..587483e --- /dev/null +++ b/.github/skills/docker-build/SKILL.md @@ -0,0 +1,86 @@ +--- +name: docker-build +description: "Build, test, publish, or pack .NET projects using the multi-stage Docker pipeline. Use when: building Docker images, running containerized tests, creating migration bundles, publishing to containers, or troubleshooting Dockerfile stages." +argument-hint: "Describe what you want to build or publish (e.g., 'publish WebApplication1 as web app')" +--- +# Docker Build Skill + +## When to Use + +- Build and run a .NET project in Docker +- Run tests inside a container +- Create a NuGet package via Docker +- Create an EF Core migration bundle +- Troubleshoot multi-stage Docker build issues + +## Prerequisites + +- Docker Desktop or Docker Engine running +- Repository root as build context + +## Common Workflows + +### Build and run a web application + +```shell +docker compose -f docker/compose.sample.yaml build sample-api \ + --build-arg PUBLISHED_PROJECT= +docker compose -f docker/compose.sample.yaml up sample-api +``` + +### Build self-contained application + +```shell +docker build -f docker/Dockerfile \ + --target final \ + --build-arg RUNTIME_BASE=self-contained \ + --build-arg PUBLISHED_PROJECT= \ + -t myapp . +``` + +### Run tests in container + +```shell +docker build -f docker/Dockerfile --target test . +``` + +### Lint OpenAPI documents + +```shell +docker build -f docker/Dockerfile --target openapi-lint . +``` + +### Pack NuGet packages + +```shell +docker build -f docker/Dockerfile --target pack --output artifacts/nuget . +``` + +### Create EF Core migration bundle + +```shell +docker build -f docker/Dockerfile --target migrations \ + --build-arg PUBLISHED_PROJECT= \ + --build-arg DATABASE_PROJECT= \ + --build-arg DBCONTEXT= \ + -t migrations . +``` + +## Stage Reference + +See [Dockerfile](../../docker/Dockerfile) for the full multi-stage pipeline. Key stages and their targets: + +| `--target` | Produces | +|------------|----------| +| `test` | Runs `dotnet test` — exits non-zero on failure | +| `openapi-lint` | Spectral lint on generated OpenAPI JSON | +| `publish` | Published app in `/app/publish` | +| `pack` | NuGet `.nupkg` files in `/app/publish` | +| `migration-bundle` | Standalone EF migration binary | +| `final` | Production runtime image | + +## Troubleshooting + +- **Restore failures**: Check `nuget.config` package sources and ensure `packages.lock.json` files are committed. +- **GitVersion errors**: The `gitversion` stage needs `.git/` — ensure the build context includes it. +- **Missing OpenAPI docs**: The `openapi-lint` stage only runs Spectral if `*.json` files exist in `artifacts/OpenApi/`. diff --git a/.github/skills/new-library/SKILL.md b/.github/skills/new-library/SKILL.md new file mode 100644 index 0000000..9a60671 --- /dev/null +++ b/.github/skills/new-library/SKILL.md @@ -0,0 +1,48 @@ +--- +name: new-library +description: 'Scaffold a new reusable .NET library with correct naming, multi-targeting, and structure. Use when: creating a new library project, adding a reusable package, setting up a shared library under src/.' +argument-hint: 'Brief description of the library purpose (e.g., "hashing utilities", "EF Core auditing")' +--- + +# New Library + +## When to Use + +- Creating a new reusable, packable library under `src/` +- Adding a shared component meant for NuGet distribution + +## Naming Convention + +Libraries use a `{Domain}.{Feature}` pattern. The `Kritikos.` root namespace prefix is applied automatically by `Directory.Build.props`. + +| Domain prefix | When to use | Example | +|---|---|---| +| `AspNetCore.{Feature}` | Depends on `Microsoft.AspNetCore.App` | `AspNetCore.ProblemDetails` | +| `Extensions.{Feature}` | Framework-agnostic, depends only on `Microsoft.Extensions.*` | `Extensions.Hashing` | +| `EntityFrameworkCore.{Feature}` | Depends on EF Core | `EntityFrameworkCore.Auditing` | +| *(no prefix)* | Pure .NET with no framework dependency | `Primitives` | + +**Always propose 2–4 name options** to the user and allow a custom choice. + +## Multi-Targeting + +Target **both current LTS versions** plus optionally **one STS version** if newer than the latest LTS: + +| Scenario | Targets | Rationale | +|---|---|---| +| .NET 10 is current LTS | `net8.0;net10.0` | Skip .NET 9 (STS, superseded) | +| .NET 11 (STS) ships | `net8.0;net10.0;net11.0` | 11 > 10, include it | +| .NET 12 (LTS) ships | `net10.0;net12.0` | .NET 8 drops; 11 superseded | + +Libraries with **no framework dependency** (no domain prefix) should also target `netstandard2.0`/`netstandard2.1` when APIs and dependencies allow. Framework-dependent libraries skip .NET Standard. + +## Procedure + +1. Ask the user for the library's purpose. +2. Propose name options using the naming table above. +3. Determine the correct multi-targeting based on current LTS/STS and framework dependencies. +4. Create the project under `src/{LibraryName}/`. +5. Do **not** specify `Version` on `PackageReference` — add versions in `Directory.Packages.props`. +6. Create a matching test project under `tests/{LibraryName}.Tests/`. +7. Add both projects to `Solution.slnx`. +8. Verify the build compiles cleanly. diff --git a/.github/skills/new-web-api-project/SKILL.md b/.github/skills/new-web-api-project/SKILL.md new file mode 100644 index 0000000..02483ec --- /dev/null +++ b/.github/skills/new-web-api-project/SKILL.md @@ -0,0 +1,58 @@ +--- +name: new-web-api-project +description: 'Scaffold a new ASP.NET web project (API or worker service) with correct structure, launch settings, and Docker integration. Use when: creating a new web API, adding an ASP.NET project, setting up a new web service under src/.' +argument-hint: 'Brief description of the web project (e.g., "REST API for user management", "background worker for email processing")' +--- + +# New Web Project + +## When to Use + +- Creating a new ASP.NET web API or worker service under `src/` +- Adding a web project that will be deployed via Docker + +## Procedure + +1. Ask the user for the project's purpose and type (API or worker). +2. Create the project under `src/{ProjectName}/` using the appropriate SDK (`Microsoft.NET.Sdk.Web` or `Microsoft.NET.Sdk.Worker`). +4. **Single target framework** — web projects target only the current LTS (no multi-targeting). +5. Create `Properties/launchSettings.json` with `http` and `https` profiles: + - Pick ports that **do not conflict** with existing projects — check all `src/*/Properties/launchSettings.json` files first. + - Set `launchBrowser: false`. + - Set `ASPNETCORE_ENVIRONMENT: Development`. +6. For APIs: use **Minimal APIs** with OpenAPI support (`Microsoft.AspNetCore.OpenApi`). The OpenAPI document generation and Spectral linting are handled automatically by the build infrastructure. +7. Create a matching test project under `tests/{ProjectName}.Tests/`. +8. Add both projects to `Solution.slnx`. +9. Add a compose service entry to `compose.yaml` (or create one from `docker/compose.sample.yaml`): + - Set `RUNTIME_BASE: web` for APIs, `RUNTIME_BASE: app` for workers. + - Set `PUBLISHED_PROJECT` to the project name. + - Build context is the repo root with `dockerfile: docker/Dockerfile`. +10. Verify the build compiles cleanly. + +## Launch Settings Template + +```json +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:{HTTP_PORT}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:{HTTPS_PORT};http://localhost:{HTTP_PORT}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} +```