Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e3edb17
Add agent instructions
ptr727 Jan 28, 2026
8a91bec
Refactor code structure and improve documentation across multiple files
ptr727 Jan 29, 2026
8f5b440
Enhance logging framework with global configuration options and impro…
ptr727 Jan 29, 2026
d69e431
Refactor logging setup and improve test structure with single instanc…
ptr727 Jan 30, 2026
3459ad9
Update testing framework references to use AwesomeAssertions and refi…
ptr727 Jan 30, 2026
6318bad
Update README to reorganize sections and enhance branching workflow r…
ptr727 Jan 30, 2026
1b99ddb
Fix formatting issues in AGENTS.md lists
ptr727 Jan 30, 2026
77ca063
Enhance logging configuration and error handling in Console applicati…
ptr727 Jan 30, 2026
799b426
Refactor CodeGen to simplify command line parsing and enhance logging…
ptr727 Jan 30, 2026
3d4022b
Enhance logging output by adding thread name enrichment and correctin…
ptr727 Jan 30, 2026
bccf795
Refactor logging setup to streamline logger creation and enhance cons…
ptr727 Jan 30, 2026
b1306d0
Refactor code structure and enhance logging; update API key handling …
ptr727 Jan 30, 2026
d9ba071
Update CODESTYLE.md to clarify usage of `#region` and improve member …
ptr727 Jan 30, 2026
157e84a
Refactor logging implementation to use lazy initialization; update do…
ptr727 Jan 31, 2026
d72b139
Refactor logger initialization in StaticTemplateLibrary for improved …
ptr727 Jan 31, 2026
8456145
Refactor LoggerFactory initialization for improved clarity and flexib…
ptr727 Jan 31, 2026
afbcfa8
Update documentation in CODESTYLE.md for clarity in remarks section
ptr727 Jan 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ insert_final_newline = true
trim_trailing_whitespace = true

# Markdown files
[*.{md}]
[*.md]
end_of_line = crlf
trim_trailing_whitespace = false

Expand All @@ -39,7 +39,6 @@ indent_size = 2
# Json files
[*.json]
end_of_line = crlf
indent_size = 4

# Linux scripts
[*.sh]
Expand Down Expand Up @@ -73,7 +72,7 @@ csharp_prefer_simple_using_statement = true
csharp_prefer_static_anonymous_function = true
csharp_prefer_static_local_function = true
csharp_prefer_system_threading_lock = true
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_preferred_modifier_order = public,private,protected,internal,file,static,abstract,sealed,virtual,override,readonly,unsafe,volatile,async,extern,new,partial:warning
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
csharp_space_after_cast = false
Expand Down Expand Up @@ -203,4 +202,10 @@ dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
dotnet_style_readonly_field = true
dotnet_style_require_accessibility_modifiers = for_non_interface_members
dotnet_style_require_accessibility_modifiers = for_non_interface_members

# ReSharper settings
resharper_csharp_trailing_comma_in_multiline_lists = true
resharper_csharp_var_for_built_in_types = false
resharper_csharp_var_when_type_is_apparent = false
resharper_csharp_var_when_type_is_not_apparent = false
10 changes: 5 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

- **Library**: Core library with AOT compatibility (`Library.csproj`)
- **Console**: Command-line application using System.CommandLine (`Console.csproj`)
- **Tests**: Unit tests using xUnit and FluentAssertions (`Tests.csproj`)
- **Tests**: Unit tests using xUnit and AwesomeAssertions (`Tests.csproj`)
- **Benchmarks**: Performance benchmarks using BenchmarkDotNet (`Benchmarks.csproj`)
- **Docker**: Docker build configurations for Linux containers

Expand Down Expand Up @@ -183,7 +183,7 @@ Available VS Code tasks (use via `run_task` tool):

### Testing Conventions

