Skip to content
v7 is in preview. Install it with dotnet tool install dotnet-affected --prerelease, and see what changed since v6.

MSBuild SDK

DotnetAffected.Tasks is an MSBuild project SDK that computes the affected projects during evaluation and hands them to Microsoft.Build.Traversal for execution. There is no CLI to install and no intermediate affected.proj — you run dotnet build against one file and only the affected projects are built.

Create a project file at the root of the Git repository. The name is yours to choose; ci.props is the convention.

<Project Sdk="DotnetAffected.Tasks/7.0.0;Microsoft.Build.Traversal/4.1.82">
</Project>

Then build, test or clean through it:

Terminal window
dotnet build ./ci.props
dotnet test ./ci.props

Instead of repeating versions in every file, pin them in global.json:

{
"msbuild-sdks": {
"DotnetAffected.Tasks": "7.0.0",
"Microsoft.Build.Traversal": "4.1.82"
}
}
<Project Sdk="DotnetAffected.Tasks;Microsoft.Build.Traversal">
</Project>

The SDK adds DotnetAffectedCheck as an initial target, so it runs before anything else. The target computes the changed and affected projects, clears any existing ProjectReference items and replaces them with the result. From there the Traversal SDK builds exactly those projects.

Two consequences follow:

  • Anything that feeds the check — the baseline, assumed changes, filter classes — must be in place before DotnetAffectedCheck runs: at evaluation time, or in a target with BeforeTargets="DotnetAffectedCheck".
  • Anything that post-processes the result belongs in a target with AfterTargets="DotnetAffectedCheck".
<Project Sdk="DotnetAffected.Tasks;Microsoft.Build.Traversal">
<Target Name="_PrintAffected" AfterTargets="DotnetAffectedCheck">
<Message Text="Found $(DotnetAffectedProjectCount) projects:" Importance="high" />
<Message Text=" >> %(ProjectReference.Identity)" Importance="high" />
</Target>
</Project>

By default the comparison is HEAD against the working tree, exactly like the CLI. Name the baseline, and choose what the working tree contributes:

<Project Sdk="DotnetAffected.Tasks;Microsoft.Build.Traversal">
<PropertyGroup>
<DotnetAffectedFromRef>origin/main</DotnetAffectedFromRef>
<!-- All (default), Staged, or None -->
<DotnetAffectedUncommitted>None</DotnetAffectedUncommitted>
</PropertyGroup>
</Project>

There is no property for the other end of the comparison: projects are discovered and evaluated from the working tree, so that is always where it ends. DotnetAffectedToRef still exists, warns when used, and will be removed in v8.

Or bypass Git entirely and assume changes, by name or by glob:

<Project Sdk="DotnetAffected.Tasks;Microsoft.Build.Traversal">
<ItemGroup>
<!-- Every csproj matching the pattern -->
<DotnetAffectedAssumeChanges Include="$(MSBuildThisFileDirectory)**/project-*.csproj" />
<!-- Or by project name — this throws if no project matches -->
<DotnetAffectedAssumeChanges Include="project3" />
</ItemGroup>
</Project>

See the SDK reference for every input and output, and Filtering by project properties for narrowing the result using each project’s own evaluated properties.