From b47a0bb58538766299518e9438554e82dea7e2f1 Mon Sep 17 00:00:00 2001 From: Francois Raminosona Date: Tue, 8 Sep 2026 15:24:06 +0200 Subject: [PATCH 1/4] Add community-health files: Code of Conduct, Security Policy, Contributing Guide, CODEOWNERS, PR template, Copilot instructions Aligns this repo with Plugin.Bluetooth's standard for OSS project hygiene. --- .github/CODEOWNERS | 1 + .github/copilot-instructions.md | 121 +++++++++++++++++++++++++++++++ .github/pull_request_template.md | 50 +++++++++++++ CODE_OF_CONDUCT.md | 55 ++++++++++++++ CONTRIBUTING.md | 35 +++++++++ SECURITY.md | 13 ++++ 6 files changed, 275 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/copilot-instructions.md create mode 100644 .github/pull_request_template.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3721bae --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @framinosona diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..13a5e02 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,121 @@ +# GitHub Copilot Instructions for Plugin.BaseTypeExtensions + +## Project Overview + +**Plugin.BaseTypeExtensions** is a collection of extension methods for .NET base types — +strings, numerics, enums, collections, dates/times, comparables, reflection, and a handful of +other primitives — aimed at productivity, type safety, and readability in modern .NET +applications. + +**Key Features:** +- Null-safe string operations and character manipulation +- Generic numeric operations for all `INumber` types (angles, percentages, clamping) +- Enum flag manipulation, description retrieval, and general enum utilities +- Dictionary/list/enumerable extensions, including synchronization-aware variants +- `DateTime`/`DateOnly`/`TimeOnly`/`TimeSpan` utilities +- Reflection and assembly helpers (type discovery, attribute scanning, embedded resources) +- `IComparable` extensions plus standalone `ComparableTools`/`NumericRangeTools` helpers +- Smaller extensions for `Byte`, `Guid`, `Version`, `Uri`, `Task`, `TaskCompletionSource`, + `CancellationToken`, `SemaphoreSlim`, `Random`, `Exception` + +## Technology Stack + +- **Framework:** .NET 9.0 (SDK `9.0.305`, see `global.json`) +- **Language:** C# (latest) +- **Testing:** xUnit + FluentAssertions (v7.x — stays open source indefinitely per Directory.Packages.props comment) +- **Package Management:** Central Package Management (`Directory.Packages.props`) +- **CI/CD:** GitHub Actions +- **Documentation:** DocFX (`Plugin.BaseTypeExtensions.Docs/`, published to GitHub Pages) + +## Project Structure + +``` +Plugin.BaseTypeExtensions/ +├── .github/ +│ ├── workflows/ci.yml +│ └── copilot-instructions.md # This file +├── Plugin.BaseTypeExtensions/ # Main library — one file per extended type/category +│ ├── StringExtensions.cs +│ ├── NumericExtensions.cs / NumericRangeTools.cs +│ ├── EnumExtensions.cs +│ ├── DictionaryExtensions.cs / ListExtensions.cs / EnumerableExtensions.cs +│ ├── ConcurrentDictionaryExtensions.cs / SemaphoreSlimExtensions.cs +│ ├── DateTimeExtensions.cs / DateOnlyExtensions.cs / TimeOnlyExtensions.cs / TimeSpanExtensions.cs +│ ├── ComparableExtensions.cs / ComparableTools.cs +│ ├── ReflectionExtensions.cs / AssemblyExtensions.cs +│ ├── ByteExtensions.cs / GuidExtensions.cs / VersionExtensions.cs / UriExtensions.cs +│ ├── TaskExtensions.cs / TaskCompletionSourceExtensions.cs / CancellationTokenExtensions.cs +│ ├── RandomExtensions.cs / ExceptionExtensions.cs +├── Plugin.BaseTypeExtensions.Tests/ # xUnit tests, one file per source file +└── Plugin.BaseTypeExtensions.Docs/ # DocFX documentation site +``` + +## Development Setup + +### Prerequisites +- .NET SDK 9.0.305 or later (see `global.json`) + +### Build Commands +```bash +dotnet restore Plugin.BaseTypeExtensions.slnx +dotnet build Plugin.BaseTypeExtensions.slnx --configuration Release --no-restore +dotnet test Plugin.BaseTypeExtensions.Tests/Plugin.BaseTypeExtensions.Tests.csproj --configuration Release --no-build +``` + +## Code Organization Patterns + +- **One file per extended type or tightly-related group** — `*Extensions.cs`, named after the + type it extends (e.g. `StringExtensions.cs`). Don't mix categories in one file. +- **Generic numeric code uses `INumber`** constraints instead of per-type overloads. +- **Standalone helpers that don't read naturally as extension methods** (e.g. clamping across a + pair of bounds) live in `ComparableTools`/`NumericRangeTools` as static classes — keep that + separation rather than bolting them onto `ComparableExtensions`/`NumericExtensions`. + +## Coding Standards + +### Naming Conventions (enforced by `.editorconfig`) +- Classes/structs/interfaces/methods/properties/constants: PascalCase +- Private fields: `_camelCase` +- Parameters/locals: camelCase + +### Style Guidelines +- Line length: max 240 characters; indentation: 4 spaces; line endings: LF +- Prefer `var` when the type is apparent +- Braces always required, even for single-line statements +- `System` usings first, separated from the rest + +### Documentation Requirements +- XML docs (``, ``, ``, ``) required on all public APIs + +## Testing Guidelines + +- **Framework:** xUnit, assertions via FluentAssertions (v7.2.2, open-source) +- **File naming:** `{ClassName}Tests.cs`, matching the source file +- **Test naming:** `MethodName_Scenario_ExpectedBehavior` +- Cover success paths, failure paths, and boundary conditions (null, empty, min/max values) +- Use `[Theory]`/`[InlineData]` for multiple similar cases; keep tests independent of each other + +## Dependencies + +### Production +- `Microsoft.Extensions.Logging.Abstractions` (10.0.10) + +### Development +- `xunit` (2.9.3), `xunit.runner.visualstudio` (3.1.5), `FluentAssertions` (7.2.2) +- `coverlet.collector` / `coverlet.msbuild` (10.0.1) +- `Microsoft.NET.Test.Sdk` (18.8.1), `JetBrains.Annotations` (2025.2.0) +- `Microsoft.CodeAnalysis.NetAnalyzers` (10.0.302), `Microsoft.SourceLink.GitHub` (10.0.201) + +(Versions drift via Dependabot — check `Directory.Packages.props` for current values rather than +trusting this list long-term.) + +## CI/CD Pipeline + +`ci.yml` runs: version generation → build/test/package → publish test artifacts → DocFX build and +deploy to GitHub Pages → publish to NuGet.org (on `main`) → tag & GitHub release. + +## Useful Resources + +- **Repository:** https://github.com/laerdal/Plugin.BaseTypeExtensions +- **NuGet Package:** https://www.nuget.org/packages/Plugin.BaseTypeExtensions +- **Documentation:** https://laerdal.github.io/Plugin.BaseTypeExtensions/ diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..7e2fdb8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,50 @@ +## Summary + +Describe the change in 2-6 lines. + +## Why + +Explain the reason for this change. + +## Change Type + +- [ ] Feature +- [ ] Bug fix +- [ ] Refactor +- [ ] Docs only +- [ ] CI/build/tooling + +## Affected Areas + +- [ ] String / Numeric / Enum extensions +- [ ] Collection extensions (Dictionary / List / Enumerable / ConcurrentDictionary) +- [ ] Temporal extensions (DateTime / DateOnly / TimeOnly / TimeSpan) +- [ ] Reflection / Assembly extensions +- [ ] Comparison extensions +- [ ] Tests +- [ ] Docs + +## Behavior And Compatibility + +- [ ] Public API changed +- [ ] Exception behavior changed +- [ ] No externally visible behavior change + +If any box above is checked, describe impact: + +## Tests + +- [ ] Unit tests added/updated +- [ ] Not applicable (explain) + +## Documentation + +- [ ] README feature list updated +- [ ] XML docs added/updated +- [ ] Not applicable (explain) + +## Checklist + +- [ ] Commit messages follow Conventional Commits (`type(scope): subject`) +- [ ] CI passes +- [ ] Change is scoped to one logical unit of work diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5d27e55 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,55 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a +harassment-free experience for everyone, regardless of age, body size, visible or invisible +disability, ethnicity, sex characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, +and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from + the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their + explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Maintainers are responsible for clarifying and enforcing standards of acceptable behavior and +will take appropriate and fair corrective action in response to any behavior deemed +inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces for this project, and also applies when +an individual is officially representing the project in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the +maintainer, [@framinosona](https://github.com/framinosona), via a private message on GitHub. All +complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), +version 2.1, available at +https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2c862d1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,35 @@ +# Contributing + +## Branching + +Work off a short-lived feature branch cut from `main`. Open a PR early and mark it as a Draft +if it's still in progress. + +## Commit convention + +This repo uses [Conventional Commits](https://www.conventionalcommits.org/): `type(scope): subject`, +imperative mood, <= 72 characters. Common types: `feat`, `fix`, `docs`, `style`, `refactor`, +`perf`, `test`, `chore`. Scope is optional but recommended (e.g. the extension category touched). + +## Pull requests + +Fill out the PR template. Keep each PR scoped to one logical change. CI must pass before merge. + +## Code style + +Follow this repo's `.editorconfig`. Prefer clarity over cleverness; avoid unnecessary abstraction +— this is a firm preference, not a suggestion. Keep each extension category in its own +`*Extensions.cs` file named after the type it extends (e.g. `StringExtensions.cs`, +`DateTimeExtensions.cs`) — don't mix categories in one file. See +`.github/copilot-instructions.md` for the existing conventions. + +## Testing + +All new functionality needs xUnit tests in `Plugin.BaseTypeExtensions.Tests`, using +FluentAssertions for assertions. Test both success and failure paths and boundary conditions +(null, empty, min/max values). + +## Documentation + +Public APIs need XML doc comments (``, ``, ``, ``). If you +add a new extension category, update the README's feature list too. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d34dc2f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security vulnerabilities privately using +[GitHub's private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) +(Security tab → "Report a vulnerability") rather than opening a public issue. + +We'll acknowledge reports as soon as possible and coordinate disclosure once a fix is available. + +## Supported Versions + +Only the latest published release is actively supported. From 5e06ba1ba3d6bebdb11113b7c562efd48e3b5eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Raminosona?= Date: Tue, 8 Sep 2026 15:29:31 +0200 Subject: [PATCH 2/4] Update Microsoft.Extensions.Logging.Abstractions version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: François Raminosona --- .github/copilot-instructions.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 13a5e02..dcf4cb2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -98,8 +98,7 @@ dotnet test Plugin.BaseTypeExtensions.Tests/Plugin.BaseTypeExtensions.Tests.cspr ## Dependencies ### Production -- `Microsoft.Extensions.Logging.Abstractions` (10.0.10) - +- `Microsoft.Extensions.Logging.Abstractions` (10.0.11) ### Development - `xunit` (2.9.3), `xunit.runner.visualstudio` (3.1.5), `FluentAssertions` (7.2.2) - `coverlet.collector` / `coverlet.msbuild` (10.0.1) From d154e50767aed9d58468c304830300a3981fd8ef Mon Sep 17 00:00:00 2001 From: Francois Raminosona Date: Tue, 8 Sep 2026 15:49:55 +0200 Subject: [PATCH 3/4] fix(docs): correct CoC contact method and copilot-instructions drift CODE_OF_CONDUCT.md pointed at GitHub private messages, which don't exist as a feature. copilot-instructions.md had a stale Logging.Abstractions version and was missing StreamExtensions.cs from the project structure and feature list. Also folds in the code-review agent skill (previously a separate PR) so this repo's community-health-files work lands as one PR. --- .github/copilot-instructions.md | 6 ++--- .github/skills/code-review/SKILL.md | 36 +++++++++++++++++++++++++++++ CODE_OF_CONDUCT.md | 4 ++-- 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 .github/skills/code-review/SKILL.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index dcf4cb2..42987f8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -15,7 +15,7 @@ applications. - `DateTime`/`DateOnly`/`TimeOnly`/`TimeSpan` utilities - Reflection and assembly helpers (type discovery, attribute scanning, embedded resources) - `IComparable` extensions plus standalone `ComparableTools`/`NumericRangeTools` helpers -- Smaller extensions for `Byte`, `Guid`, `Version`, `Uri`, `Task`, `TaskCompletionSource`, +- Smaller extensions for `Byte`, `Guid`, `Version`, `Uri`, `Stream`, `Task`, `TaskCompletionSource`, `CancellationToken`, `SemaphoreSlim`, `Random`, `Exception` ## Technology Stack @@ -43,7 +43,7 @@ Plugin.BaseTypeExtensions/ │ ├── DateTimeExtensions.cs / DateOnlyExtensions.cs / TimeOnlyExtensions.cs / TimeSpanExtensions.cs │ ├── ComparableExtensions.cs / ComparableTools.cs │ ├── ReflectionExtensions.cs / AssemblyExtensions.cs -│ ├── ByteExtensions.cs / GuidExtensions.cs / VersionExtensions.cs / UriExtensions.cs +│ ├── ByteExtensions.cs / GuidExtensions.cs / VersionExtensions.cs / UriExtensions.cs / StreamExtensions.cs │ ├── TaskExtensions.cs / TaskCompletionSourceExtensions.cs / CancellationTokenExtensions.cs │ ├── RandomExtensions.cs / ExceptionExtensions.cs ├── Plugin.BaseTypeExtensions.Tests/ # xUnit tests, one file per source file @@ -98,7 +98,7 @@ dotnet test Plugin.BaseTypeExtensions.Tests/Plugin.BaseTypeExtensions.Tests.cspr ## Dependencies ### Production -- `Microsoft.Extensions.Logging.Abstractions` (10.0.11) +- `Microsoft.Extensions.Logging.Abstractions` (10.0.10) ### Development - `xunit` (2.9.3), `xunit.runner.visualstudio` (3.1.5), `FluentAssertions` (7.2.2) - `coverlet.collector` / `coverlet.msbuild` (10.0.1) diff --git a/.github/skills/code-review/SKILL.md b/.github/skills/code-review/SKILL.md new file mode 100644 index 0000000..c2a4bae --- /dev/null +++ b/.github/skills/code-review/SKILL.md @@ -0,0 +1,36 @@ +--- +name: code-review +description: Use for reviewing pull requests on Plugin.BaseTypeExtensions — checks file-per-category organization, generic numeric constraints, and null/boundary safety on new extension methods. +--- + +# Code Review — Plugin.BaseTypeExtensions + +## Checklist for new or changed extension methods + +- [ ] **One file per extended type/category**: a new extension method belongs in the + `*Extensions.cs` file named after the type it extends (e.g. a new `DateTime` helper goes in + `DateTimeExtensions.cs`, not bundled into an unrelated file). A PR mixing categories into + one file, or adding a same-named method to two files, is a real finding. +- [ ] **Generic numeric code uses `INumber`**: new numeric operations should use generic math + constraints rather than adding per-type overloads (`int`, `double`, `float`, ...) — flag + copy-pasted overloads as a sign the generic constraint was skipped. +- [ ] **Null/boundary safety**: string, collection, and comparable extensions handle `null` + input explicitly (per this library's "null-safe" positioning) rather than throwing an + unhandled `NullReferenceException`. +- [ ] **Standalone helpers stay standalone**: logic that doesn't read naturally as an extension + method (e.g. clamping across a pair of bounds) belongs in `ComparableTools`/ + `NumericRangeTools` as static helpers — don't bolt it onto `ComparableExtensions`/ + `NumericExtensions` as an awkward extension method just to keep the "everything is an + extension" pattern. +- [ ] **XML docs**: new public members have accurate ``/``/``/ + `` docs. +- [ ] **README feature list**: a new extension category should be reflected there. + +## What to flag as a real risk, not a nit + +- A new extension method that duplicates behavior already available in `System.Linq` or the + BCL without a clear reason (this library's value is filling *gaps*, not re-wrapping existing + APIs). +- Breaking changes to existing extension method signatures — this is a published NuGet package + consumed by other repos in this org (e.g. `Plugin.ExceptionListeners`), so a signature change + is a semver-relevant, not a routine tweak. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5d27e55..0a20c72 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -45,8 +45,8 @@ an individual is officially representing the project in public spaces. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the -maintainer, [@framinosona](https://github.com/framinosona), via a private message on GitHub. All -complaints will be reviewed and investigated promptly and fairly. +maintainer, [@framinosona](https://github.com/framinosona), via the contact details listed on +their GitHub profile. All complaints will be reviewed and investigated promptly and fairly. ## Attribution From 406f8e4766738344c630e7beee99b0f33923eae3 Mon Sep 17 00:00:00 2001 From: Francois Raminosona Date: Tue, 8 Sep 2026 15:52:16 +0200 Subject: [PATCH 4/4] fix(docs): correct remaining dependency versions in copilot-instructions My previous commit wrongly reverted Logging.Abstractions to a stale value read from an outdated local main. Restored 10.0.11 (correct, already matched by the prior Copilot Autofix commit) and fixed three more genuinely stale versions against this branch's actual Directory.Packages.props: xunit.runner.visualstudio (3.1.5 -> 4.0.0), Microsoft.NET.Test.Sdk (18.8.1 -> 18.9.0), Microsoft.CodeAnalysis.NetAnalyzers (10.0.302 -> 10.0.400). --- .github/copilot-instructions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 42987f8..b58d996 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -98,12 +98,12 @@ dotnet test Plugin.BaseTypeExtensions.Tests/Plugin.BaseTypeExtensions.Tests.cspr ## Dependencies ### Production -- `Microsoft.Extensions.Logging.Abstractions` (10.0.10) +- `Microsoft.Extensions.Logging.Abstractions` (10.0.11) ### Development -- `xunit` (2.9.3), `xunit.runner.visualstudio` (3.1.5), `FluentAssertions` (7.2.2) +- `xunit` (2.9.3), `xunit.runner.visualstudio` (4.0.0), `FluentAssertions` (7.2.2) - `coverlet.collector` / `coverlet.msbuild` (10.0.1) -- `Microsoft.NET.Test.Sdk` (18.8.1), `JetBrains.Annotations` (2025.2.0) -- `Microsoft.CodeAnalysis.NetAnalyzers` (10.0.302), `Microsoft.SourceLink.GitHub` (10.0.201) +- `Microsoft.NET.Test.Sdk` (18.9.0), `JetBrains.Annotations` (2025.2.0) +- `Microsoft.CodeAnalysis.NetAnalyzers` (10.0.400), `Microsoft.SourceLink.GitHub` (10.0.201) (Versions drift via Dependabot — check `Directory.Packages.props` for current values rather than trusting this list long-term.)