From 289943e86b5be0abf9a39631a91dd1c7643d2707 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 17 Feb 2026 13:12:00 -0800 Subject: [PATCH 1/4] Add json schema --- samples/DependencyRules.json | 1 + src/Build/DependencyRules.schema.json | 145 ++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/Build/DependencyRules.schema.json diff --git a/samples/DependencyRules.json b/samples/DependencyRules.json index 91d30f3..75b9e4c 100644 --- a/samples/DependencyRules.json +++ b/samples/DependencyRules.json @@ -1,4 +1,5 @@ { + "$schema": "https://raw.githubusercontent.com/olstakh/ReferenceProtector/main/src/Build/DependencyRules.schema.json", "ProjectDependencies": [ { "From": "*\\ClassA.csproj", diff --git a/src/Build/DependencyRules.schema.json b/src/Build/DependencyRules.schema.json new file mode 100644 index 0000000..5907ce5 --- /dev/null +++ b/src/Build/DependencyRules.schema.json @@ -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." + } + } + } + } +} From 7bddaf5f3a401ebd01c66b94cf3ea4f3173ed9f5 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 17 Feb 2026 13:13:28 -0800 Subject: [PATCH 2/4] Updated readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ffebd02..6ebb3d9 100644 --- a/README.md +++ b/README.md @@ -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": "", From 71369a38a28934fdf13d1a7af4810121bb1ad729 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 17 Feb 2026 13:26:47 -0800 Subject: [PATCH 3/4] Fix flakiness --- .../ReferenceProtector.Tasks.TestBase.cs | 17 ++++++++++++++--- ...enceProtector.IntegrationTests.TestBase.cs | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Tasks/ReferenceProtector.Tasks.IntegrationTests/ReferenceProtector.Tasks.TestBase.cs b/src/Tasks/ReferenceProtector.Tasks.IntegrationTests/ReferenceProtector.Tasks.TestBase.cs index 82de26c..bd02b11 100644 --- a/src/Tasks/ReferenceProtector.Tasks.IntegrationTests/ReferenceProtector.Tasks.TestBase.cs +++ b/src/Tasks/ReferenceProtector.Tasks.IntegrationTests/ReferenceProtector.Tasks.TestBase.cs @@ -51,11 +51,22 @@ internal string SetupTestEnvironment() File.WriteAllText(dirsProjPath, """ - + - - + + + + + + + + + + + + + """); } diff --git a/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs b/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs index 243568a..6e1ed36 100644 --- a/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs +++ b/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs @@ -59,11 +59,22 @@ internal string SetupTestEnvironment() File.WriteAllText(dirsProjPath, """ - + - - + + + + + + + + + + + + + """); } @@ -171,7 +182,7 @@ internal async Task> 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); + await RunDotnetCommandAsync(TestDirectory, $"list dirs.proj package", TestContext.Current.CancellationToken); string buildArgs = $"build dirs.proj " + From 34b34e04510625e8adaff79532bcdbd2b4f7b01f Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 17 Feb 2026 13:32:18 -0800 Subject: [PATCH 4/4] I guess dotnet list doesn't work --- .../ReferenceProtector.IntegrationTests.TestBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs b/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs index 6e1ed36..818f6b9 100644 --- a/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs +++ b/tests/ReferenceProtector.IntegrationTests/ReferenceProtector.IntegrationTests.TestBase.cs @@ -182,7 +182,6 @@ internal async Task> Build(string additionalArgs = "") string errorsFilePath = Path.Combine(logDirBase, "build.errors.log"); await RunDotnetCommandAsync(TestDirectory, $"restore dirs.proj -f", TestContext.Current.CancellationToken); - await RunDotnetCommandAsync(TestDirectory, $"list dirs.proj package", TestContext.Current.CancellationToken); string buildArgs = $"build dirs.proj " +