Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .docfx/Dockerfile.docfx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NGINX_VERSION=1.29.3-alpine
ARG NGINX_VERSION=1.29.4-alpine

FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base
RUN rm -rf /usr/share/nginx/html/*
Expand Down
2 changes: 1 addition & 1 deletion .docfx/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
],
"globalMetadata": {
"_appTitle": "Extensions for Globalization by Codebelt",
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2024-2025 Geekle. All rights reserved.</span>",
"_appFooter": "<span>Generated by <strong>DocFX</strong>. Copyright 2024-2026 Geekle. All rights reserved.</span>",
"_appLogoPath": "images/50x50.png",
"_appFaviconPath": "images/favicon.ico",
"_googleAnalyticsTagId": "G-R07CSX4Z91",
Expand Down
610 changes: 610 additions & 0 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions .github/prompts/benchmark.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
mode: agent
description: 'Writing Performance Benchmarks'
---

# Benchmark Fixture Prompt (Tuning Benchmarks)

This prompt defines how to generate performance tests (“benchmarks”) for a project/solution using BenchmarkDotNet.
Benchmarks are *not* unit tests — they are micro- or component-level performance measurements that belong under the `tuning/` directory and follow strict conventions.

Copilot must follow these guidelines when generating benchmark fixtures.

---

## 1. Naming and Placement

- All benchmark projects live under the `tuning/` folder.
Examples:
- `tuning/<ProjectName>.Benchmarks/`
- `tuning/<ProjectName>.Console.Benchmarks/`

- **Namespaces must NOT end with `.Benchmarks`.**
They must mirror the production assembly’s namespace.

Example:
If benchmarking a type inside `YourProject.Console`, then:

```csharp
namespace YourProject.Console
{
public class Sha512256Benchmark { … }
}
```

* **Benchmark class names must end with `Benchmark`.**
Example: `DateSpanBenchmark`, `FowlerNollVoBenchmark`.

* Benchmark files should be located in the matching benchmark project
(e.g., benchmarks for `YourProject.Console` go in `YourProject.Console.Benchmarks.csproj`).

* In the `.csproj` for each benchmark project, set the root namespace to the production namespace, for example:

```xml
<RootNamespace>YourProject.Console</RootNamespace>
```

---

## 2. Attributes and Configuration

Each benchmark class should use:

```csharp
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
```

Optional but strongly recommended where meaningful:

* `[Params(...)]` — define small, medium, large input sizes.
* `[GlobalSetup]` — deterministic initialization of benchmark data.
* `[Benchmark(Description = "...")]` — always add descriptions.
* `[Benchmark(Baseline = true)]` — when comparing two implementations.

Avoid complex global configs; prefer explicit attributes inside the class.

---

## 3. Structure and Best Practices

A benchmark fixture must:

* Measure a **single logical operation** per benchmark method.
* Avoid I/O, networking, disk access, logging, or side effects.
* Avoid expensive setup inside `[Benchmark]` methods.
* Use deterministic data (e.g., seeded RNG or predefined constants).
* Use `[GlobalSetup]` to allocate buffers, random payloads, or reusable test data only once.
* Avoid shared mutable state unless reset per iteration.

Use representative input sizes such as:

```csharp
[Params(8, 256, 4096)]
public int Count { get; set; }
```

BenchmarkDotNet will run each benchmark for each parameter value.

---

## 4. Method Naming Conventions

Use descriptive names that communicate intent:

* `Parse_Short`
* `Parse_Long`
* `ComputeHash_Small`
* `ComputeHash_Large`
* `Serialize_Optimized`
* `Serialize_Baseline`

When comparing approaches, always list them clearly and tag one as the baseline.

---

## 5. Example Benchmark Fixture

```csharp
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;

namespace YourProject
{
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
public class SampleOperationBenchmark
{
[Params(8, 256, 4096)]
public int Count { get; set; }

private byte[] _payload;

[GlobalSetup]
public void Setup()
{
_payload = new byte[Count];
// deterministic initialization
}

[Benchmark(Baseline = true, Description = "Operation - baseline")]
public int Operation_Baseline() => SampleOperation.Process(_payload);

[Benchmark(Description = "Operation - optimized")]
public int Operation_Optimized() => SampleOperation.ProcessOptimized(_payload);
}
}
```

---

## 6. Reporting and CI

* Benchmark projects live exclusively under `tuning/`. They must not affect production builds.
* Heavy BenchmarkDotNet runs should *not* run in CI unless explicitly configured.
* Reports are produced by the benchmark runner and stored under the configured artifacts directory.

---

## 7. Additional Guidelines

* Keep benchmark fixtures focused and readable.
* Document non-obvious reasoning in short comments.
* Prefer realistic but deterministic data sets.
* When benchmarks reveal regressions or improvements, reference the associated PR or issue in a comment.
* Shared benchmark helpers belong in `tuning/` projects, not in production code.

---

## Final Notes

* Benchmarks are performance tests, not unit tests.
* Use `[Benchmark]` only for pure performance measurement.
* Avoid `MethodImplOptions.NoInlining` unless absolutely necessary.
* Use small sets of meaningful benchmark scenarios — avoid combinatorial explosion.
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
name: Globalization Ext. CI/CD Pipeline
name: Globalization Ext. CI Pipeline
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix line ending characters.

Static analysis reports wrong line ending characters. The file uses CRLF (\r\n) but the linter expects LF (\n). Ensure the file uses Unix-style line endings for consistency across the repository.

🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 1-1: wrong new line character: expected \n

(new-lines)

🤖 Prompt for AI Agents
In @.github/workflows/ci-pipeline.yml at line 1, The file
.github/workflows/ci-pipeline.yml (the workflow with "name: Globalization Ext.
CI Pipeline") currently has CRLF line endings; convert the file to Unix LF line
endings so the linter stops reporting errors—e.g., run a dos2unix on the file or
set your editor to use LF, commit the normalized file, and optionally add or
update a .gitattributes entry to enforce LF for *.yml files to prevent
recurrence.

on:
pull_request:
paths-ignore:
- .codecov
- .docfx
- .github
- .nuget
- '**.md'
branches: [main]
workflow_dispatch:
inputs:
configuration:
Expand All @@ -18,6 +13,9 @@ on:
- Debug
- Release

permissions:
contents: read

jobs:
build:
name: call-build
Expand Down Expand Up @@ -48,7 +46,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2022]
os: [ubuntu-24.04, windows-2025, ubuntu-24.04-arm, windows-11-arm]
configuration: [Debug, Release]
uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3
with:
Expand Down Expand Up @@ -78,17 +76,17 @@ jobs:
repository: codebeltnet/globalization
secrets: inherit

# codeql:
# name: call-codeql
# needs: [build,test]
# uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3
# with:
# projects: src/**/*.csproj
codeql:
name: call-codeql
needs: [build,test]
uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3
with:
projects: src/**/*.csproj

