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 betweenFrom
andTo
projects should be allowed or forbiddenLinkType
- enum with valuesDirect
/Transitive
/DirectOrTransitive
. Specifies whether link betweenFrom
andTo
projects 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
Policy
value isForbidden
- the rule is considered violated if no exceptions were matched - If current
Policy
value 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>
)