Protect from unwanted dependencies in your repository. As repo gets bigger - there's often a need to secure from bad dependencies between projects, or to unwanted packages.
The following warnings are generated by this package:
| Id | Description |
|---|---|
| RP0001 | Provide DependencyRulesFile property to specify valid dependency rules file. |
| RP0002 | Make sure the dependency rules file is in the correct json format |
| RP0003 | No dependency rules matched the current project |
| RP0004 | Project reference 'x' ==> 'y' violates dependency rule or one of its exceptions |
| RP0005 | Package reference 'x' ==> 'y' violates dependency rule or one of its exceptions |
Add a package reference to the ReferenceProtector package in your projects, or as a common package reference in the repo's Directory.Packages.props.
If you're using Central Package Management, you can use it as a GlobalPackageReference in your Directory.Packages.props to apply it to the entire repo.
<ItemGroup>
<GlobalPackageReference Include="ReferenceProtector" Version="{LatestVersion}" />
</ItemGroup>Create a .json file somewhere in your repository that will contain dependency rules, and provide full path to this file in MSBuild property for specific project, or for all projects. For example, placing DependencyRules.json file in the root of the repo, you can update Directpry.Build.props (assuming it's also in the root) with <DependencyRulesFile>$(MSBuildThisFileDirectory)DependencyRules.json</DependencyRulesFile>. You can also provide it via command line like /p:DependencyRulesFile=...
Schema of the rules file is as follows:
{
"ProjectDependencies": [
{
"From": "",
"To": "",
"Policy": "",
"LinkType": "",
"Description": "",
"Exceptions": [
{
"From": "",
"To": "",
"Justification": ""
},
// ...
]
},
// ...
]
"PackageDependencies": [
{
"From": "",
"To": "",
"Policy": "",
// "LinkType": "", Only direct package references are analyzed, so LinkType is not needed in this section
"Description": "",
"Exceptions": [
{
"From": "",
"To": "",
"Justification": ""
},
// ...
]
},
// ...
]
}Top ProjectDependencies object will contain a list of rules to validate against. Each rule has the following schema:
From/To- Full path regex for source and target projects to be matched.Policy- enum with valuesAllowed/Forbidden. Describes whether link betweenFromandToprojects should be allowed or forbiddenLinkType- enum with valuesDirect/Transitive/DirectOrTransitive. Specifies whether link betweenFromandToprojects is expected to be direct, transitive or bothDescription- human readable explanation for this policy rule. Will be displayed in a build warning if policy is violated (ruleRP0004).Exceptions(Optional) - list of exceptions from this policy (can be due to tech debt, or for any other reason).
Top PackageDependences object will have the same format as ProjectDependencies with LinkType omitted, since only direct package references will be considered. Also, Description section will be part of RP0005 warning (as opposed to RP0004)
Each reference between the projects / packages during the build is evaluated against provided list of policies. First each pair of dependencies is evaluated against From and To patterns, based on their full path. For project dependencies - if the match is successful - their link type is evaluated: if current pair has a direct dependency on each other and LinkType value is Direct or DirectOrTransient - the match is successful, otherwise (the dependency is transient) - LinkType should be Transient or DirectOrTransient for the match to be successful. Package dependencies are only viewed as direct references. Then the exceptions are evaluated using the same pattern matching logic with From and To fields.
The decision logic is as follows
- If current
Policyvalue isForbidden- the rule is considered violated if no exceptions were matched - If current
Policyvalue isAllowed- the rule is considered violated if there are any matched exceptions
Violations of the rule will produce RP0004 (for projects) and RP0005 (for packages) warning during build.
Note: in regex matches - * is substituted with .* for proper regex, and $ is added at the end.
Regex.Escape(pattern).Replace("\\*", ".*") + "$";
Below are few examples of potential rules
{
"description": "Infrastructure referencing application logic is fordbidden",
"Policy": "forbidden",
"LinkType": "DirectOrTransient",
"from": "*\\Infrastructure\\*",
"to": "*",
"exceptions": [
{
"from": "*\\Infrastructure\\*",
"to": "*\\Infrastructure\\*",
"justification": "Infrastructure projects can reference each other"
},
{
"from": "*Tests.csproj",
"to": "*",
"justification": "tech debt <work item link>"
},
{
"from": "*",
"to": "LegacyDependency.csproj",
"justification": "tech debt <work item link>"
}
]
},
{
"description": "Referencing Common is ok",
"Policy": "allowed",
"LinkType": "DirectOrTransient",
"from": "*",
"to": "*\\Common.csproj"
},
{
"PackageDependencies": [
{
"description": "Use System.Text.Json, instead of Newtonsoft.Json",
"Policy": "forbidden",
"from": "*",
"to": "Newtonsoft.json",
"exceptions": [
// tech debt
]
}
]
}
First - MSBuild task with gather all direct / indirect project references and dump them into a file (typically in obj/Debug/ folder), named _ReferenceProtector_DeclaredReferences.tsv (inspired by ReferenceTrimmer implementation). During the second stage - Roslyn analyzer will read this file and match it against the dependency rules, defined in a file from <DependencyRulesFile> property. Corresponding diagnostics will be produced if violations are found.
Easiest way is to set EnableReferenceProtector variable to false (either in command line or in a project file, like <EnableReferenceProtector>false</EnableReferenceProtector>)