deploy:
if: github.event_name != 'pull_request'
name: call-nuget
needs: [build, pack, test, sonarcloud, codecov]
needs: [build, pack, test, sonarcloud, codecov, codeql]
uses: codebeltnet/jobs-nuget-push/.github/workflows/default.yml@v2
with:
version: ${{ needs.build.outputs.version }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 10.0.0
Version 10.0.1
Availability: .NET 10, .NET 9 and .NET Standard 2.0

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

Version 10.0.0
Availability: .NET 10, .NET 9 and .NET Standard 2.0

# ALM
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba
> [!NOTE]
> Changelog entries prior to version 9.0.1 was migrated from previous versions of Cuemon.Extensions.Globalization.

## [10.0.1] - 2026-01-22

This is a service update that focuses on package dependencies.

## [10.0.0] - 2025-11-13

This is a major release that focuses on adapting the latest `.NET 10` release (LTS) in exchange for current `.NET 8` (LTS).
Expand Down
48 changes: 0 additions & 48 deletions Codebelt.Extensions.Globalization.sln

This file was deleted.

11 changes: 11 additions & 0 deletions Codebelt.Extensions.Globalization.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/Codebelt.Extensions.Globalization/Codebelt.Extensions.Globalization.csproj" />
</Folder>
<Folder Name="/test/">
<Project Path="test/Codebelt.Extensions.Globalization.Tests/Codebelt.Extensions.Globalization.Tests.csproj" />
</Folder>
<Folder Name="/tooling/">
<Project Path="tooling/gse/gse.csproj" />
</Folder>
</Solution>
34 changes: 31 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

<PropertyGroup>
<IsTestProject>$(MSBuildProjectName.EndsWith('Tests'))</IsTestProject>
<IsBenchmarkProject>$(MSBuildProjectName.EndsWith('Benchmarks'))</IsBenchmarkProject>
<IsSourceProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src'))</IsSourceProject>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsSourceProject condition checks for projects starting with '$(MSBuildThisFileDirectory.ToLower())src', which expects a trailing backslash. MSBuildThisFileDirectory includes a trailing backslash on Windows but may behave differently on Linux. Consider explicitly adding a directory separator or normalizing the path. Example: $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) for Windows and $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src/')) for Unix-like systems, or use a more reliable path comparison approach.

