Skip to content

Latest commit

 

History

History
147 lines (102 loc) · 9.11 KB

CONTRIBUTING.md

File metadata and controls

147 lines (102 loc) · 9.11 KB

Contributing

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

Best practices

  • Use Windows PowerShell or PowerShell Core (including on Linux/OSX) to run .ps1 scripts. Some scripts set environment variables to help you, but they are only retained if you use PowerShell as your shell.

Prerequisites

All dependencies can be installed by running the init.ps1 script at the root of the repository using Windows PowerShell or PowerShell Core (on any OS). Some dependencies installed by init.ps1 may only be discoverable from the same command line environment the init script was run from due to environment variables, so be sure to launch Visual Studio or build the repo from that same environment. Alternatively, run init.ps1 -InstallLocality Machine (which may require elevation) in order to install dependencies at machine-wide locations so Visual Studio and builds work everywhere.

The only prerequisite for building, testing, and deploying from this repository is the .NET SDK. You should install the version specified in global.json or a later version within the same major.minor.Bxx "hundreds" band. For example if 2.2.300 is specified, you may install 2.2.300, 2.2.301, or 2.2.310 while the 2.2.400 version would not be considered compatible by .NET SDK. See .NET Core Versioning for more information.

Package restore

The easiest way to restore packages may be to run init.ps1 which automatically authenticates to the feeds that packages for this repo come from, if any. dotnet restore or nuget restore also work but may require extra steps to authenticate to any applicable feeds.

Building

This repository can be built on Windows, Linux, and OSX.

Building, testing, and packing this repository can be done by using the standard dotnet CLI commands (e.g. dotnet build, dotnet test, dotnet pack, etc.).

Testing

You can use dotnet test to build and/or test the repo.

There may be tests that are known to be unstable or have special requirements. These can be avoided by running tests using the dotnet-test-cloud.ps1 script after running dotnet build.

To build both managed and NativeAOT tests, run dotnet build tools/dirs.proj -t:build,pack,publish -c Release. Then run ./tools/dotnet-test-cloud.ps1 -Configuration Release -IncludeNativeAOT. The test/dirs.proj traversal discovers and publishes NativeAOT tests through GetNativeAOTTestVariants. Nerdbank.MessagePack.TUnit opts into this with PublishNativeAOTTests for net9.0 and net10.0 (not net8.0 until microsoft/testfx#7153 is fixed). Other test projects remain managed-only via the default <PublishNativeAOTTests>false</PublishNativeAOTTests> in test/Directory.Build.props. Keep all test projects in the solution for managed runs. The cloud test script runs the solution, then uses tools/Get-NativeAOTTestProjects.ps1 to locate the expected NativeAOT images. To publish the NativeAOT tests for a single framework and RID, run dotnet publish test/Nerdbank.MessagePack.TUnit/Nerdbank.MessagePack.TUnit.csproj -c Release -f net10.0 -r <RID> -p:NativeAOT=true. NativeAOT builds of the TUnit project use separate nativeaot intermediate and output directories, so they do not disturb the managed build. Test builds keep dynamic code, startup hooks, and event tracing enabled for managed code coverage. The ConfigureNativeAOTTestFeatures target disables those features only in the native compiler's publish-time inputs, without rewriting the managed runtime configuration. The target preserves other runtime feature options, including each test project's globalization setting; the TUnit project explicitly enables full globalization. Test builds retain only English satellite resources. Shipping libraries targeting .NET 8 or later opt into NativeAOT compatibility analysis with IsAotCompatible. The test/AotCompatibilityTest project complements those analyzers by rooting the shipping assembly and passing it through the NativeAOT compiler during every traversal publish. Add each shipping assembly that must be validated as a TrimmerRootAssembly there, and keep this project publishable in test/dirs.proj. Do not add TrimmerRootAssembly to test/AotNativeConsole: it is the NativeAOT image size guard, and rooting the library there inflates the image. Root Directory.Build.props supplies project-reference defaults for both traversal and SDK projects that remove the _IsPublishing global property for managed dependencies, avoiding duplicate project instances that write to the same outputs during parallel publishing.

Releases

Use nbgv tag to create a tag for a particular commit that you mean to release. Learn more about nbgv and its tag and prepare-release commands.

Push the tag.

GitHub Actions

When your repo is hosted by GitHub and you are using GitHub Actions, you should create a GitHub Release using the standard GitHub UI. Having previously used nbgv tag and pushing the tag will help you identify the precise commit and name to use for this release.

After publishing the release, the .github/workflows/release.yml workflow will be automatically triggered, which will:

  1. Find the most recent .github/workflows/build.yml GitHub workflow run of the tagged release.
  2. Upload the deployables-Linux artifact from that workflow run to your GitHub Release.
  3. Any nuget packages in the deployables-Linux artifact will be pushed to nuget.org.

The workflow is written to leverage NuGet.org Trusted Publishing. You should set NUGET_USER as a repo secret to satisfy Trusted Publishing requirements.

Azure Pipelines

When your repo builds with Azure Pipelines, use the azure-pipelines/release.yml pipeline. Trigger the pipeline by adding the auto-release tag on a run of your main azure-pipelines.yml pipeline.

Tutorial and API documentation

Markup links are checked in CI with mlc. Run mlc from the repository root to use the same settings, configured in .mlc.toml.

The site at https://aarnott.github.io/Nerdbank.MessagePack builds from this repo's docfx/ directory. The site is updated on every push to the main branch.

You can make changes and host the site locally to preview them by switching to that directory and running the dotnet docfx --serve command. After making a change, you can rebuild the docs site while the localhost server is running by running dotnet docfx again from a separate terminal.

Learn more about docfx.

Updating dependencies

This repo uses Renovate to keep dependencies current. Configuration is in the .github/renovate.json file. Learn more about configuring Renovate.

When changing the renovate.json file, follow these validation steps.

If Renovate is not creating pull requests when you expect it to, check that the Renovate GitHub App is configured for your account or repo.

Merging latest from Library.Template

Maintaining your repo based on this template

The best way to keep your repo in sync with Library.Template's evolving features and best practices is to periodically merge the template into your repo:

git fetch
git checkout origin/main
./tools/MergeFrom-Template.ps1
# resolve any conflicts, then commit the merge commit.
git push origin -u HEAD

Code editing guidelines

Monitoring and mitigating code gen size

Optimized NativeAOT targeting is a very important feature of this library. Not just that it's possible, but that the emitted code size is reasonably small. We use PR/CI checks in our GitHub workflow to guard the emitted code size so that it does not creep up without intentional review.

We use the sizoscope .NET CLI tool to understand what contributes to our NativeAOT output size so we can understand what we can do to reduce the emitted binary size.

There are two very important things you can do to keep emitted binary size down:

  1. Avoid using generic types and methods with user-defined types type arguments, especially if they might be value types. This often manifests as avoiding Linq extension methods when any type argument is not known to be a ref type. Value type arguments force unique native code to be emitted, multiplying the size of the native code.
  2. When defining generic types that will close over user-defined types, make them classes instead of structs. This will help users of these types to follow guideline number one in this list.
  3. Avoid declaring record where class or struct would suffice. The extra IEquatable<T> implementation bloats the code size, especially on generic types.