1. **Test Framework**: xUnit with FluentAssertions
1. **Test Framework**: xUnit with AwesomeAssertions
```csharp
[Fact]
public void MethodName_Scenario_ExpectedBehavior()
Expand Down Expand Up @@ -252,7 +252,7 @@ Available VS Code tasks (use via `run_task` tool):
### Testing Dependencies

- **xUnit**: Test framework
- **FluentAssertions**: Fluent assertion library
- **AwesomeAssertions**: Fluent assertion library
- **BenchmarkDotNet**: Performance benchmarking

### Development Tools
Expand Down Expand Up @@ -280,7 +280,7 @@ Available VS Code tasks (use via `run_task` tool):
- `Console/` - Console/CLI application using System.CommandLine
- `Docker/` - Docker build scripts and Dockerfile
- `Library/` - Core reusable library
- `Tests/` - Unit tests using xUnit and FluentAssertions
- `Tests/` - Unit tests using xUnit and AwesomeAssertions

## Best Practices

Expand Down Expand Up @@ -327,7 +327,7 @@ The project includes comprehensive `.editorconfig` settings that enforce:
- [CSharpier Documentation](https://csharpier.com)
- [Husky.Net Documentation](https://alirezanet.github.io/Husky.Net)
- [xUnit Documentation](https://xunit.net)
- [FluentAssertions Documentation](https://fluentassertions.com)
- [AwesomeAssertions Documentation](https://awesomeassertions.org/)
- [BenchmarkDotNet Documentation](https://benchmarkdotnet.org)
- [System.CommandLine Documentation](https://learn.microsoft.com/en-us/dotnet/standard/commandline/)
- [Serilog Documentation](https://serilog.net)
Expand Down
23 changes: 19 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "Console",
"name": "Console Root",
"type": "coreclr",
"request": "launch",
"preLaunchTask": ".Net Build",
Expand All @@ -16,6 +16,23 @@
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Console Test",
"type": "coreclr",
"request": "launch",
"preLaunchTask": ".Net Build",
"program": "${workspaceFolder}/Console/bin/Debug/net10.0/Console.dll",
"args": [
"--loglevel=Debug",
"--logfile=console.log",
"--logfile-clear=true",
"test",
"--test=test"
],
"cwd": "${workspaceFolder}/Console/bin/Debug/net10.0",
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "CodeGen",
"type": "coreclr",
Expand All @@ -24,9 +41,7 @@
"program": "${workspaceFolder}/CodeGen/bin/Debug/net10.0/CodeGen.dll",
"args": [
"--codepath",
"${workspaceFolder}/CodeGen",
"--apikey",
""
"${workspaceFolder}/CodeGen"
],
"cwd": "${workspaceFolder}/CodeGen/bin/Debug/net10.0",
"console": "internalConsole",
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ For comprehensive coding standards and detailed conventions, refer to [`.github/

- **Library**: Core reusable library
- **Console**: CLI application using System.CommandLine
- **Tests**: xUnit with FluentAssertions (Arrange-Act-Assert pattern)
- **Tests**: xUnit with AwesomeAssertions (Arrange-Act-Assert pattern)
- **Benchmarks**: BenchmarkDotNet performance measurements
- **Docker**: Multi-platform Linux containers

### Testing

- Use xUnit v3 and FluentAssertions
- Use xUnit v3 and AwesomeAssertions
- Organize tests logically in separate files
- Follow Arrange-Act-Assert pattern
- Test naming: `MethodName_Scenario_ExpectedBehavior()`
Expand Down
28 changes: 23 additions & 5 deletions CODESTYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,38 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
- C#, XML, YAML, JSON, Windows scripts: CRLF
- Linux scripts (`.sh`): LF

6. **`#region`**: Do not use. Prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop-like)**: Constants → fields → constructors → properties → indexers → methods → events → operators → finalizers → delegates → nested types
6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization.
7. **Member ordering (StyleCop SA1201)**: conststatic readonly → static fields → instance readonly fields → instance fields → constructors → public (events → properties → indexers → methods → operators)non-public in same order → nested types