Suggested change
<IsSourceProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src'))</IsSourceProject>
<RootDirLower>$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)')).ToLower()</RootDirLower>
<SrcRootLower>$(RootDirLower)src$([System.IO.Path]::DirectorySeparatorChar)</SrcRootLower>
<IsSourceProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(SrcRootLower)'))</IsSourceProject>

Copilot uses AI. Check for mistakes.
<IsToolingProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling'))</IsToolingProject>
Comment on lines +6 to +7
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same path comparison issue exists for IsToolingProject. MSBuildThisFileDirectory may not include a trailing separator in all scenarios, which could cause the StartsWith check to fail or match incorrectly. Consider using a more robust path comparison or explicitly handling directory separators for cross-platform compatibility.

Suggested change
<IsSourceProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src'))</IsSourceProject>
<IsToolingProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling'))</IsToolingProject>
<IsSourceProject>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)$(DirectorySeparatorChar)').ToLower().StartsWith($([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)src$(DirectorySeparatorChar)').ToLower()))</IsSourceProject>
<IsToolingProject>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)$(DirectorySeparatorChar)').ToLower().StartsWith($([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)tooling$(DirectorySeparatorChar)').ToLower()))</IsToolingProject>

Copilot uses AI. Check for mistakes.
<IsLinux>$([MSBuild]::IsOSPlatform('Linux'))</IsLinux>
<IsWindows>$([MSBuild]::IsOSPlatform('Windows'))</IsWindows>
<IsMainAuthor Condition="'$(EMAIL)' == 'michael@geekle.io'">true</IsMainAuthor>
<SkipSignAssembly>false</SkipSignAssembly>
<PackageReleaseNotesFile>..\..\.nuget\$(MSBuildProjectName)\PackageReleaseNotes.txt</PackageReleaseNotesFile>
<LangVersion>latest</LangVersion>
</PropertyGroup>

Expand All @@ -14,9 +18,9 @@
<Deterministic>true</Deterministic>
</PropertyGroup>

<PropertyGroup Condition="'$(IsTestProject)' == 'false'">
<PropertyGroup Condition="'$(IsSourceProject)' == 'true'">
<TargetFrameworks>net10.0;net9.0;netstandard2.0</TargetFrameworks>
<Copyright>Copyright © Geekle 2025. All rights reserved.</Copyright>
<Copyright>Copyright © Geekle 2025-2026. All rights reserved.</Copyright>
<Authors>gimlichael</Authors>
<Company>Geekle</Company>
<Product>Extensions for Globalization API by Codebelt</Product>
Expand All @@ -42,7 +46,7 @@
<SourceRoot Include="$(NuGetPackageRoot)" RepositoryUrl="https://github.com/codebeltnet/globalization" />
</ItemGroup>

