Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Create a `.json` file somewhere in your repository that will contain dependency
Schema of the rules file is as follows:
```json
{
"$schema": "https://raw.githubusercontent.com/olstakh/ReferenceProtector/main/src/Build/DependencyRules.schema.json",
"ProjectDependencies": [
{
"From": "",
Expand Down
1 change: 1 addition & 0 deletions samples/DependencyRules.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/olstakh/ReferenceProtector/main/src/Build/DependencyRules.schema.json",
"ProjectDependencies": [
{
"From": "*\\ClassA.csproj",
Expand Down
145 changes: 145 additions & 0 deletions src/Build/DependencyRules.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/olstakh/ReferenceProtector/main/src/Build/DependencyRules.schema.json",
"title": "ReferenceProtector Dependency Rules",
"description": "Defines rules for allowed and forbidden project and package references, enforced at build time by ReferenceProtector.",
"type": "object",
"additionalProperties": false,
"properties": {
"$schema": {
"type": "string",
"description": "The JSON schema reference for this file."
},
"ProjectDependencies": {
"type": "array",
"description": "Rules governing project-to-project references (both direct and transitive).",
"items": {
"$ref": "#/definitions/ProjectDependency"
}
},
"PackageDependencies": {
"type": "array",
"description": "Rules governing direct package references.",
"items": {
"$ref": "#/definitions/PackageDependency"
}
}
},
"definitions": {
"ProjectDependency": {
"type": "object",
"description": "A rule that matches project-to-project references.",
"required": [
"From",
"To",
"Description",
"Policy",
"LinkType"
],
"additionalProperties": false,
"properties": {
"From": {
"type": "string",
"description": "A regex pattern matching the source project path. Use '*' as a shorthand for '.*' (e.g., '*\\\\ClassA.csproj')."
},
"To": {
"type": "string",
"description": "A regex pattern matching the referenced project path. Use '*' as a shorthand for '.*' (e.g., '*\\\\ClassB.csproj')."
},
"Description": {
"type": "string",
"description": "A human-readable description of this rule, shown in diagnostic messages when the rule is violated."
},
"Policy": {
"$ref": "#/definitions/Policy"
},
"LinkType": {
"$ref": "#/definitions/LinkType"
},
"Exceptions": {
"type": "array",
"description": "Optional list of exceptions to this rule. For Forbidden rules, matching exceptions allow the reference. For Allowed rules, matching exceptions forbid the reference.",
"items": {
"$ref": "#/definitions/Exception"
}
}
}
},
"PackageDependency": {
"type": "object",
"description": "A rule that matches direct package references.",
"required": [
"From",
"To",
"Description",
"Policy"
],
"additionalProperties": false,
"properties": {
"From": {
"type": "string",
"description": "A regex pattern matching the source project path. Use '*' as a shorthand for '.*' (e.g., '*\\\\ClassA.csproj')."
},
"To": {
"type": "string",
"description": "A regex pattern matching the package name. Use '*' as a shorthand for '.*' (e.g., 'Forbidden.*')."
},
"Description": {
"type": "string",
"description": "A human-readable description of this rule, shown in diagnostic messages when the rule is violated."
},
"Policy": {
"$ref": "#/definitions/Policy"
},
"Exceptions": {
"type": "array",
"description": "Optional list of exceptions to this rule. For Forbidden rules, matching exceptions allow the reference. For Allowed rules, matching exceptions forbid the reference.",
"items": {
"$ref": "#/definitions/Exception"
}
}
}
},
"Policy": {
"type": "string",
"description": "Determines whether the matched reference is allowed or forbidden.",
"enum": [
"Allowed",
"Forbidden"
]
},
"LinkType": {
"type": "string",
"description": "Specifies which type of project reference the rule applies to.",
"enum": [
"Direct",
"Transitive",
"DirectOrTransitive"
]
},
"Exception": {
"type": "object",
"description": "An exception to a dependency rule.",
"required": [
"From",
"To",
"Justification"
],
"additionalProperties": false,
"properties": {
"From": {
"type": "string",
"description": "A regex pattern matching the source project path for this exception."
},
"To": {
"type": "string",
"description": "A regex pattern matching the referenced project or package for this exception."
},
"Justification": {
"type": "string",
"description": "A human-readable justification for why this exception exists."
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,22 @@ internal string SetupTestEnvironment()

File.WriteAllText(dirsProjPath, """
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.Build.Traversal">
<Project DefaultTargets="Build">
<ItemGroup>
<ProjectReference Include="**\dirs.proj" />
<ProjectReference Include="**\*.csproj" />
<ProjectFiles Include="**\*.csproj" />
</ItemGroup>
<Target Name="Restore">
<MSBuild Projects="@(ProjectFiles)" Targets="Restore" />
</Target>
<Target Name="Build">
<MSBuild Projects="@(ProjectFiles)" />
</Target>
<Target Name="Rebuild">
<MSBuild Projects="@(ProjectFiles)" Targets="Rebuild" />
</Target>
<Target Name="Clean">
<MSBuild Projects="@(ProjectFiles)" Targets="Clean" />
</Target>
</Project>
""");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,22 @@ internal string SetupTestEnvironment()

File.WriteAllText(dirsProjPath, """
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.Build.Traversal">
<Project DefaultTargets="Build">
<ItemGroup>
<ProjectReference Include="**\dirs.proj" />
<ProjectReference Include="**\*.csproj" />
<ProjectFiles Include="**\*.csproj" />
</ItemGroup>
<Target Name="Restore">
<MSBuild Projects="@(ProjectFiles)" Targets="Restore" />
</Target>
<Target Name="Build">
<MSBuild Projects="@(ProjectFiles)" />
</Target>
<Target Name="Rebuild">
<MSBuild Projects="@(ProjectFiles)" Targets="Rebuild" />
</Target>
<Target Name="Clean">
<MSBuild Projects="@(ProjectFiles)" Targets="Clean" />
</Target>
</Project>
""");
}
Expand Down Expand Up @@ -171,7 +182,6 @@ internal async Task<IReadOnlyList<Warning>> Build(string additionalArgs = "")
string errorsFilePath = Path.Combine(logDirBase, "build.errors.log");

await RunDotnetCommandAsync(TestDirectory, $"restore dirs.proj -f", TestContext.Current.CancellationToken);
await RunDotnetCommandAsync(TestDirectory, $"dotnet list dirs.proj package", TestContext.Current.CancellationToken);

string buildArgs =
$"build dirs.proj " +
Expand Down