### Comments and Documentation

1. **XML documentation**
- `<GenerateDocumentationFile>true</GenerateDocumentationFile>`
- Missing XML comments for public APIs are suppressed (`.editorconfig`)
- Single-line summaries
- Must document all public surfaces.
- Single-line summaries, additional details in remarks, document input parameters, returns values, exceptions, and add crefs

```csharp
/// <summary>
/// This property always returns a value < 1.
/// Example of a single line summary.
/// </summary>
/// <remarks>
/// Additional important details about usage.
/// Multiple lines if needed.
/// </remarks>
/// <param name="category">
/// The quote category to request
/// </param>
/// <param name="cancellationToken">
/// A <see cref="System.Threading.CancellationToken"/> that can be used to cancel the request.
/// </param>
/// <returns>
/// A <see cref="string"/> containing the quote text.
/// </returns>
/// <exception cref="System.ArgumentException">
/// Thrown when <paramref name="category"/> is not a supported value.
/// </exception>
public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
```

2. **Code analysis suppressions**
Expand Down Expand Up @@ -248,7 +266,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to

### Testing Conventions

1. **Framework**: xUnit with FluentAssertions
1. **Framework**: xUnit with AwesomeAssertions

```csharp
[Fact]
Expand Down
10 changes: 8 additions & 2 deletions CodeGen/ApiNinjas.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using System.IO;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ptr727.ProjectTemplate.CodeGen;

internal sealed class ApiNinjas(string apiKey, CancellationToken cancellationToken)
{
[SuppressMessage(
"Usage",
"CA2007:Consider calling ConfigureAwait on the awaited task",
Justification = "https://github.com/dotnet/roslyn-analyzers/issues/7185"
)]
internal async Task<string> GetQuoteOfTheDayAsync()
{
// https://api-ninjas.com/api/quotes#v2-quoteoftheday
Expand All @@ -21,9 +26,10 @@ internal async Task<string> GetQuoteOfTheDayAsync()
.ConfigureAwait(false);
_ = response.EnsureSuccessStatusCode();

using Stream responseStream = await response
await using Stream responseStream = await response
.Content.ReadAsStreamAsync(cancellationToken)
.ConfigureAwait(false);

QuoteOfTheDayItem[]? items = await JsonSerializer
.DeserializeAsync(
responseStream,
Expand Down
11 changes: 3 additions & 8 deletions CodeGen/CodeGen.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
namespace ptr727.ProjectTemplate.CodeGen;

[System.CodeDom.Compiler.GeneratedCode("ptr727.ProjectTemplate.CodeGen", "1.0")]
internal static class CodeGen
{
private const string QuoteOfTheDay =
"Don't accept a life that has been molded for you by others because eventually you'll succumb to its falseness.";
private const string QuoteOfTheDay = "No API key provided.";

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Globalization",
"CA1303:Do not pass literals as localized parameters",
Justification = "Demonstration code."
)]
internal static void Quote()
{
string dateTime = $"2026-01-28T04:21:26.4533219Z";
const string dateTime = "2026-01-30T22:28:14.6290903Z";
Console.WriteLine($"{dateTime} : {QuoteOfTheDay}");
Log.Logger.Information("Quote of the Day: {DateTime} : {Quote}", dateTime, QuoteOfTheDay);
}
Expand Down
9 changes: 2 additions & 7 deletions CodeGen/CodeGenBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.IO;
using System.Text;

namespace ptr727.ProjectTemplate.CodeGen;
Expand All @@ -11,18 +10,14 @@ internal async Task CodeGenAsync(string quote)
string codeGen = $$"""
namespace ptr727.ProjectTemplate.CodeGen;

[System.CodeDom.Compiler.GeneratedCode("ptr727.ProjectTemplate.CodeGen", "1.0")]
internal static class CodeGen
{
private const string QuoteOfTheDay = {{ToCSharpStringLiteral(quote)}};

[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Globalization",
"CA1303:Do not pass literals as localized parameters",
Justification = "Demonstration code."
)]
internal static void Quote()
{
string dateTime = $"{{DateTime.UtcNow:o}}";
const string dateTime = "{{DateTime.UtcNow:o}}";
Comment thread
ptr727 marked this conversation as resolved.
Console.WriteLine($"{dateTime} : {QuoteOfTheDay}");
Log.Logger.Information("Quote of the Day: {DateTime} : {Quote}", dateTime, QuoteOfTheDay);
}
Expand Down
16 changes: 8 additions & 8 deletions CodeGen/CommandLine.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
using System.CommandLine;
using System.CommandLine.Parsing;
using System.IO;

namespace ptr727.ProjectTemplate.CodeGen;

internal sealed class CommandLine
{
private readonly Option<DirectoryInfo> _codePathOption = CreateCodePathOption();
private readonly Option<string> _apiKeyOption = CreateAPIKeyOption();
private readonly Option<string> _apiKeyOption = CreateApiKeyOption();

private static readonly FrozenSet<string> s_cliBypassList = FrozenSet.Create(
StringComparer.OrdinalIgnoreCase,
["--help", "--version"]
"--help",
"--version"
);

internal CommandLine(string[] args)
Expand All @@ -20,8 +20,8 @@ internal CommandLine(string[] args)
Result = Root.Parse(args);
}

internal RootCommand Root { get; init; }
internal ParseResult Result { get; init; }
internal RootCommand Root { get; }
internal ParseResult Result { get; }

internal RootCommand CreateRootCommand()
{
Expand All @@ -41,7 +41,7 @@ internal Options CreateOptions(ParseResult parseResult) =>
new()
{
CodePath = parseResult.GetValue(_codePathOption)!,
APIKey = parseResult.GetValue(_apiKeyOption) ?? string.Empty,
ApiKey = parseResult.GetValue(_apiKeyOption) ?? string.Empty,
};

private static Option<DirectoryInfo> CreateCodePathOption()
Expand All @@ -54,7 +54,7 @@ private static Option<DirectoryInfo> CreateCodePathOption()
return option.AcceptExistingOnly();
}

private static Option<string> CreateAPIKeyOption() =>
private static Option<string> CreateApiKeyOption() =>
new("--apikey", "-a") { Description = "The API key to use (optional).", Required = false };

internal static bool BypassStartup(ParseResult parseResult) =>
Expand All @@ -67,6 +67,6 @@ symbolResult is OptionResult optionResult
internal sealed class Options
{
internal required DirectoryInfo CodePath { get; init; }
internal required string APIKey { get; init; }
internal required string ApiKey { get; init; }
}
}
2 changes: 1 addition & 1 deletion CodeGen/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace ptr727.ProjectTemplate.CodeGen;

internal static partial class LogExtensions
internal static class LogExtensions
{
extension(ILogger logger)
{
Expand Down
1 change: 1 addition & 0 deletions CodeGen/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
global using System;
global using System.Collections.Frozen;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
Expand Down
10 changes: 2 additions & 8 deletions CodeGen/HttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ private static ResilienceHandler CreateResilienceHandler() =>
ShouldHandle = args =>
ValueTask.FromResult(
args.Outcome.Exception != null
|| (
args.Outcome.Result != null
&& !args.Outcome.Result.IsSuccessStatusCode
)
|| args.Outcome.Result is { IsSuccessStatusCode: false }
),
}
)
Expand All @@ -46,10 +43,7 @@ private static ResilienceHandler CreateResilienceHandler() =>
ShouldHandle = args =>
ValueTask.FromResult(
args.Outcome.Exception != null
|| (
args.Outcome.Result != null
&& !args.Outcome.Result.IsSuccessStatusCode
)
|| args.Outcome.Result is { IsSuccessStatusCode: false }
),
}
)
Expand Down
Loading
Loading