<ItemGroup Condition="'$(IsTestProject)' == 'false'">
<ItemGroup Condition="'$(IsSourceProject)' == 'true'">
<PackageReference Include="MinVer" PrivateAssets="all" />
<None Include="..\..\.nuget\icon.png" Pack="true" Visible="false" PackagePath="\" />
<None Include="..\..\.nuget\$(MSBuildProjectName)\README.md" Pack="true" PackagePath="\" />
Expand Down Expand Up @@ -70,6 +74,12 @@
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
</PropertyGroup>

<PropertyGroup Condition="'$(IsToolingProject)' == 'true'">
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
<SonarQubeExclude>true</SonarQubeExclude>
</PropertyGroup>

<ItemGroup Condition="'$(IsTestProject)' == 'true'">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit.v3" />
Expand All @@ -89,4 +99,22 @@
<PackageReference Include="Codebelt.Extensions.Xunit" />
</ItemGroup>

<PropertyGroup Condition="'$(IsBenchmarkProject)' == 'true'">
<TargetFrameworks>net10.0;net9.0;netstandard2.0</TargetFrameworks>
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmark projects target netstandard2.0, which is unusual for benchmark projects. BenchmarkDotNet benchmarks are typically console applications that should target specific runtime frameworks (like net10.0 and net9.0) but not netstandard2.0, as netstandard2.0 is a specification for libraries, not executable applications. Consider removing netstandard2.0 from the TargetFrameworks list for benchmark projects.

Suggested change
<TargetFrameworks>net10.0;net9.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net10.0;net9.0</TargetFrameworks>

Copilot uses AI. Check for mistakes.
<IsPackable>false</IsPackable>
<RunAnalyzers>false</RunAnalyzers>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<SonarQubeExclude>true</SonarQubeExclude>
<AnalysisLevel>none</AnalysisLevel>
<NoWarn>NU1701,NU1902,NU1903</NoWarn>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<NuGetAudit>false</NuGetAudit>
</PropertyGroup>

<ItemGroup Condition="'$(IsBenchmarkProject)' == 'true'">
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" />
</ItemGroup>
Comment on lines +115 to +118
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BenchmarkDotNet package references are missing version definitions in Directory.Packages.props. Since this project uses central package management (ManagePackageVersionsCentrally is set to true), all package versions must be defined in Directory.Packages.props. Add PackageVersion entries for both "BenchmarkDotNet" and "BenchmarkDotNet.Diagnostics.Windows" to Directory.Packages.props.

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +118
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

BenchmarkDotNet.Diagnostics.Windows is Windows-only.

This package provides Windows-specific diagnostics (ETW tracing, hardware counters via Windows Performance Counters). Including it unconditionally will cause restore failures or runtime issues on Linux/macOS builds. Consider conditionalizing this reference.

Proposed fix with platform condition
   <ItemGroup Condition="'$(IsBenchmarkProject)' == 'true'">
     <PackageReference Include="BenchmarkDotNet" />
-    <PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" />
+    <PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Condition="'$(IsWindows)' == 'true'" />
   </ItemGroup>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ItemGroup Condition="'$(IsBenchmarkProject)' == 'true'">
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" />
</ItemGroup>
<ItemGroup Condition="'$(IsBenchmarkProject)' == 'true'">
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Condition="'$(IsWindows)' == 'true'" />
</ItemGroup>
🤖 Prompt for AI Agents
In `@Directory.Build.props` around lines 115 - 118, The unconditional
PackageReference to BenchmarkDotNet.Diagnostics.Windows inside the ItemGroup for
IsBenchmarkProject causes cross-platform restore failures; modify the project
file so the PackageReference for BenchmarkDotNet.Diagnostics.Windows is only
included on Windows builds (e.g., add a Condition referencing the
OS/RuntimeIdentifier) while leaving BenchmarkDotNet unconditional. Locate the
ItemGroup with Condition "'$(IsBenchmarkProject)' == 'true'" and add a
platform-specific Condition to the PackageReference element named
BenchmarkDotNet.Diagnostics.Windows so non-Windows builds skip that package.


</Project>
Loading
Loading