How the dotnet build command, MSBuild and NuGet build .NET applications, which files they need, how to lint, scan and test, and a complete GitHub Actions pipeline.
Posted
DevOps pipeline loop motif with the title DotNet Build
When working with .NET applications, the dotnet build command is the essential tool that compiles your code and prepares it for execution. Whether you are developing a console application, a web API or a large enterprise solution, the dotnet CLI (a front end for MSBuild and NuGet) is the build system you will use locally and in CI.
dotnet build restores dependencies, compiles the source code and produces the binaries for your application.
It writes intermediate outputs to obj/ and the final assemblies to bin/<Configuration>/<TFM>/, which can then be run, tested or published.
In CI, each stage of the generic build pipeline maps to a dotnet CLI command, plugin or scanner. Press play, or step through it:
Source + project files: A commit triggers CI. Project and solution files, Directory.Packages.props and packages.lock.json define the build; global.json pins the SDK.
Restore: dotnet restore --locked-mode restores exactly what packages.lock.json says from NuGet; NuGet audit warns about vulnerable packages.
Lint and format: dotnet format --verify-no-changes fails if the code does not match .editorconfig and the analyzer style rules.
Compile: dotnet build -c Release --no-restore compiles with MSBuild; -warnaserror turns analyzer warnings, including security rules, into errors.
Test and coverage: dotnet test runs xUnit, NUnit or MSTest, and --collect:"XPlat Code Coverage" records coverage with Coverlet.
Security scans in parallel: Roslyn security analyzers check our code, dotnet list package --vulnerable --include-transitive checks dependencies, and Gitleaks scans for secrets.
Quality gate: Format, the build with analyzers as errors, the tests and the vulnerability report must pass before publishing.
Fail the build (fail): A failed check stops the pipeline at the gate, so nothing is packaged, signed or published.
Publish the output (pass): dotnet publish -c Release writes the deployable output; dotnet pack builds a .nupkg for libraries.
SBOM and signing (pass): dotnet CycloneDX writes the SBOM for the solution, and the output is signed with Sigstore cosign.
Publish (pass): Packages go to a NuGet feed (nuget.org, Azure Artifacts, GitHub Packages); deployable output is kept as a CI artifact.
Key functions of dotnet build
Compiles the project: converts the source code into assemblies (.dll, .exe).
Restores dependencies: runs dotnet restore implicitly unless you pass --no-restore. Since .NET 8, restore also runs NuGet audit and warns about packages with known vulnerabilities.
Runs analyzers: Roslyn code-quality and code-style analyzers run during compilation, so lint and part of SAST happen inside the build.
Handles configurations and frameworks: Debug (default) or Release, and one or more target frameworks (TFMs) such as net10.0.
Basic usage
1
2
3
4
dotnet build # Debug build of the project/solution in the current folder
dotnet build MySolution.sln # build every project in a solution
dotnet build --configuration Release # optimised build
dotnet build --framework net8.0 # one target framework of a multi-targeted project
Understanding a sample CI command
A commonly used command for building a .NET solution in a GitHub Actions workflow is:
${{ inputs.SOLUTION_FILE_PATH }}: a workflow input holding the path to the solution or project file.
-c Release: builds the Release configuration. The default is Debug.
--configfile 'src/NuGet.Config': uses a specific NuGet configuration so the correct package sources and authentication settings apply during the implicit restore.
<!-- Directory.Build.props: applies to every project in the repo --><Project><PropertyGroup><TargetFramework>net10.0</TargetFramework><Nullable>enable</Nullable><ImplicitUsings>enable</ImplicitUsings><TreatWarningsAsErrors>true</TreatWarningsAsErrors><AnalysisLevel>latest-recommended</AnalysisLevel><EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild><RestorePackagesWithLockFile>true</RestorePackagesWithLockFile><NuGetAudit>true</NuGetAudit><NuGetAuditMode>all</NuGetAuditMode><!-- include transitive packages --></PropertyGroup></Project>
With TreatWarningsAsErrors on, NuGet audit warnings (NU1901–NU1904) fail the build when a vulnerable package is restored, which turns SCA into a gate without extra tooling.
Frameworks
Framework
Start a project
ASP.NET Core Web API
dotnet new webapi -n Api
Blazor
dotnet new blazor -n Web
.NET MAUI
dotnet new maui -n App
Worker Service
dotnet new worker -n Worker
Entity Framework Core
dotnet add package Microsoft.EntityFrameworkCore.SqlServer then dotnet ef migrations add Init