Distributing C# Code Standards with a Nuget Package

My deep dive into Nuget packages made me ask a new question: Can I use Nuget packages to deploy code standards for C#?

It’s Good To Have Standards

Generally, the goal of coding standards is to produce a uniform code base. My go-to explanation of what coding standards are is this: “If you look at the code, you should not be able to tell who wrote it.”

Linting is, to some degree, an extension of formatting. Linting applies a level of analysis to your code, identifying potential runtime problems. Linting rules typically derive from various best practices which can (and do) change over time, especially for languages in active development. Thing of it this way: C++ has not changed much in the last 20 years, but C#, JavaScript, and, by association, TypeScript, have all seen active changes in recent history.

There are many tools for each language that offer linting and formatting. I have developed some favorites:

  • Formatting
    • dotnet format – C#
    • prettier – Javascript/TypeScript
    • black – Python
  • Linting
    • Sonar – support for various languages
    • eslint – Javascript/TypeScript
    • flake8 – Python

Regardless of your tool of choice, being able to deploy standards across multiple projects has been difficult. If your projects live in the same repository, you can store settings files in a common location, but for projects across multiple repositories, the task becomes harder.

Nuget to the Rescue!

I spent a good amount of time in the last week relearning Nuget. In this journey, it occurred to me that I could create a Nuget package that only contained references and settings for our C# standards. So I figured I’d give it a shot. Turns out, it was easier than I anticipated.

There are 3 files in the project:

  • The csproj file – MyStandards.CSharp.csproj
  • main.editorconfig
  • MyStandards.CSharp.props

I put the content of the CSProj and Props files below. main.editorconfig will get copied into the project where this package is referenced as .editorconfig. You can read all about customizing .editorconfig here.

MyStandards.CSharp.csproj

<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>netstandard2.1</TargetFramework>
		<Nullable>enable</Nullable>
		<IncludeBuildOutput>false</IncludeBuildOutput>
		<DevelopmentDependency>true</DevelopmentDependency>
	</PropertyGroup>

	<ItemGroup>
		<Content Include="main.editorconfig">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<PackagePath>build</PackagePath>
		</Content>
		<Content Include="MyStandards.CSharp.props">
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
			<PackagePath>build</PackagePath>
		</Content>
	</ItemGroup>

	<ItemGroup>
		<PackageReference Include="SonarAnalyzer.CSharp" Version="9.15.0.81779" PrivateAssets="None" />
	</ItemGroup>

</Project>

MyStandards.CSharp.props

<Project>
	<Target Name="CopyFiles" BeforeTargets="Build">
		<ItemGroup>
			<SourceFile Include="$(MSBuildThisFileDirectory)main.editorconfig"></SourceFile>
			<DestFile Include="$(ProjectDir)\.editorconfig"></DestFile>
		</ItemGroup>
		<Copy SourceFiles="@(SourceFile)" DestinationFiles="@(DestFile)"></Copy>
	</Target>
</Project>

What does it do?

When this project is built as a Nuget package, it contains the .props and main.editorconfig files in the build folder of the package. Additionally, it has a dependency on SonarAnalyzer.CSharp.

Make it a Development Dependency

Note that IncludeBuildOutput is set to false, and DevelopmentDependency is set to true. This tells Nuget to not include the empty MyStandards.CSharp.dll file that gets built, and to package this Nuget file as a development only dependency.

When referenced by another project, the reference will be added with the following defaults:

<PackageReference Include="MyStandards.CSharp" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

So, the consuming package won’t include a dependency for my standards, meaning no “chain” of references.

Set the Settings

The .props file does the yeoman’s work: it copies main.editorconfig into the target project’s folder as .editorconfig. Notice that this happens before every build, which prevents individual projects from overriding the .editorconfig file.

Referencing Additional Development Tools

As I mentioned, I’m a fan of the Sonar toolset for linting. As such, I like to add their built-in analyzers wherever I can. This includes adding a reference to SonarAnalyzer.CSharp to get some built-in analysis.

Since SonarAnalyzer.CSharp is a development dependency, when installed, it is not included in the package. It would be nice if my standards package would carry this over to projects which use it. To do this, I set PrivateAssets="None" in the PackageReference, and removed the default settings.

The Road Ahead

With my C# standards wrapped nicely in a Nuget package, my next steps will be to automate the build pipeline for the packages so that changes can be tracked.

For my next trick, I would like to dig in to eslint and prettier, to see if there is a way to create my own extensions for those tools to distribute our standards for JavaScript/TypeScript. However, that task may have to wait a bit, as I have some build pipelines to create.


Posted

in

,

by

Tags: