Template
Compare commits
41
Commits
388c036ce6
...
1912484334
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1912484334
|
||
|
|
f4c843b9f7
|
||
|
|
eee54a4a36
|
||
|
|
70a0eeec33
|
||
|
|
3b31cbcb14
|
||
|
|
0d7d18fae0
|
||
|
|
243a3ce519
|
||
|
|
f975b7a41f
|
||
|
|
be09aef8a3
|
||
|
|
406af21694
|
||
|
|
d7b610d6f5
|
||
|
|
4d05e08d98
|
||
|
|
fdbf44149a
|
||
|
|
88b3e5c661
|
||
|
|
96696ab2bb
|
||
|
|
569fdd8053
|
||
|
|
5f7dc55e66
|
||
|
|
661908bcea
|
||
|
|
9b27bbdc37
|
||
|
|
7bc86d8a34
|
||
|
|
4095d6385b
|
||
|
|
bd50907471
|
||
|
|
eea4ce7a0e
|
||
|
|
b169a233ed
|
||
|
|
b34dffe5f2
|
||
|
|
5d109c31aa
|
||
|
|
0cc34f7b4b
|
||
|
|
f5ce51d854
|
||
|
|
0f5eab59a8
|
||
|
|
57062f7bea
|
||
|
|
c80a6fbe58
|
||
|
|
14f84c0300
|
||
|
|
741b2f0501
|
||
|
|
664d676ab6
|
||
|
|
bc4c5de10c
|
||
|
|
f51e396bdb
|
||
|
|
e4fe3d91c2
|
||
|
|
780f133885
|
||
|
|
9a8dde4699
|
||
|
|
94a85e5ba4
|
||
|
|
6809901e8d
|
No files matched your search
+164
-1
@@ -13,5 +13,168 @@ end_of_line = lf
|
||||
[*.{xml,csproj,props,targets,nuspec}]
|
||||
max_line_length = 1000
|
||||
|
||||
[{*.Generated.*.cs,**/Migrations/*.cs}]
|
||||
[*.{cs,vb}]
|
||||
# Organize usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_separate_import_directive_groups = true
|
||||
csharp_using_directive_placement = inside_namespace:suggestion
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
# this. preferences
|
||||
dotnet_style_qualification_for_field = false:none
|
||||
dotnet_style_qualification_for_property = false:none
|
||||
dotnet_style_qualification_for_method = false:none
|
||||
dotnet_style_qualification_for_event = false:none
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
# var preferences
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_properties = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
# Null-checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
# Modifier preferences
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
|
||||
# Expression-level preferences
|
||||
csharp_prefer_braces = true:none
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
# Unused value preferences
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:none
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
# Index and Range preferences
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
# Indentation preferences
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents_when_block = true
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_square_brackets = false
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
# ReSharper/Rider custom properties (only honoured in .editorconfig, not .globalconfig)
|
||||
# Inspection severities
|
||||
resharper_class_never_instantiated_global_highlighting = none
|
||||
resharper_class_never_instantiated_local_highlighting = none
|
||||
resharper_convert_to_primary_constructor_highlighting = none
|
||||
resharper_cognitive_complexity_threshold = 20
|
||||
# Blank lines
|
||||
resharper_blank_lines_around_region = 0
|
||||
resharper_blank_lines_before_single_line_comment = 1
|
||||
# Line breaks
|
||||
resharper_new_line_before_while = true
|
||||
resharper_place_accessor_attribute_on_same_line = never
|
||||
resharper_place_accessorholder_attribute_on_same_line = never
|
||||
resharper_place_field_attribute_on_same_line = never
|
||||
resharper_wrap_before_binary_opsign = true
|
||||
resharper_wrap_chained_method_calls = chop_if_long
|
||||
resharper_wrap_linq_expressions = chop_always
|
||||
resharper_wrap_ternary_expr_style = chop_always
|
||||
# Spaces, alignment and usings
|
||||
resharper_space_within_single_line_array_initializer_braces = true
|
||||
resharper_alignment_tab_fill_style = use_spaces
|
||||
resharper_qualified_using_at_nested_scope = true
|
||||
# Naming (consolidated here from DotSettings + .globalconfig so Roslyn and Rider both enforce).
|
||||
# Order matters: const and static-readonly fields are PascalCase and must be listed before
|
||||
# the private-field camelCase rule so they are matched first (mirrors ReSharper's groups).
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.symbols = static_readonly_fields
|
||||
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.static_readonly_fields.required_modifiers = static,readonly
|
||||
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning
|
||||
dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields
|
||||
dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_style
|
||||
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
|
||||
dotnet_naming_style.camel_case_style.capitalization = camel_case
|
||||
|
||||
[*.vb]
|
||||
# Modifier preferences
|
||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||
|
||||
[{*.Generated.*.cs,**/Migrations/*.cs,**/*ModelSnapshot.cs}]
|
||||
generated_code = true
|
||||
|
||||
[{tests,samples}/**/*.cs]
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
+3
-2
@@ -1,2 +1,3 @@
|
||||
Directory.Packages.props merge=union
|
||||
Directory.Packages.template.props merge=upstream-wins
|
||||
* text=auto
|
||||
*.png binary
|
||||
*.cs diff=csharp
|
||||
@@ -0,0 +1,19 @@
|
||||
Follow [gitmoji](https://gitmoji.dev) conventions. Use the **emoji character** (not the `:shortcode:`).
|
||||
|
||||
## Format
|
||||
|
||||
```
|
||||
<emoji> <imperative summary, third person>
|
||||
|
||||
<longer explanation of what and why, wrap at 72 chars>
|
||||
|
||||
<issue references, e.g. Closes #123, Fixes PROJ-456>
|
||||
```
|
||||
|
||||
- Subject line under 72 characters.
|
||||
- Third-person imperative mood ("Adds feature", not "Add feature" or "Added feature").
|
||||
- Do not end the subject with a period.
|
||||
- Separate subject, body, and footer with blank lines.
|
||||
- Body and footer are **optional** — only include when the subject alone isn't self-explanatory or when issues need linking.
|
||||
- Body explains **what** changed and **why**.
|
||||
- Footer links related issues (GitHub `Closes #N` / Jira `PROJ-N`).
|
||||
@@ -0,0 +1,21 @@
|
||||
# Project Guidelines
|
||||
|
||||
You are a senior software engineer and solution architect. Prioritize correctness, maintainability, and simplicity. Evaluate trade-offs before proposing solutions; prefer the least complex approach that meets requirements.
|
||||
|
||||
## 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.
|
||||
- When ambiguous or multiple approaches exist, **ask clarifying questions with 2–4 predefined options** (include trade-offs). Only proceed silently when intent is obvious.
|
||||
- Briefly explain every suggestion: what it does, why, and how it fits the architecture.
|
||||
- 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.
|
||||
|
||||
## Build & test
|
||||
|
||||
- Always build and test the entire solution (pass the solution slnx as a parameter to `dotnet build` and `dotnet test`).
|
||||
|
||||
## Documentation
|
||||
|
||||
- When a project has a `README.md`, treat it as part of the implementation: any rename or restructure that touches type names, configuration sections, or project references must update the affected `README.md` **in the same commit**.
|
||||
- Before opening a PR, scan each touched project's existing `README.md` (if any) for stale references.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
description: "Use when editing files under docs/adr/. Enforces ADR immutability and supersession rules."
|
||||
applyTo: "docs/adr/**"
|
||||
---
|
||||
# ADR Editing Rules
|
||||
|
||||
ADRs are append-only history. To create a new ADR, use the `new-adr` skill.
|
||||
|
||||
- **Accepted ADRs are immutable.** Do not change `Context`, `Decision Drivers`, `Considered Options`, or `Decision Outcome` after acceptance — write a new ADR that supersedes instead.
|
||||
- **Permitted edits to accepted ADRs**: typo/grammar fixes, adding links to related ADRs, and updating the `status` field as part of supersession or deprecation.
|
||||
- **Superseding**: set the predecessor's frontmatter to `status: superseded by ADR-NNNN`, update its `date`, and add a back-link in its body. Never delete the predecessor.
|
||||
- **Filenames are permanent.** Do not renumber or rename existing ADRs even if numbers have gaps.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
description: "Use when editing analyzer or code-style configuration: .editorconfig, .globalconfig, stylecop.json, .DotSettings."
|
||||
applyTo: ".editorconfig,.globalconfig,stylecop.json,*.DotSettings"
|
||||
---
|
||||
# Analyzer & Style Configuration
|
||||
|
||||
These files are the source of truth for code style and analyzer severity. Changes have wide blast radius.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Ask for explicit permission before editing.** State the rule(s), the new severity, and the rationale; wait for approval. Do not touch these files as part of unrelated work. The stated rationale belongs in the PR description.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Do not relax a rule to silence a single occurrence.** Fix the code, or suppress narrowly with a justification comment in the offending file.
|
||||
- New rules start at the **strictest justifiable severity**; downgrade later only with evidence.
|
||||
- Never disable an entire analyzer category.
|
||||
|
||||
## File Roles
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `.editorconfig` | Per-language formatting, naming rules, and analyzer severities scoped by file glob. |
|
||||
| `.globalconfig` | Repo-wide analyzer severities not needing file-pattern scoping. |
|
||||
| `stylecop.json` | StyleCop-specific settings (file headers, ordering, documentation). |
|
||||
| `Solution.sln.DotSettings` | ReSharper/Rider mirror — **derived**, follows the others; do not lead changes here. |
|
||||
|
||||
## Coordination
|
||||
|
||||
- **A rule has one source of truth** across `.editorconfig`, `.globalconfig`, and `stylecop.json` — never duplicate.
|
||||
- When a rule must appear in multiple files for IDE parity (e.g., mirrored to `Solution.sln.DotSettings`), severities **must match exactly**. Drift is a bug.
|
||||
- StyleCop rules: configuration in `stylecop.json`, severity in `.editorconfig`.
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
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 endpoint group requires action filters, model binding customisation, or exceeds 10 endpoints sharing significant cross-cutting logic.
|
||||
- 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()`.
|
||||
|
||||
## Authorisation
|
||||
|
||||
- Apply `.RequireAuthorization()` at the group level for protected resources.
|
||||
- Reference policy names through `const string` fields on a shared static class (e.g., `.RequireAuthorization(AuthorizationPolicies.RequireAdminRole)`) instead of inline string literals.
|
||||
- Mark public endpoints explicitly with `.AllowAnonymous()`.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
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 all `public` and `protected` members in `src/` (suppressed in `tests/` and `samples/`).
|
||||
- Avoid `null!` (null-forgiving operator) — fix the nullability flow instead of suppressing the warning.
|
||||
|
||||
## Comments
|
||||
|
||||
Applies to `src/`. Tests keep the guidance in the testing instructions.
|
||||
|
||||
- **No comments inside member bodies.** Anything worth saying goes in an XML doc comment on the member that says it.
|
||||
- Document `private` and `internal` members — the analyzers only *require* docs on exposed elements, they do not object to more. Put the "why" in `<remarks>`.
|
||||
- If the explanation is about one statement rather than the whole member, extract that statement into a named private method and document that instead.
|
||||
- For logic inside lambdas or local functions where an XML doc comment is not applicable, extract the logic into a named private method and document that method instead.
|
||||
- Never state what the code already shows, and never narrate history — a past bug, a change, or an answer to a reviewer belongs in the commit message, where it cannot go stale.
|
||||
- Sole exception: the justification required beside a narrow analyzer suppression, which has nowhere else to live.
|
||||
|
||||
## 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(…)`). Do not make existing types partial without explicit user sign-off. Do not fall back to inline logging.
|
||||
- If a type cannot be made partial (no user sign-off obtained), do not modify its logging calls — surface the conflict to the user and wait for instruction rather than silently falling back to inline logging. Present at least these options: (1) make the type `partial`; (2) move the `LoggerMessage` methods to a separate type — group reusable messages in one shared class, and keep the rest in a per-type class.
|
||||
@@ -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 <parent> AS <name>`.
|
||||
|
||||
## Build Args
|
||||
|
||||
- `RUNTIME_BASE`: `web` (ASP.NET), `app` (console), or `self-contained`. If a use case does not match `web`, `app`, or `self-contained`, do not invent a new `RUNTIME_BASE` value — flag this to the user and ask which existing value best fits their scenario.
|
||||
- `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. If omitted, the migration bundle step must not pass `--context`; EF Core auto-discovers the single DbContext in `DATABASE_PROJECT`.
|
||||
|
||||
## 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` |
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
description: "README.md authoring: structure, callouts, diagrams, and assets."
|
||||
applyTo: "**/README.md"
|
||||
---
|
||||
# README Style
|
||||
|
||||
## Structure
|
||||
|
||||
Follow this ordering when sections apply; omit those that don't:
|
||||
|
||||
1. **Title** (`# <ProjectName>`) and one-sentence purpose.
|
||||
1. **Getting Started** — minimal commands to consume or run the project.
|
||||
1. **Features** / **Capabilities** — bulleted highlights.
|
||||
1. **Configuration** — build args, environment variables, MSBuild properties.
|
||||
1. **Usage Examples** — code or CLI snippets.
|
||||
1. **Recommendations** / **Caveats** — known limitations, in callouts.
|
||||
1. **Reference link definitions** at the bottom.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Do not hard-wrap prose; one paragraph per line.
|
||||
- Ordered lists: write every item as `1.` and let the renderer auto-number.
|
||||
- Use reference-style links for external URLs in prose, with definitions at the bottom of the file. Inline links are fine in tables and for in-repo relative paths.
|
||||
- Keep code examples minimal — strip boilerplate unless it is the point.
|
||||
|
||||
## Callouts
|
||||
|
||||
Use [GitHub-flavoured alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) for emphasis; reserve plain `>` blockquotes for quoted text.
|
||||
|
||||
| Alert | Use for |
|
||||
| --- | --- |
|
||||
| `> [!NOTE]` | Neutral context the reader should know |
|
||||
| `> [!TIP]` | Optional best-practice |
|
||||
| `> [!IMPORTANT]` | Required for correct usage |
|
||||
| `> [!WARNING]` | Risk of breakage or data loss |
|
||||
| `> [!CAUTION]` | Security or destructive-action risk |
|
||||
|
||||
## Mermaid Diagrams
|
||||
|
||||
- Match diagram type to intent: `flowchart` (processes), `sequenceDiagram` (interactions over time), `classDiagram` (type relationships), `erDiagram` (data models).
|
||||
- Keep diagrams under ~15 nodes; split larger ones.
|
||||
- No custom styling — defaults render in both light and dark modes.
|
||||
|
||||
## Assets
|
||||
|
||||
- Repo-wide assets in `/docs/assets/`; project-scoped assets in the project's own `docs/assets/`.
|
||||
- Prefer **SVG** for diagrams and logos; PNG only for raster screenshots.
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
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 `Directory.Packages.props`, ensure the last child element of the root `<Project>` tag is `<Import Project="Directory.Packages.template.props" />`. `Directory.Packages.template.props` is **READ ONLY**, never edit it. The template provides default `<PackageVersion>` entries; any entry with the same `Include` declared earlier in `Directory.Packages.props` takes precedence over the template default, because MSBuild uses first-definition-wins semantics and the import appears last.
|
||||
|
||||
In `.csproj` files:
|
||||
|
||||
```xml
|
||||
<!-- Correct -->
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
|
||||
<!-- Wrong — never specify Version -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
```
|
||||
|
||||
To add or update a version, edit `Directory.Packages.props`:
|
||||
|
||||
```xml
|
||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
```
|
||||
|
||||
If the package does not exist in the template, simply add a new `<PackageVersion>` entry in `Directory.Packages.props`. No override logic is needed; the entry will be the sole definition.
|
||||
|
||||
## 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 `<InternalsVisibleToSuffix>` in the root props. Default suffixes are `.Tests` and `Tests`. To add custom visibility:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="MyProject.IntegrationTests" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
This is in addition to the automatically generated entries; do not remove or duplicate the default suffix entries.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
description: "Use when adding tracing, metrics, or feature flags: ActivitySource/Meter setup, semantic-convention tags, OTLP exporter config, OpenFeature client usage and flag-key naming."
|
||||
---
|
||||
# Observability & Feature Flags
|
||||
|
||||
## OpenTelemetry
|
||||
|
||||
- One **`ActivitySource`** per logical component, named `Kritikos.<Project>.<Component>`. Declare as `static readonly` on a dedicated `Telemetry` static class within the component (avoid scattering sources across every type).
|
||||
- One **`Meter`** per component, alongside its `ActivitySource`. The names **may** match when both belong to the same component.
|
||||
- Always wrap `StartActivity(...)` in a `using` — leaked activities skew downstream timings.
|
||||
- **Activity names**:
|
||||
- When [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/semconv/) cover the operation (HTTP, DB, messaging, RPC), follow semconv exactly (`HTTP GET`, `db.query`, `messaging.process`).
|
||||
- For custom operations not covered by semconv: `<Verb><Object>` in PascalCase (`ProcessOrder`, `LoadConfiguration`). Avoid generic verbs like `Handle` or `Execute`.
|
||||
- **Tags / attributes**: lowercase dotted, follow semantic conventions where they exist (`http.request.method`, `db.system`, `messaging.destination.name`). Project-specific tags use the `kritikos.<area>.<name>` namespace.
|
||||
- **Metric instruments**: snake_case names with units in the suffix (`orders_processed_total`, `request_duration_seconds`). Always set `unit` and `description` at creation.
|
||||
- **Resource attributes**: when a shared registration helper is added, it should populate `service.name` from `AssemblyName` and `service.version` from the GitVersion-computed `InformationalVersion`. Do not set them manually at call sites.
|
||||
- **Exporter**: OTLP over gRPC, endpoint via `OTEL_EXPORTER_OTLP_ENDPOINT`. Do not register console exporters in `Release` builds.
|
||||
- **Sampling**: parent-based with `TraceIdRatioBased` (1.0 in dev, configurable in prod). Do not set custom samplers without justification.
|
||||
|
||||
## OpenFeature
|
||||
|
||||
- Resolve flags through the configured OpenFeature client (obtained via DI, or `Api.Instance.GetClient()` if no DI integration is wired). Never call provider SDKs directly.
|
||||
- **Flag keys**: `kebab-case`, prefixed by domain (`orders-allow-partial-fulfilment`, `auth-require-mfa`). Keys are stable contracts — treat renames like API breaking changes.
|
||||
- Always pass `defaultValue:` — `client.GetBooleanValue("flag", defaultValue: false, context)`.
|
||||
- **Evaluation context** is built once per request via the configured context provider — do not assemble ad-hoc contexts inline.
|
||||
- Long-lived flags (kill switches, licensing) live in code; short-lived flags (rollouts, experiments) must have a removal date in their description and a tracking issue.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
description: "Use when adding, removing, or moving projects, or when editing the solution file or VS Code workspace."
|
||||
applyTo: "*.slnx,*.sln,Solution.code-workspace"
|
||||
---
|
||||
# Solution Conventions
|
||||
|
||||
`Solution.slnx` (XML) is the canonical solution format. The legacy `.sln` is not used; do not regenerate it.
|
||||
|
||||
## Adding & Removing Entries
|
||||
|
||||
Any project, configuration, or asset file added to, removed from, or renamed in a tracked location **must** be reflected in `Solution.slnx` in the same commit. New top-level folders also require an entry in `Solution.code-workspace`.
|
||||
|
||||
Place entries in the virtual folder matching the on-disk location:
|
||||
|
||||
| On-disk location | Virtual folder |
|
||||
| --- | --- |
|
||||
| `src/**`, `samples/**`, `tests/**` (projects + their `Directory.Build.props`) | `/src/`, `/samples/`, `/tests/` |
|
||||
| Repo-root config files (`.editorconfig`, `Directory.*.props`, `GitVersion.yml`, etc.) | `/Configuration/` |
|
||||
| Repo-root assets (`README.md`, `LICENSE.md`, `icon.png`, logos) | `/Assets/` |
|
||||
|
||||
Reserved virtual folder names must not be renamed.
|
||||
|
||||
## Renames
|
||||
|
||||
- Update the `.slnx` entry, the on-disk path, and any `ProjectReference` paths in dependent projects in the same commit.
|
||||
- Never leave dangling `<File Path="…" />` or project entries.
|
||||
|
||||
After any `.slnx` change, run `dotnet build` to confirm all referenced projects load.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
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: `<ProjectUnderTest>.Tests` (matches InternalsVisibleTo default).
|
||||
- Test classes: `<ClassUnderTest>Tests`.
|
||||
- Test methods: must use `MethodName_Condition_ExpectedResult` format. Example: `Add_NegativeNumbers_ThrowsArgumentException`.
|
||||
|
||||
## Structure
|
||||
|
||||
- Follow **Arrange / Act / Assert**.
|
||||
- Always propose a set of basic tests for new or changed code. A basic test set must include at minimum: one happy-path test, one test per documented edge case or boundary condition, and one test for each expected exception.
|
||||
- If no edge cases are explicitly documented, use judgment to identify and cover the most likely boundary conditions (e.g. null inputs, empty collections, zero/max values) and note the assumption in a comment.
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
description: "Audit .github/instructions/ and .github/skills/ for duplication, stale references, and unscoped or misscoped files."
|
||||
agent: "agent"
|
||||
---
|
||||
|
||||
# Audit Instructions & Skills
|
||||
|
||||
Walk every file under `.github/instructions/` and `.github/skills/` and produce a prioritised report of issues. Make no edits unless the user approves them after reading the report.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Inventory
|
||||
|
||||
List every `*.instructions.md` and every `**/SKILL.md`, plus `copilot-instructions.md` and any `*.prompt.md`.
|
||||
|
||||
### 2. Per-File Checks
|
||||
|
||||
For each instructions file:
|
||||
|
||||
- `applyTo` glob exists and matches actual files in the repo (no dead globs).
|
||||
- `description` accurately reflects the content (no stale TOCs, no over-promises).
|
||||
- Content is project-specific — flag anything restating universal Markdown / C# / formatter rules, or duplicating the agent runtime (e.g., "work in parallel", "save to memory").
|
||||
- File defers to enforcement layers (`.editorconfig`, analyzers, `Directory.Build.props`) instead of restating their rules.
|
||||
- Cross-references point at files that exist.
|
||||
|
||||
For each skill:
|
||||
|
||||
- Frontmatter `name` matches the directory name.
|
||||
- `description` starts with the action and includes "Use when:".
|
||||
- Procedure steps are actionable, not narration.
|
||||
- Commands actually work against this repo's tool versions and config files.
|
||||
- Cross-skill references use relative paths.
|
||||
|
||||
### 3. Cross-File Checks
|
||||
|
||||
- Same rule defined in multiple files with different wording or severity → flag.
|
||||
- Topics not covered by any file but enforced via PR comments / convention → flag as candidate new file.
|
||||
- `applyTo` overlap that could cause conflicting guidance on a single file → flag.
|
||||
|
||||
### 4. Report
|
||||
|
||||
Produce a table sorted by priority (high / medium / low):
|
||||
|
||||
| Priority | File | Issue | Suggested fix |
|
||||
|
||||
End with two summary counts: total files inspected, total issues found.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Read-only by default. Apply fixes only if the user explicitly approves them.
|
||||
- When suggesting a fix, quote the exact lines to change so the user can decide without re-reading the file.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
description: "Convert an MVC controller to Minimal API endpoints following the project's ASP.NET conventions."
|
||||
agent: "agent"
|
||||
---
|
||||
|
||||
# Convert Controller to Minimal API
|
||||
|
||||
Refactor an MVC controller into Minimal API endpoints aligned with [aspnet-api.instructions.md](../instructions/aspnet-api.instructions.md).
|
||||
|
||||
## Input
|
||||
|
||||
Path to the controller file (e.g., `src/MyApi/Controllers/OrdersController.cs`). Ask if not provided.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Analyse the Controller
|
||||
|
||||
For each action method, capture:
|
||||
|
||||
- HTTP verb and route template.
|
||||
- Parameter binding sources (`[FromRoute]`, `[FromQuery]`, `[FromBody]`, `[FromServices]`).
|
||||
- Return type and possible status codes.
|
||||
- Filters / attributes (`[Authorize]`, `[ProducesResponseType]`, etc.).
|
||||
- Async signature and `CancellationToken` usage.
|
||||
|
||||
### 2. Produce a Migration Plan
|
||||
|
||||
Present a table to the user before writing any code:
|
||||
|
||||
| Action | New endpoint | Notes (auth, filters, response types) |
|
||||
|
||||
Ask for confirmation or adjustments.
|
||||
|
||||
### 3. Generate the Endpoint File
|
||||
|
||||
Create `<Resource>Endpoints.cs` next to the controller, following the conventions:
|
||||
|
||||
- Static class with a `MapXEndpoints(this IEndpointRouteBuilder)` extension.
|
||||
- `MapGroup("/api/v{apiVersion}/{resource}")` with shared `.WithTags(...)`.
|
||||
- Use **`TypedResults`** for every return path.
|
||||
- Use **`[AsParameters]`** for complex parameter sets.
|
||||
- Use **`CancellationToken`** on every async lambda.
|
||||
- Errors return **`ProblemDetails`** via `TypedResults.Problem(...)`.
|
||||
- Endpoint lambdas stay thin; delegate to injected services.
|
||||
|
||||
### 4. Wire Up Registration
|
||||
|
||||
In `Program.cs`, replace `MapControllers()` (or supplement it) with the new `MapXEndpoints()` call. If the controller was the last one, also remove `AddControllers()`.
|
||||
|
||||
### 5. Migrate Tests
|
||||
|
||||
For each test targeting the controller, port it to use `WebApplicationFactory` against the new endpoint. Keep the file name and test names; update the arrange/act sections only.
|
||||
|
||||
### 6. Delete the Controller
|
||||
|
||||
Only after the new endpoints + tests pass. Run `dotnet build` and `dotnet test` before deletion.
|
||||
|
||||
### 7. Update README
|
||||
|
||||
If the project's `README.md` documented the old controller routes, update it in the same commit (per [copilot-instructions.md](../copilot-instructions.md)).
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Do not change behaviour while converting — refactor first, behaviour changes go in a separate commit.
|
||||
- If a controller uses filters or model binders without a Minimal API equivalent, surface the gap and ask before inventing one.
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
description: "Convert inline logger calls to LoggerMessage-generated partial methods, per the C# conventions."
|
||||
agent: "agent"
|
||||
---
|
||||
|
||||
# Refactor to LoggerMessage
|
||||
|
||||
Replace inline `logger.LogX(...)` calls with `LoggerMessage`-generated `partial` methods, as required by [csharp.instructions.md](../instructions/csharp.instructions.md).
|
||||
|
||||
## Input
|
||||
|
||||
A file or project path. If unspecified, default to the active editor file.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Inventory
|
||||
|
||||
Find every `ILogger.Log*(...)` call site (`LogTrace`, `LogDebug`, `LogInformation`, `LogWarning`, `LogError`, `LogCritical`). Group by containing class.
|
||||
|
||||
### 2. Plan
|
||||
|
||||
For each class with hits, present a table before editing:
|
||||
|
||||
| Method | Level | EventId | Message template | Args |
|
||||
|
||||
Pick stable, sequential `EventId` values per class (start at 1 within the class). Confirm with the user when message templates need to be reworded for clarity.
|
||||
|
||||
### 3. Convert the Class
|
||||
|
||||
- Mark the class `partial`.
|
||||
- Add a nested `static partial class Log` (or top-level partial methods, per existing project style — match what's already in the codebase).
|
||||
- For each call site, generate:
|
||||
```csharp
|
||||
[LoggerMessage(EventId = N, Level = LogLevel.X, Message = "...")]
|
||||
static partial void <Verb><Subject>(ILogger logger, <args>);
|
||||
```
|
||||
- Replace each call site with the new method invocation.
|
||||
- Method names: `PascalCase`, verb-led, third-person (`OrderProcessed`, not `LogOrderProcessed` or `OrderWasProcessed`).
|
||||
|
||||
### 4. Handle Edge Cases
|
||||
|
||||
- **Exceptions**: `Exception` parameter goes first; do not include it in the message template.
|
||||
- **Scopes** (`logger.BeginScope`): leave alone — `LoggerMessage` does not replace scopes.
|
||||
- **Conditional logging** (`if (logger.IsEnabled(...))`): drop the guard; the generated method already checks.
|
||||
|
||||
### 5. Verify
|
||||
|
||||
```bash
|
||||
dotnet build
|
||||
```
|
||||
|
||||
Source generators must produce zero warnings. If a `[LoggerMessage]` definition is rejected, surface the analyzer message and the offending method.
|
||||
|
||||
### 6. Run Tests
|
||||
|
||||
```bash
|
||||
dotnet test
|
||||
```
|
||||
|
||||
Logger-based assertions in tests usually keep working, but message-template changes can break string-matching tests — flag any test failures explicitly.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Do not change log levels while refactoring — that's a separate decision.
|
||||
- Do not invent new log statements; only convert what already exists.
|
||||
- If a class is `sealed` and has external partial declarations, surface the conflict before adding `partial` to the type.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
description: "Create a temporary markdown file summarising all branch changes vs. main/master for PR review"
|
||||
agent: "agent"
|
||||
---
|
||||
|
||||
# Summarize Branch Changes for PR
|
||||
|
||||
Create a temporary markdown file in the repository root called `BRANCH_CHANGES.md` that summarises all changes on the current branch compared to the master/main/default branch. This file is intended to aid PR review and should be deleted before merging.
|
||||
|
||||
## Input
|
||||
|
||||
No explicit input required — the summary is derived from git history and the current Copilot session context.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Gather Git Context
|
||||
|
||||
- Determine the current branch name (`git branch --show-current`).
|
||||
- Find the merge base with `origin/master`.
|
||||
- Collect the commit log between the merge base and HEAD (`git log --oneline`).
|
||||
- Collect the diff stat (`git diff --stat`), file change list (`--diff-filter`), and numstat.
|
||||
|
||||
### 2. Distil Session Requests (if applicable)
|
||||
|
||||
If Copilot session context is available, extract the key user requests that drove the changes and present them as a bullet-point list at the top of the file under a **Copilot Session Requests** heading.
|
||||
|
||||
### 3. Produce the Markdown File
|
||||
|
||||
Create `BRANCH_CHANGES.md` in the repository root with the following sections:
|
||||
|
||||
#### Header
|
||||
|
||||
- Branch name, base branch, and current date.
|
||||
|
||||
#### Copilot Session Requests
|
||||
|
||||
- Bullet list of the user prompts/requests from the current session that led to changes (omit if no session context).
|
||||
|
||||
#### Commits
|
||||
|
||||
- Table with columns: Hash, Message, Author, Date.
|
||||
|
||||
#### Files Changed
|
||||
|
||||
- Table with columns: File, Change type (Added/Modified/Deleted), Insertions, Deletions.
|
||||
- Summary line: _N files changed, X insertions, Y deletions._
|
||||
|
||||
#### Change Details
|
||||
|
||||
- One sub-heading per changed file (or logical group of related files).
|
||||
- Numbered list of the distinct changes made, written for a reviewer who is unfamiliar with the recent context.
|
||||
|
||||
#### Pending Session Work (if applicable)
|
||||
|
||||
- Any in-progress or uncommitted work from the current Copilot session that has not yet been committed.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Keep descriptions concise but specific — a reviewer should understand _what_ changed and _why_ without reading the diff.
|
||||
- Use git data as the source of truth; supplement with session context only for intent and motivation.
|
||||
- Do not include the full diff content — summarise it.
|
||||
- Mark the file clearly as temporary (mention it should be deleted before merge).
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
description: "Sync the current repository with upstream changes from kritikos-io/templates-dotnet, with conflict-resolution guidance."
|
||||
agent: "agent"
|
||||
---
|
||||
|
||||
# Sync From Template
|
||||
|
||||
Pull updates from the upstream `kritikos-io/templates-dotnet` repository, following the workflow documented in the root `README.md`.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Verify the `template` Remote
|
||||
|
||||
```bash
|
||||
git remote -v | grep template
|
||||
```
|
||||
|
||||
If absent, add it and warn the user that the first sync requires `--allow-unrelated-histories`:
|
||||
|
||||
```bash
|
||||
git remote add template https://github.com/kritikos-io/templates-dotnet
|
||||
git fetch --all
|
||||
```
|
||||
|
||||
### 2. Check Working Tree
|
||||
|
||||
`git status` must be clean. If it isn't, stop and ask the user to commit or stash before continuing.
|
||||
|
||||
### 3. Pull
|
||||
|
||||
- **First-time sync** (template remote was just added):
|
||||
```bash
|
||||
git merge template/main --allow-unrelated-histories
|
||||
```
|
||||
- **Subsequent syncs**:
|
||||
```bash
|
||||
git pull template main
|
||||
```
|
||||
|
||||
### 4. Resolve Conflicts
|
||||
|
||||
Common conflict zones, with resolution guidance:
|
||||
|
||||
| File / area | Strategy |
|
||||
| --- | --- |
|
||||
| `Solution.slnx`, `Solution.code-workspace` | Keep local — these are project-specific. Re-apply any new entries from upstream manually. |
|
||||
| `Directory.Build.props`, `Directory.Build.targets`, `Directory.Packages.props` | Take upstream as the base, re-apply project-specific overrides on top. |
|
||||
| `.github/instructions/`, `.github/skills/`, `.github/prompts/` | Take upstream; project-specific guidance should live in a *separate* file, never as edits to template-shipped files. |
|
||||
| `README.md`, `LICENSE.md`, `icon.png` | Keep local — these are project-specific. |
|
||||
| `docker/Dockerfile` | Take upstream; project-specific compose lives in `docker/compose.*.yaml`. |
|
||||
|
||||
When a conflict needs human judgement, list it with file path + decision options rather than guessing.
|
||||
|
||||
### 5. Verify
|
||||
|
||||
```bash
|
||||
dotnet restore --force-evaluate
|
||||
dotnet build
|
||||
dotnet test
|
||||
```
|
||||
|
||||
If lock files moved, commit `packages.lock.json` updates.
|
||||
|
||||
### 6. Commit
|
||||
|
||||
Use a single merge commit; do not squash. Subject:
|
||||
|
||||
```
|
||||
🔀 Syncs with kritikos-io/templates-dotnet
|
||||
```
|
||||
|
||||
(Per [copilot-commit-message-instructions.md](../copilot-commit-message-instructions.md): gitmoji, third-person imperative.)
|
||||
@@ -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=<ProjectName>
|
||||
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=<ProjectName> \
|
||||
-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=<StartupProject> \
|
||||
--build-arg DATABASE_PROJECT=<EFProject> \
|
||||
--build-arg DBCONTEXT=<OptionalContextName> \
|
||||
-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/`.
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: ef-migration
|
||||
description: 'Add, remove, or apply Entity Framework Core migrations against a project under src/. Use when: creating a new migration, reverting the last migration, generating a migration bundle, applying migrations to a database.'
|
||||
argument-hint: 'Describe the schema change (e.g., "add audit columns to Orders") and the target DbContext if multiple exist'
|
||||
---
|
||||
|
||||
# EF Core Migration
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The EF project (containing the `DbContext`) is referenced by — or is itself — a runnable startup project.
|
||||
- Requires `dotnet ef`: `dotnet tool restore` (if a manifest exists) or `dotnet tool install -g dotnet-ef`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Migration name**: `PascalCase`, imperative, third person, ≤ 60 chars (e.g., `AddsOrderAuditColumns`, not `audit_changes` or `Migration1`).
|
||||
- **Output folder**: default `Migrations/` inside the EF project. Do not relocate.
|
||||
- **One DbContext per command** — pass `--context` whenever the project has more than one.
|
||||
- **Never edit a migration after it has been applied to a shared environment.** Add a corrective migration instead.
|
||||
- **Commit migrations together with the model change** that produced them.
|
||||
|
||||
## Procedure
|
||||
|
||||
The **EF project** contains the `DbContext`; the **startup project** is the runnable project EF executes against (usually a web/worker referencing the EF project).
|
||||
|
||||
1. **Add the migration:**
|
||||
```bash
|
||||
dotnet ef migrations add <PascalCaseName> \
|
||||
--project src/<EFProject> \
|
||||
--startup-project src/<StartupProject> \
|
||||
--context <DbContextName>
|
||||
```
|
||||
2. **Inspect** the generated `*.cs` and `*.Designer.cs` files for unintended changes (unrelated columns, dropped indices).
|
||||
3. **If wrong, remove before committing:**
|
||||
```bash
|
||||
dotnet ef migrations remove --project src/<EFProject> --startup-project src/<StartupProject>
|
||||
```
|
||||
|
||||
For a deployable migration binary, use the [docker-build](../docker-build/SKILL.md) skill's `migrations` target.
|
||||
|
||||
## Generating an SQL Script
|
||||
|
||||
```bash
|
||||
dotnet ef migrations script <FromMigration> <ToMigration> \
|
||||
--project src/<EFProject> \
|
||||
--startup-project src/<StartupProject> \
|
||||
--idempotent \
|
||||
--output artifacts/migrations.sql
|
||||
```
|
||||
|
||||
Use `--idempotent` whenever the script may run against a database in an unknown state.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **"Unable to create a 'DbContext'…"** — pass `--startup-project` explicitly; auto-detection picks the wrong project in multi-project solutions.
|
||||
- **Multiple DbContexts found** — pass `--context <Name>`.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
status: proposed
|
||||
date: YYYY-MM-DD
|
||||
decision-makers: [names or roles]
|
||||
# consulted: [subject-matter experts with two-way communication]
|
||||
# informed: [stakeholders kept up-to-date with one-way communication]
|
||||
---
|
||||
# NNNN. {short title, representative of solved problem and found solution}
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
{Describe the context and problem statement in two to three sentences, or as a short illustrative story. Articulate the problem as a question if helpful, and link to issues, PRs, or discussions.}
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- {decision driver 1, e.g., a force, facing concern, quality attribute}
|
||||
- {decision driver 2}
|
||||
- …
|
||||
|
||||
## Considered Options
|
||||
|
||||
- {title of option 1}
|
||||
- {title of option 2}
|
||||
- {title of option 3}
|
||||
- …
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Chosen option: **"{title of option X}"**, because {justification — e.g., the only option meeting a k.o. criterion, resolves driver {driver}, or comes out best in the comparison below}.
|
||||
|
||||
### Consequences
|
||||
|
||||
- Good, because {positive consequence}.
|
||||
- Bad, because {negative consequence / accepted trade-off}.
|
||||
- Follow-up: {action item, if any}.
|
||||
|
||||
<!-- Optional. Remove if not used. -->
|
||||
### Confirmation
|
||||
|
||||
{How will compliance with this ADR be confirmed? E.g., a design/code review, an ArchUnit test, a CI check, or a follow-up audit.}
|
||||
|
||||
<!-- Optional. Remove if not used. -->
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Comparison
|
||||
|
||||
| Driver | {Option 1} | {Option 2} | {Option 3} |
|
||||
| ---------- | ---------- | ---------- | ---------- |
|
||||
| {driver 1} | Yes | Maybe | No |
|
||||
| {driver 2} | No | Yes | Yes |
|
||||
|
||||
### {title of option 1}
|
||||
|
||||
{example | description | pointer to more information}
|
||||
|
||||
- Good, because {argument}.
|
||||
- Neutral, because {argument}.
|
||||
- Bad, because {argument}.
|
||||
|
||||
### {title of option 2}
|
||||
|
||||
{example | description | pointer to more information}
|
||||
|
||||
- Good, because {argument}.
|
||||
- Bad, because {argument}.
|
||||
|
||||
<!-- Optional. Remove if not used. -->
|
||||
## More Information
|
||||
|
||||
{Additional evidence/confidence for the decision, team agreement notes, when/how to revisit, links to related ADRs and resources.}
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: new-adr
|
||||
description: 'Create a new Architectural Decision Record (ADR) capturing a justified design choice that addresses an architecturally significant functional or non-functional requirement. Use when: creating a new ADR, documenting an architectural choice, recording a design decision, superseding a previous decision, capturing rationale for a framework/library/pattern selection.'
|
||||
argument-hint: 'Brief description of the decision (e.g., "adopt Minimal APIs over MVC controllers")'
|
||||
---
|
||||
# New Architectural Decision Record
|
||||
|
||||
You are assisting the user in producing a well-structured, comprehensive ADR. Ask clarifying questions, offer suggestions and examples, and ensure the record follows the standard format with all required sections filled.
|
||||
|
||||
Reference: <https://adr.github.io>.
|
||||
|
||||
## When to Use
|
||||
|
||||
- A design choice has a **measurable effect on architecture or quality attributes** (security, performance, scalability, maintainability, cost, DX).
|
||||
- The decision is **non-obvious**, has **trade-offs**, or **supersedes** prior guidance.
|
||||
- A reviewer or future maintainer would reasonably ask *"why did we do it this way?"*.
|
||||
|
||||
**Not an ADR**: cosmetic refactors, bug fixes, style preferences, or decisions with no viable alternatives. Use a commit message or code comment instead.
|
||||
|
||||
## Location & Filename
|
||||
|
||||
- **Folder**: `docs/adr/`
|
||||
- **Filename**: `NNNN-kebab-title.md` (zero-padded, four digits, sequential; e.g., `0001-use-minimal-apis.md`).
|
||||
- **Index** (optional): `docs/adr/README.md` listing all ADRs with status.
|
||||
|
||||
Determine the next `NNNN` by scanning existing files and incrementing the highest number. Never reuse numbers, even for retracted ADRs.
|
||||
|
||||
## Required Sections
|
||||
|
||||
Every ADR must include these MADR sections, in order:
|
||||
|
||||
1. **Context and Problem Statement** — the situation, goals, and constraints that frame the decision; pose the problem as a question when helpful.
|
||||
2. **Decision Drivers** — bullet list of forces, concerns, and quality attributes that matter.
|
||||
3. **Considered Options** — bullet list of viable alternatives.
|
||||
4. **Decision Outcome** — the chosen option, justification, nested **Consequences** (good/bad/follow-up), and optional **Confirmation** subsection.
|
||||
|
||||
MADR-optional sections (include when they add value):
|
||||
|
||||
- **Pros and Cons of the Options** — per-option Good/Neutral/Bad bullets. Prefix with the comparison table below when more than two options compete.
|
||||
- **More Information** — links, revisit notes, related ADRs.
|
||||
|
||||
### Comparison Table (project addition)
|
||||
|
||||
When using **Pros and Cons of the Options**, lead with a Yes/No/Maybe matrix scoring each option against each driver. This is a project convention on top of MADR, not part of the spec, but it forces explicit comparison and exposes weak options.
|
||||
|
||||
```markdown
|
||||
| Driver | Option A | Option B | Option C |
|
||||
| ---------- | -------- | -------- | -------- |
|
||||
| {driver 1} | Yes | Maybe | No |
|
||||
| {driver 2} | No | Yes | Yes |
|
||||
```
|
||||
|
||||
## Status Lifecycle
|
||||
|
||||
`proposed` → `accepted` → (`deprecated` | `superseded by ADR-NNNN`) · `rejected` is terminal from `proposed`.
|
||||
|
||||
- New ADRs start as `proposed` unless the user confirms acceptance.
|
||||
- When superseding, **update the old ADR's status** to `superseded by ADR-NNNN` and add a back-link. Never rewrite history of an accepted ADR.
|
||||
- `date` reflects the last status change.
|
||||
|
||||
## MADR Template
|
||||
|
||||
This skill follows the [MADR 4.0](https://adr.github.io/madr/) (Markdown Any Decision Records) format with one project addition (see *Comparison Table* above). Copy [MADR.md](MADR.md) to `docs/adr/NNNN-kebab-title.md` and fill in the placeholders. The folder `docs/adr/` is a project convention; MADR's own recommendation is `docs/decisions/`.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Confirm significance.** Check against the "When to Use" criteria. If it doesn't qualify, suggest a lighter alternative (code comment, PR description, CHANGELOG entry) and stop.
|
||||
2. **Assign a number.** List `docs/adr/` and pick the next `NNNN`.
|
||||
3. **Interview the user — one section at a time.** Ask focused questions for the current section, offer suggestions/examples where useful, then move on. Do not dump every question at once.
|
||||
4. **Show progress** after each section using a status table with these markers:
|
||||
- ✔️ completed
|
||||
- 🟡 in progress
|
||||
- ➖ not started
|
||||
|
||||
```markdown
|
||||
| Section | Status |
|
||||
| ----------------------------- | ------ |
|
||||
| Context and Problem Statement | ✔️ |
|
||||
| Decision Drivers | 🟡 |
|
||||
| Considered Options | ➖ |
|
||||
| Decision Outcome | ➖ |
|
||||
| Consequences | ➖ |
|
||||
| Pros and Cons (optional) | ➖ |
|
||||
```
|
||||
|
||||
5. **Draft the ADR** as sections are filled. Keep the title short and decision-oriented (`"Use X for Y"` or `"Adopt X over Y"`). Status = `proposed` unless the user confirms acceptance.
|
||||
6. **Handle supersession.** If this ADR replaces another, update the predecessor's frontmatter `status` to `superseded by ADR-NNNN` and add a cross-link in its body.
|
||||
7. **Update index.** If `docs/adr/README.md` exists, append the new entry with status; otherwise offer to create one.
|
||||
8. **Save & confirm.** Write the file to `docs/adr/NNNN-kebab-title.md`, validate frontmatter YAML and filename, and confirm the saved path back to the user.
|
||||
|
||||
## Quality Bar
|
||||
|
||||
- **Title states the decision**, not the topic. ✅ `Use Minimal APIs for new endpoints` · ❌ `API style`.
|
||||
- **Options are real alternatives** that were genuinely considered — not strawmen.
|
||||
- **Consequences include the downsides** we accept. An ADR with only positives is incomplete.
|
||||
- **Links > prose** for context already documented elsewhere (issues, RFCs, benchmarks).
|
||||
- **Immutable once accepted.** Further changes happen via a new ADR that supersedes.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: new-release
|
||||
description: 'Cut a new release: tag the appropriate commit, generate the changelog, and produce build artifacts. Use when: releasing a new version, tagging a release, publishing NuGet packages, preparing release artifacts.'
|
||||
argument-hint: 'Branch or commit to release from (default: current main) and the kind of release (stable, prerelease, hotfix)'
|
||||
---
|
||||
|
||||
# New Release
|
||||
|
||||
Versioning is **GitVersion-driven** (`GitVersion.yml`). Do not hand-pick versions — let GitVersion compute them from branch and tag history.
|
||||
|
||||
## Versioning
|
||||
|
||||
The root `GitVersion.yml` uses the `TrunkBased/preview1` workflow. Its `Mainline` strategy is what makes stable branches increment per commit rather than holding one version until the next tag.
|
||||
|
||||
| Branch | Label | Resulting version |
|
||||
| --- | --- | --- |
|
||||
| `main`, `release/*` | — | `MAJOR.MINOR.PATCH` (stable; **patch increments on every commit**) |
|
||||
| `feature/*` | branch name | `…-<branch>.N` (pre-release; merging bumps **minor**) |
|
||||
| `hotfix/*` | branch name | `…-<branch>.N` (pre-release; merging bumps **patch**) |
|
||||
|
||||
- Tags use the prefix `v` (regex `[vV]?` also accepts `V` or none).
|
||||
- Each `src/*` project is versioned **independently**: only changes under that project's own directory, `Directory.Packages.props`, or `GitVersion.yml` increment its version (see `GenerateProjectGitVersionConfig` in `src/Directory.Build.props`, which renders the root `GitVersion.yml` into a per-project copy). A change to one library does not bump a sibling's version.
|
||||
- All five packages still share one tag namespace, so a tag rebases every project's `MAJOR.MINOR` at once. Only the patch/pre-release counters diverge per project.
|
||||
- Increments are suppressed on already-tagged commits, so the tagged commit builds exactly the tagged version.
|
||||
|
||||
## Changelog
|
||||
|
||||
Release notes are generated at pack time and embedded as `PackageReleaseNotes`. The `GenerateChangelogFromGit` target writes `git log` subjects to `$(ChangeLogPath)`, and `CreateReleaseNotesFromFile` reads that file into the nuspec. Both run before `GenerateNuspec`, and generation is skipped when the repository root has no `.git` directory.
|
||||
|
||||
The commit range is resolved in this order, first match wins:
|
||||
|
||||
1. `$(ChangeLogFromRef)..HEAD` — when `ChangeLogFromRef` is set.
|
||||
2. `<tag>..HEAD` — from `git describe --tags --abbrev=0`.
|
||||
3. `<sha>..HEAD` — the last first-parent merge whose subject matches `$(ChangeLogUpstreamMergePattern)`.
|
||||
4. `<sha>..HEAD` — the last first-parent merge.
|
||||
5. No range; the log is capped at `$(ChangeLogCommitLimit)` commits.
|
||||
|
||||
Commits are filtered with `--no-merges` and scoped to the project's own directory, plus the directories of its `ProjectReference`s unless `ChangeLogIncludeReferences` is `false`. This mirrors the per-project versioning rule above: a sibling library's commits do not appear in this package's notes.
|
||||
|
||||
| Property | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `ChangeLogFromRef` | *(empty)* | Explicit range start; overrides the fallback chain |
|
||||
| `ChangeLogPath` | `$(BaseIntermediateOutputPath)changes.log` | Where the log is written and read from |
|
||||
| `ChangeLogCommitLimit` | `100` | Cap used only when no range resolves |
|
||||
| `ChangeLogUpstreamMergePattern` | `from .*/` | Subject matcher for the upstream-merge fallback |
|
||||
| `ChangeLogIncludeReferences` | `true` | Include `ProjectReference` directories in scoping |
|
||||
| `GenerateChangelogFromGit` | *(unset)* | Set to `false` to skip generation entirely |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Step 2 uses the **nearest reachable** tag, not the newest. Packing from a commit that already carries a release tag therefore yields only the commits added since it — often a single entry. Pass `ChangeLogFromRef` to widen the range:
|
||||
>
|
||||
> ```shell
|
||||
> dotnet pack -c Release -p:ChangeLogFromRef=v4.0.0
|
||||
> ```
|
||||
|
||||
To ship hand-written notes instead, disable generation and point `ChangeLogPath` at your own file. Generation must be disabled, otherwise the target overwrites it:
|
||||
|
||||
```shell
|
||||
dotnet pack -c Release -p:GenerateChangelogFromGit=false -p:ChangeLogPath=notes.md
|
||||
```
|
||||
|
||||
Confirm the resolved range in the build output before publishing:
|
||||
|
||||
```text
|
||||
Generated changelog at <path> using range v4.0.0..HEAD.
|
||||
```
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Sync.** `git fetch --all --tags && git status` — confirm clean.
|
||||
2. **Compute.** `dotnet build src\<Project>\<Project>.csproj -getProperty:Version` — verify the value matches expectations for that specific project (there is no single repo-wide version).
|
||||
3. **Pack.** `dotnet pack -c Release` (or use the [docker-build](../docker-build/SKILL.md) `pack` target). Artifacts land in `artifacts/nuget/`. Check the logged changelog range and pass `ChangeLogFromRef` if it is narrower than the release warrants.
|
||||
4. **Tag.** `git tag -a v<version> -m "Release v<version>" && git push origin v<version>`.
|
||||
5. **Publish** packages to the configured feed.
|
||||
6. **Create the GitHub release** linked to the tag, using the generated changelog as notes.
|
||||
|
||||
For hotfixes: branch from the latest stable tag (`git switch -c hotfix/<name> v<latest-stable>`), follow the procedure, then merge back into `main`. There is no `develop` branch to reconcile with — this repo is trunk-based.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- **Tags are immutable** once pushed; never move or delete them.
|
||||
- **Never skip a `PATCH`** — ship a fix forward.
|
||||
- **Do not edit `GitVersion.yml` or a generated `src/*/GitVersion.yml` to force a version.** Address the underlying cause (missing tag, branch base, path filter, commit messages). Generated per-project files are gitignored build artifacts, re-derived from the root `GitVersion.yml` on every build — never hand-edit them.
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1156,3 +1156,9 @@ artifacts/**
|
||||
|
||||
## Machine-specific overrides
|
||||
compose.override.yaml
|
||||
|
||||
## Generated per-project GitVersion config (rendered from the root GitVersion.yml)
|
||||
src/*/GitVersion.yml
|
||||
|
||||
## Copilot agent scratch/research notes (ephemeral, not project documentation)
|
||||
.copilot-tracking/
|
||||
+2
-153
@@ -3,10 +3,11 @@ is_global = true
|
||||
# Ruleset replacement
|
||||
# C# compiler diagnostics
|
||||
dotnet_diagnostic.CS1573.severity = none # Missing XML comment for parameter
|
||||
dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member
|
||||
dotnet_diagnostic.CS1591.severity = suggestion # Missing XML comment for publicly visible type or member
|
||||
dotnet_diagnostic.CS2008.severity = none # No source files specified
|
||||
|
||||
# Code quality analyzers
|
||||
dotnet_diagnostic.CA1515.severity = suggestion # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1515
|
||||
dotnet_diagnostic.CA1303.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1303
|
||||
dotnet_diagnostic.CA1707.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1707
|
||||
dotnet_diagnostic.CA1848.severity = none # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1848
|
||||
@@ -19,155 +20,3 @@ dotnet_diagnostic.Serilog004.severity = none # Serilog context property accessor
|
||||
dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this
|
||||
dotnet_diagnostic.SA1633.severity = none # File must have header
|
||||
dotnet_diagnostic.SA1649.severity = none # File name must match first type name
|
||||
|
||||
# Organize usings
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_separate_import_directive_groups = true
|
||||
csharp_using_directive_placement = inside_namespace:suggestion
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_style_namespace_declarations = file_scoped:warning
|
||||
# this. preferences
|
||||
dotnet_style_qualification_for_field = false:none
|
||||
dotnet_style_qualification_for_property = false:none
|
||||
dotnet_style_qualification_for_method = false:none
|
||||
dotnet_style_qualification_for_event = false:none
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_auto_properties = true:silent
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
###############################
|
||||
# Naming Conventions #
|
||||
###############################
|
||||
# Style Definitions
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style
|
||||
dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style
|
||||
# Use PascalCase for constant fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
###############################
|
||||
# C# Coding Conventions #
|
||||
###############################
|
||||
# var preferences
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_constructors = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_operators = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_properties = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_indexers = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_accessors = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_lambdas = when_on_single_line :suggestion
|
||||
csharp_style_expression_bodied_local_functions = when_on_single_line :suggestion
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
# Null-checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
# Modifier preferences
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
|
||||
# Expression-level preferences
|
||||
csharp_prefer_braces = true:none
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
# Unused value preferences
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:none
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
## Index and Range preferences
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
###############################
|
||||
# C# Formatting Rules #
|
||||
###############################
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
# Indentation preferences
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = flush_left
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents_when_block = true
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_square_brackets = false
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
###############################
|
||||
# VB Coding Conventions #
|
||||
###############################
|
||||
# Modifier preferences
|
||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
||||
|
||||
|
||||
###############################
|
||||
# Extra Rules #
|
||||
###############################
|
||||
|
||||
# dotnet_naming_rule.private_constants_rule.severity = warning
|
||||
# dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style
|
||||
# dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols
|
||||
# dotnet_naming_rule.private_instance_fields_rule.severity = warning
|
||||
dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style
|
||||
# dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols
|
||||
# dotnet_naming_rule.private_static_fields_rule.severity = warning
|
||||
dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style
|
||||
# dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols
|
||||
# dotnet_naming_style.prefix_private_field_with_underscore.required_prefix = _
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"EditorConfig.EditorConfig",
|
||||
"mhutchie.git-graph",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-vscode-remote.remote-containers",
|
||||
"seatonjiang.gitmoji-vscode",
|
||||
"redhat.vscode-xml"
|
||||
]
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"xunit.runner.json"
|
||||
],
|
||||
"url": "https://xunit.net/schema/current/xunit.runner.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"stylecop.json"
|
||||
],
|
||||
"url": "https://raw.githubusercontent.com/bjornhellander/NewStyleCopAnalyzers/refs/heads/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json"
|
||||
}
|
||||
],
|
||||
"editor.wordWrap": "on",
|
||||
"[xml]": {
|
||||
"editor.defaultFormatter": "redhat.vscode-xml"
|
||||
}
|
||||
}
|
||||
+13
-27
@@ -10,6 +10,7 @@
|
||||
<NoLogo>true</NoLogo>
|
||||
<!-- Prepend organization name to default namespace -->
|
||||
<RootNamespace>Kritikos.$(MSBuildProjectName)</RootNamespace>
|
||||
<AssemblyName>Kritikos.$(MSBuildProjectName)</AssemblyName>
|
||||
<!-- Enable global usings based on project type -->
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<!-- Generate documentation file and ignore warnings for undocumented elements -->
|
||||
@@ -49,8 +50,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Defaults to checked context for math operations, throws OverflowException when needed -->
|
||||
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
|
||||
<!-- Defaults to unchecked context for math operations for performance -->
|
||||
<!-- set to true to throws OverflowException when needed -->
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="OpenApi Source Generated Interceptors">
|
||||
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="$(ArtifactsPath) == '' AND $(DisableCustomArtifactsPath) != 'true'">
|
||||
@@ -74,20 +80,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="Project Assets">
|
||||
<ProjectIcon Condition="Exists('$(MSBuildThisFileDirectory)\icon.png')">
|
||||
$(MSBuildThisFileDirectory)icon.png
|
||||
</ProjectIcon>
|
||||
<ProjectIcon Condition="Exists('$(MSBuildProjectDirectory)\icon.png')">
|
||||
$(MSBuildProjectDirectory)icon.png
|
||||
</ProjectIcon>
|
||||
<ProjectIcon Condition="Exists('$(MSBuildThisFileDirectory)icon.png')">$(MSBuildThisFileDirectory)icon.png</ProjectIcon>
|
||||
<ProjectIcon Condition="Exists('$(MSBuildProjectDirectory)\icon.png')">$(MSBuildProjectDirectory)\icon.png</ProjectIcon>
|
||||
<ProjectIconExists Condition="$(ProjectIcon) != ''">true</ProjectIconExists>
|
||||
|
||||
<ProjectReadMe Condition="Exists('$(MSBuildThisFileDirectory)\README.md')">
|
||||
$(MSBuildThisFileDirectory)README.md
|
||||
</ProjectReadMe>
|
||||
<ProjectReadMe Condition="Exists('$(MSBuildProjectDirectory)\README.md')">
|
||||
$(MSBuildProjectDirectory)README.md
|
||||
</ProjectReadMe>
|
||||
<ProjectReadMe Condition="Exists('$(MSBuildThisFileDirectory)README.md')">$(MSBuildThisFileDirectory)README.md</ProjectReadMe>
|
||||
<ProjectReadMe Condition="Exists('$(MSBuildProjectDirectory)\README.md')">$(MSBuildProjectDirectory)\README.md</ProjectReadMe>
|
||||
<ProjectReadMeExists Condition="$(ProjectReadMe) != ''">true</ProjectReadMeExists>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -122,11 +120,6 @@
|
||||
<EFOptimizeContext>true</EFOptimizeContext>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Mark assemblies as CLS compliant so the compiler warns on non compliant usage -->
|
||||
<PropertyGroup Condition="$(IsPackable) == 'true'">
|
||||
<ClsCompliant>true</ClsCompliant>
|
||||
<GenerateSBOM>true</GenerateSBOM>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.CLSCompliant">
|
||||
<_Parameter1>$(ClsCompliant)</_Parameter1>
|
||||
@@ -154,13 +147,6 @@
|
||||
CopyToOutputDirectory="Never" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Project internals are exposed to projects named $(Project).Tests by default -->
|
||||
<ItemGroup
|
||||
Condition="@(InternalsVisibleToSuffix->Count()) == 0 AND @(InternalsVisibleTo->Count()) == 0">
|
||||
<InternalsVisibleToSuffix Include=".Tests" />
|
||||
<InternalsVisibleToSuffix Include="Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Label="Package">
|
||||
<PackageId>$(RootNamespace)</PackageId>
|
||||
<Company>Kritikos IO</Company>
|
||||
@@ -178,7 +164,7 @@
|
||||
<EnablePackageValidation>true</EnablePackageValidation>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="Versioning" Condition="'$(IsTestProject)' != 'true'">
|
||||
|
||||
+60
-25
@@ -9,8 +9,14 @@
|
||||
<None Update="packages.lock.json" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ClsCompliant Condition="'$(SkipClsCompliant)' != 'true' AND '$(IsPackable)' != 'false' AND '$(IsTestProject)' != 'true'">true</ClsCompliant>
|
||||
<ClsCompliant Condition="'$(ClsCompliant)' == ''">false</ClsCompliant>
|
||||
<GenerateSBOM Condition="'$(IsPackable)' != 'false' AND '$(IsTestProject)' != 'true'">true</GenerateSBOM>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup
|
||||
Condition="@(PackageReference->WithMetadataValue('Identity','Microsoft.AspNetCore.OpenApi')->Count()) > 0">
|
||||
Condition="'$(UsingMicrosoftNETSdkWeb)' == 'True' AND @(PackageReference->WithMetadataValue('Identity','Microsoft.AspNetCore.OpenApi')->Count()) > 0">
|
||||
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -29,40 +35,69 @@
|
||||
AfterTargets="GenerateOpenApiDocuments"
|
||||
Condition="'$(OpenApiDocumentsDirectory)' != ''">
|
||||
<Move SourceFiles="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName).json"
|
||||
DestinationFiles="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName)-v1.json"
|
||||
DestinationFiles="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName)_v1.json"
|
||||
Condition="Exists('$(OpenApiDocumentsDirectory)/$(MSBuildProjectName).json')" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
OpenApiDocumentsDirectory tracks $(ArtifactsPath), which developers may override to a
|
||||
machine-specific location. Client generators (e.g. a checked-in .refitter file) need a
|
||||
stable, repo-relative path, so mirror the renamed document(s) into the repo's own
|
||||
artifacts/OpenApi folder regardless of where ArtifactsPath actually points.
|
||||
|
||||
Uses a glob (not a single "_v1.json" name) because a project may define multiple named
|
||||
OpenAPI documents (AddOpenApi("v1"), AddOpenApi("v2"), ...), each emitted as its own
|
||||
"$(MSBuildProjectName)_{documentName}.json" file.
|
||||
-->
|
||||
<Target Name="CopyOpenApiDocumentsToRepoArtifacts"
|
||||
AfterTargets="RenameOpenApiDocuments"
|
||||
Condition="'$(OpenApiDocumentsDirectory)' != ''">
|
||||
<ItemGroup>
|
||||
<OpenApiDocumentToCopy Include="$(OpenApiDocumentsDirectory)/$(MSBuildProjectName)*.json" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(OpenApiDocumentToCopy)"
|
||||
DestinationFolder="$(MSBuildThisFileDirectory)artifacts/OpenApi"
|
||||
SkipUnchangedFiles="true"
|
||||
Condition="@(OpenApiDocumentToCopy->Count()) > 0" />
|
||||
</Target>
|
||||
|
||||
<Target Name="AddInternalsVisibleTo" BeforeTargets="BeforeCompile">
|
||||
<!--
|
||||
Add an ItemGroup with tags in the following format:
|
||||
<InternalsVisibleTo Include="CustomTest1" />
|
||||
-> [assembly: InternalsVisibleTo("CustomTest1")]
|
||||
<InternalsVisibleTo Include="CustomTest2, PublicKey=abc" />
|
||||
-> [assembly: InternalsVisibleTo("CustomTest2, PublicKey=abc")]
|
||||
<InternalsVisibleTo Include="$(AssemblyName).Custom" />
|
||||
-> [assembly: InternalsVisibleTo("ClassLibrary1.Custom")]
|
||||
Exposes assembly internals to friend assemblies.
|
||||
|
||||
on a project with assembly name ClasLibrary1:
|
||||
The .NET SDK already turns <InternalsVisibleTo Include="..." /> items into
|
||||
[assembly: InternalsVisibleTo("...")] attributes in its GetAssemblyAttributes
|
||||
target (including strong-name PublicKey handling via PublicKey/Key metadata).
|
||||
This target must therefore NOT emit those attributes itself - doing so emits
|
||||
every friend assembly twice.
|
||||
|
||||
Instead it only layers the project-local convention on top of the SDK:
|
||||
* translate <InternalsVisibleToSuffix> entries into InternalsVisibleTo items
|
||||
so the SDK can emit them, and
|
||||
* apply a sensible default when a project declared no friend assemblies.
|
||||
|
||||
Examples on an assembly named "ClassLibrary1":
|
||||
<InternalsVisibleTo Include="CustomTest1" /> (handled by the SDK)
|
||||
-> [assembly: InternalsVisibleTo("CustomTest1")]
|
||||
<InternalsVisibleToSuffix Include=".Tests" /> (handled here)
|
||||
-> [assembly: InternalsVisibleTo("ClassLibrary1.Tests")]
|
||||
|
||||
Running at build time (before GetAssemblyAttributes) means the counts below
|
||||
see items declared in both imported props and the project body, so an
|
||||
explicit InternalsVisibleTo correctly suppresses the default suffix.
|
||||
-->
|
||||
|
||||
<!-- Default: expose internals to "<AssemblyName>.Tests" when the project has
|
||||
not declared any friend assemblies of its own. -->
|
||||
<ItemGroup Condition="@(InternalsVisibleTo->Count()) == 0 AND @(InternalsVisibleToSuffix->Count()) == 0">
|
||||
<InternalsVisibleToSuffix Include=".Tests" />
|
||||
-> [assembly: InternalsVisibleTo("ClassLibrary1.Tests")]
|
||||
<InternalsVisibleToSuffix Include=".FunctionalTests" />
|
||||
-> [assembly: InternalsVisibleTo("ClassLibrary1.FunctionalTests")]
|
||||
-->
|
||||
|
||||
<!-- Handle InternalsVisibleTo -->
|
||||
<ItemGroup Condition="'@(InternalsVisibleTo->Count())' > 0">
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>%(InternalsVisibleTo.Identity)</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Handle InternalsVisibleToSuffix -->
|
||||
<!-- Translate the suffix convention into InternalsVisibleTo items for the SDK,
|
||||
skipping the current assembly (e.g. a "*.Tests" project itself). -->
|
||||
<ItemGroup Condition="@(InternalsVisibleToSuffix->Count()) > 0">
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo"
|
||||
Condition="$(AssemblyName.EndsWith(%(InternalsVisibleToSuffix.Identity))) == 'false'">
|
||||
<_Parameter1>$(AssemblyName)%(InternalsVisibleToSuffix.Identity)</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<InternalsVisibleTo Include="$(AssemblyName)%(InternalsVisibleToSuffix.Identity)"
|
||||
Condition="$(AssemblyName.EndsWith(%(InternalsVisibleToSuffix.Identity))) == 'false'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
<ItemGroup Label="Packages">
|
||||
<PackageVersion Include="GitVersion.MsBuild" Version="6.6.2" />
|
||||
<PackageVersion Include="GitVersion.MsBuild" Version="6.8.2" />
|
||||
<PackageVersion Include="Microsoft.Sbom.Targets" Version="4.1.5" />
|
||||
<PackageVersion Include="NewStyleCop.Analyzers" Version="1.2.1" />
|
||||
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.5" />
|
||||
<PackageVersion Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.10" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+36
-16
@@ -1,26 +1,46 @@
|
||||
workflow: GitHubFlow/v1
|
||||
mode: ContinuousDelivery
|
||||
workflow: TrunkBased/preview1
|
||||
tag-prefix: '[vV]?'
|
||||
ignore:
|
||||
paths:
|
||||
- ^(?!src\/|Directory\.Packages\.props|GitVersion.yml).*
|
||||
- ^(?!src\/|Directory\.Packages\.props|GitVersion.yml).*
|
||||
branches:
|
||||
develop:
|
||||
label: 'beta'
|
||||
regex: '^(dev|develop|development)$'
|
||||
source-branches:
|
||||
- main
|
||||
feature:
|
||||
mode: ContinuousDelivery
|
||||
source-branches:
|
||||
- develop
|
||||
hotfix:
|
||||
regex: ^hotfix?[\/-](?<BranchName>.+)
|
||||
mode: ContinuousDelivery
|
||||
release:
|
||||
mode: ContinuousDeployment
|
||||
label: ''
|
||||
increment: Patch
|
||||
regex: ^releases?[\/-](?<BranchName>.+)
|
||||
source-branches:
|
||||
- main
|
||||
is-release-branch: true
|
||||
is-main-branch: true
|
||||
pre-release-weight: 55000
|
||||
develop:
|
||||
mode: ContinuousDelivery
|
||||
label: alpha
|
||||
increment: Minor
|
||||
regex: ^develop$
|
||||
source-branches:
|
||||
- main
|
||||
prevent-increment:
|
||||
when-current-commit-tagged: false
|
||||
pre-release-weight: 0
|
||||
feature:
|
||||
source-branches:
|
||||
- main
|
||||
- develop
|
||||
- release
|
||||
hotfix:
|
||||
source-branches:
|
||||
- main
|
||||
- release
|
||||
pull-request:
|
||||
source-branches:
|
||||
- main
|
||||
- develop
|
||||
- release
|
||||
- feature
|
||||
- hotfix
|
||||
|
||||
prevent-increment:
|
||||
of-merged-branch: true
|
||||
when-branch-merged: true
|
||||
when-current-commit-tagged: true
|
||||
+5
-28
@@ -4,39 +4,16 @@
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
"EditorConfig.EditorConfig",
|
||||
"mhutchie.git-graph",
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-vscode-remote.remote-containers",
|
||||
"seatonjiang.gitmoji-vscode",
|
||||
"redhat.vscode-xml"
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"files.associations": {
|
||||
"nuget.config": "xml"
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"xunit.runner.json"
|
||||
],
|
||||
"url": "https://xunit.net/schema/current/xunit.runner.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"stylecop.json"
|
||||
],
|
||||
"url": "https://raw.githubusercontent.com/bjornhellander/NewStyleCopAnalyzers/refs/heads/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json"
|
||||
}
|
||||
],
|
||||
"editor.wordWrap": "on",
|
||||
"[xml]": {
|
||||
"editor.defaultFormatter": "redhat.vscode-xml"
|
||||
},
|
||||
"xml.format.maxLineWidth": 0,
|
||||
"xml.format.splitAttributes": "preserve",
|
||||
"github.copilot.chat.commitMessageGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/copilot-commit-message-instructions.md"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeEditing/GenerateMemberBody/DebuggerStepThroughAccessors/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeEditing/GenerateMemberBody/DocumentationGenerationKind/@EntryValue">Inherit</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeEditing/GenerateMemberBody/PlaceBackingFieldAboveProperty/@EntryValue">True</s:Boolean>
|
||||
@@ -14,14 +14,8 @@
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsEnabledAtOtherPositions/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsInsertNullCheckWhenAlreadyAnnotated/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:Int64 x:Key="/Default/CodeInspection/CognitiveComplexityAnalysis/CSharpThreshold/@EntryValue">20</s:Int64>
|
||||
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=_002AModelSnapshot_002Ecs/@EntryIndexedValue">*ModelSnapshot.cs</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=Migrations_002F_002A/@EntryIndexedValue">Migrations/*</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/CalculateInheritedTypeMembers/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/DoNotShowVsBulb/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassNeverInstantiated_002ELocal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertToPrimaryConstructor/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/MergeVsBulbItemsIntoReSharperBulb/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/ReadSettingsFromFileLevel/@EntryValue">True</s:Boolean>
|
||||
@@ -29,83 +23,12 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/SweaWarningsMode/@EntryValue">ShowAndRun</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/XmlDocInspections/TypeAccessibility/@EntryValue">None</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/XmlDocInspections/TypeMemberAccessibility/@EntryValue">None</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CommonFormatter/AUTODETECT_INDENT_SETTINGS/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGNMENT_TAB_FILL_STYLE/@EntryValue">USE_SPACES</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_REGION/@EntryValue">0</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BEFORE_SINGLE_LINE_COMMENT/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_BETWEEN_USING_GROUPS/@EntryValue">1</s:Int64>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_BINARY_OPSIGN/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINQ_EXPRESSIONS/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_TERNARY_EXPR_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/EnableStyleCopSupport/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/EditorConfig/ShowEditorConfigStatusBarIndicator/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FGLOBAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLABEL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FCONSTRUCTOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FLOCAL_005FVARIABLE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FOBJECT_005FPROPERTY_005FOF_005FFUNCTION/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FCLASS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FENUM_005FMEMBER/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FINTERFACE/@EntryIndexedValue"><Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMIXED_005FENUM/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FEXPORTED/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FMODULE_005FLOCAL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPRIVATE_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPROTECTED_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FMEMBER_005FACCESSOR/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FSTATIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FPUBLIC_005FTYPE_005FMETHOD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=TS_005FTYPE_005FPARAMETER/@EntryIndexedValue"><Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FHTML_005FCONTROL/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FNAME/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/WebNaming/UserRules/=ASP_005FTAG_005FPREFIX/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CustomTools/CustomToolsData/@EntryValue"></s:String>
|
||||
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=AutoRecoverer/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Format/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Roslyn_002Dswea/@EntryIndexedValue">DO_NOTHING</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=ShowAnnotations/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=SolExp_002DTrack/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=StartPage_002DIsDownloadRefreshEnabled/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=StartPage_002DOnEnvironmentStatup/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=SyncSettings/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=TextEditor_002DCodeLens/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=TextEditor_002DTrackChanges_002D2/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VCS/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VsBulb/@EntryIndexedValue">DO_NOTHING</s:String>
|
||||
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=XAML_0020Designer/@EntryIndexedValue">LIVE_MONITOR</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
@@ -115,6 +38,5 @@
|
||||
<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=Testing_0020Platform/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/UnitTesting/DisabledProviders/=VsTest/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/UnitTesting/TestingPlatformTestProvider/IgnoreProjectsSupportedByOtherProviders/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/ReSpeller/ReSpellerEnabled/@EntryValue">False</s:Boolean>
|
||||
</wpf:ResourceDictionary>
|
||||
@@ -6,7 +6,7 @@
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsPublishable>false</IsPublishable>
|
||||
<NoWarn>$(NoWarn);SA1600</NoWarn>
|
||||
<NoWarn>$(NoWarn);SA1600;SA1601;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,9 +1,79 @@
|
||||
<Project>
|
||||
<ImportGroup Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
<ImportGroup
|
||||
Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))) == 'true'">
|
||||
<Import
|
||||
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</ImportGroup>
|
||||
|
||||
<PropertyGroup Label="Package">
|
||||
<PackageProjectUrl>http://localhost</PackageProjectUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="GitVersion per-project scoping">
|
||||
<GitVersionTemplateFile>$(MSBuildThisFileDirectory)..\GitVersion.yml</GitVersionTemplateFile>
|
||||
<_GitVersionIgnorePathsRegex>(paths:\s*\r?\n\s*- ).+</_GitVersionIgnorePathsRegex>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
GitVersion.MsBuild runs `gitversion "$(MSBuildProjectDirectory)"`, and GitVersion resolves its
|
||||
config by
|
||||
walking up from that directory to the repo root, using the first GitVersion.yml it finds. Rendering
|
||||
one
|
||||
here (rewriting the repo-root GitVersion.yml's ignore.paths entry, so there is a single
|
||||
hand-maintained
|
||||
file) scopes ignore.paths to this project's own directory plus the full transitive closure of its
|
||||
ProjectReference dependencies, so a change anywhere upstream in the dependency graph also counts
|
||||
toward
|
||||
this project's version. Generated file is gitignored (see .gitignore).
|
||||
-->
|
||||
<Target Name="_CollectGitVersionTransitiveReferences" Returns="@(_GitVersionTransitiveRef)">
|
||||
<ItemGroup>
|
||||
<!-- Resolve to FullPath here, before crossing the MSBuild task-output boundary below:
|
||||
relative
|
||||
Includes from an intermediate project resolve against the wrong directory once passed through. -->
|
||||
<_GitVersionTransitiveRef Include="@(ProjectReference->'%(FullPath)')" />
|
||||
</ItemGroup>
|
||||
<MSBuild Projects="@(ProjectReference)" Targets="_CollectGitVersionTransitiveReferences"
|
||||
Condition="'@(ProjectReference)' != ''" SkipNonexistentTargets="true">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="_GitVersionTransitiveRef" />
|
||||
</MSBuild>
|
||||
</Target>
|
||||
|
||||
<Target Name="GenerateProjectGitVersionConfig" BeforeTargets="RunGitVersion"
|
||||
Condition="Exists('$(GitVersionTemplateFile)')">
|
||||
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="_CollectGitVersionTransitiveReferences"
|
||||
Condition="'@(ProjectReference)' != ''" SkipNonexistentTargets="true">
|
||||
<Output TaskParameter="TargetOutputs" ItemName="_GitVersionScopeProjectRef" />
|
||||
</MSBuild>
|
||||
<ItemGroup>
|
||||
<_GitVersionScopePath Include="$(MSBuildProjectDirectory)" />
|
||||
<_GitVersionScopePath Include="@(_GitVersionScopeProjectRef->'%(RootDir)%(Directory)')" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<_GitVersionScopePath>
|
||||
<EscapedPath>$([MSBuild]::EnsureTrailingSlash($([MSBuild]::MakeRelative('$(MSBuildThisFileDirectory)..\',
|
||||
'%(Identity)'))).Replace('\', '/').Replace('.', '\.').Replace('/', '\/'))</EscapedPath>
|
||||
</_GitVersionScopePath>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_GitVersionIgnorePattern>^(?!@(_GitVersionScopePath->'%(EscapedPath)',
|
||||
'|')|Directory\.Packages\.props|GitVersion\.yml).*</_GitVersionIgnorePattern>
|
||||
<_GitVersionTemplateContent>$([System.IO.File]::ReadAllText('$(GitVersionTemplateFile)'))</_GitVersionTemplateContent>
|
||||
<_GitVersionRenderedContent>$([System.Text.RegularExpressions.Regex]::Replace('$(_GitVersionTemplateContent)',
|
||||
'$(_GitVersionIgnorePathsRegex)', '$1$(_GitVersionIgnorePattern)'))</_GitVersionRenderedContent>
|
||||
</PropertyGroup>
|
||||
<Error Condition="'$(_GitVersionRenderedContent)' == '$(_GitVersionTemplateContent)'"
|
||||
Text="GenerateProjectGitVersionConfig: could not find an ignore.paths entry to rewrite in $(GitVersionTemplateFile) for $(MSBuildProjectName) -- check its ignore.paths structure." />
|
||||
<WriteLinesToFile File="$(MSBuildProjectDirectory)\GitVersion.yml"
|
||||
Lines="$(_GitVersionRenderedContent)"
|
||||
Overwrite="true" WriteOnlyWhenDifferent="true" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Label="Changelog per-project scoping">
|
||||
<!-- GenerateChangelogFromGit (root Directory.Build.targets) walks direct ProjectReference
|
||||
directories when this is true. A dependency's own changes belong in its own changelog/version,
|
||||
not copied here (that would misattribute a dependency's commit as this project's own change):
|
||||
scope to this project's own directory only. -->
|
||||
<ChangeLogIncludeReferences>false</ChangeLogIncludeReferences>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project>
|
||||
<ImportGroup
|
||||
Condition="Exists($([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))) == 'true'">
|
||||
<Import
|
||||
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</ImportGroup>
|
||||
|
||||
<!-- Overrides the root's CreateReleaseNotesFromFile: its Condition="Exists('$(ChangeLogPath)')" is
|
||||
evaluated before its own DependsOnTargets="GenerateChangelogFromGit" has run, so on a clean build the file
|
||||
never exists yet and PackageReleaseNotes silently never gets set. Moving the existence check onto the
|
||||
task (evaluated after the dependency runs) instead of the target fixes the ordering. -->
|
||||
<Target Name="CreateReleaseNotesFromFile" BeforeTargets="GenerateNuspec"
|
||||
DependsOnTargets="GenerateChangelogFromGit">
|
||||
<ReadLinesFromFile File="$(ChangeLogPath)" Condition="Exists('$(ChangeLogPath)')">
|
||||
<Output TaskParameter="Lines" ItemName="ReleaseNoteLines" />
|
||||
</ReadLinesFromFile>
|
||||
<PropertyGroup Condition="Exists('$(ChangeLogPath)')">
|
||||
<PackageReleaseNotes>@(ReleaseNoteLines, '%0a')</PackageReleaseNotes>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -6,7 +6,7 @@
|
||||
<PropertyGroup>
|
||||
<IsPublishable>false</IsPublishable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<NoWarn>$(NoWarn);SA1600</NoWarn>
|
||||
<NoWarn>$(NoWarn);SA1600;SA1601;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Label="xUnit Configuration" Condition="Exists('$(MSBuildThisFileDirectory)\..\xunit.runner.json') AND $(IsTestProject)=='True' AND @(PackageReference->StartsWith('xunit')->Count()) > 0">
|
||||
|
||||
Reference in new issue
Block a user