Contributing
Thanks for considering a contribution. This project takes small, focused pull requests over large rewrites — see ARCHITECTURE.md for the shape of the codebase before diving in, and STYLEGUIDE.md for the code style, which the analyzers only partly enforce.
Build expectations
Warnings are errors.
Directory.Build.propssetsTreatWarningsAsErrors, with a curatedAnalysisMode=Allanalyzer set (NetAnalyzers plus Meziantou). A PR that doesn't build clean locally won't build clean in CI either — run a full build before pushing:dotnet build ExcelReader.slnx --configuration ReleasePublic API changes require a
PublicAPI.Unshipped.txtentry.Microsoft.CodeAnalysis.PublicApiAnalyzersis active and fails the build on any unrecorded public member. If you add, change, or remove anything public, updatesrc/ExcelReader.Core/PublicAPI/PublicAPI.Unshipped.txt. A bot promotesUnshipped→Shippedautomatically after each release — don't editShipped.txtby hand.Minor versions don't break callers. docs/guide/versioning.md is the policy; in practice:
- A new member on a library-implemented interface (
IExcelWorkbook,IExcelSheet,IExcelRowEnumerator,IWorkbookWriter,ISheetWriter,ITableSheetWriter,IRowWriter) gets a default implementation. A user-implemented one (IExcelCellConverter,IExcelCellWriter,IExcelRowMap,IExcelRecordMap,IRowRecord,IRowAccumulator) gets no new member a user would have to implement. - A feature only some formats support goes on the concrete types first, or on its own interface
the way
ITableSheetWritersits besideISheetWriter. - New inputs go on the options object or a new overload, never as a new optional parameter on an existing method.
dotnet packruns package validation againstPackageValidationBaselineVersionin theExcelReader.CoreandExcelReader.Arrowprojects, and CI runs it on every pull request. After each release, move the baseline to the version just published.
- A new member on a library-implemented interface (
Tests are required for behavior changes. Run the suite before opening a PR:
dotnet test tests/ExcelReader.Tests/ExcelReader.Tests.csproj --configuration ReleaseUntrusted-input paths (the CFB/OLE, BIFF8, BIFF12, and ZIP parsers) get extra scrutiny — new parsing code should have a corresponding limit/fuzz-safety test in
tests/ExcelReader.Tests/Reader/ReaderLimitTests.csortests/ExcelReader.Tests/FuzzTests.cswhere relevant. Read STYLEGUIDE.md § Untrusted Input before touching a parser: every length, offset, and size read from the file must be bounded before it drives an allocation.Guide examples compile. A C# block in
docs/guidemarked<!-- snippet: name -->is copied from#region nameindocs/snippets, which builds with the solution. Edit the region, then rewrite the guides; CI fails if they differ:python docs/snippets/sync.py
Commit messages
Use Conventional Commits: type(scope): summary, imperative
mood, lowercase summary, no trailing period. Common types in this repo: feat, fix, refactor,
perf, test, docs, chore, ci, build. The scope is usually the area touched (crypto,
reader, writer, parser, native). Example: fix(crypto): bound VerifyPassword's encryptedVerifierHash length.
Pull requests
- One focused change per PR — don't batch unrelated fixes into one commit or one PR.
- If a change is user-visible (new API, behavior change, performance claim), mention it in the PR description; the README's benchmark tables and changelog are updated separately, not as part of every PR.
- CI runs on Linux, Windows, and macOS — a change that only builds on one OS isn't ready to merge.
Reporting bugs / requesting features
Use GitHub Issues for bugs and feature requests. For suspected security vulnerabilities, do not open a public issue — see SECURITY.md for the private reporting channel.
Code of conduct
This project follows the Code of Conduct.