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.props sets TreatWarningsAsErrors, with a curated AnalysisMode=All analyzer 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 Release
    
  • Public API changes require a PublicAPI.Unshipped.txt entry. Microsoft.CodeAnalysis.PublicApiAnalyzers is active and fails the build on any unrecorded public member. If you add, change, or remove anything public, update src/ExcelReader.Core/PublicAPI/PublicAPI.Unshipped.txt. A bot promotes Unshipped → Shipped automatically after each release — don't edit Shipped.txt by 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 ITableSheetWriter sits beside ISheetWriter.
    • New inputs go on the options object or a new overload, never as a new optional parameter on an existing method.
    • dotnet pack runs package validation against PackageValidationBaselineVersion in the ExcelReader.Core and ExcelReader.Arrow projects, and CI runs it on every pull request. After each release, move the baseline to the version just published.
  • Tests are required for behavior changes. Run the suite before opening a PR:

    dotnet test tests/ExcelReader.Tests/ExcelReader.Tests.csproj --configuration Release
    

    Untrusted-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.cs or tests/ExcelReader.Tests/FuzzTests.cs where 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/guide marked <!-- snippet: name --> is copied from #region name in docs/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.