This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new a4d630779db IGNITE-25473 .NET: Fix compute executor type resolver 
compatibility (#5898)
a4d630779db is described below

commit a4d630779dbe635baebf88e49cf3842ebebd71aa
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Mon May 26 19:45:33 2025 +0300

    IGNITE-25473 .NET: Fix compute executor type resolver compatibility (#5898)
    
    * Fix `DeploymentUnitLoader` to substitute current `Apache.Ignite` assembly 
as a job dependency, regardless of requested version. **This allows the users 
to run .NET jobs compiled against different Ignite versions.**
    * Improve error handling in `JobLoadContext`.
    * Add compatibility test that compiles Ignite with version 11.22.33, 
compiles a Compute job against that, and runs it on current cluster.
---
 .../platform/dotnet/DotNetComputeExecutor.java     |  21 ++-
 .../Compute/Executor/JobGenerator.cs               |  30 +++-
 .../Compute/PlatformComputeCompatibilityTests.cs   | 161 +++++++++++++++++++++
 .../Compute/PlatformComputeTests.cs                |   6 +-
 .../Table/DataStreamerPlatformReceiverTests.cs     |  10 +-
 .../TestHelpers/AssemblyGenerator.cs               |   4 +-
 .../TestHelpers/ManagementApi.cs                   |  15 +-
 .../Compute/Executor/DeploymentUnitLoader.cs       |  11 ++
 .../Internal/Compute/Executor/JobLoadContext.cs    |  15 +-
 modules/platforms/dotnet/Directory.Build.props     |   2 +
 10 files changed, 248 insertions(+), 27 deletions(-)

diff --git 
a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/platform/dotnet/DotNetComputeExecutor.java
 
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/platform/dotnet/DotNetComputeExecutor.java
index d8903bb5381..a6761cfca66 100644
--- 
a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/platform/dotnet/DotNetComputeExecutor.java
+++ 
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/platform/dotnet/DotNetComputeExecutor.java
@@ -196,9 +196,7 @@ public class DotNetComputeExecutor {
         return fut;
     }
 
-    private static Throwable handleTransportError(Process proc, Throwable 
cause) {
-        Throwable cause0 = unwrapCause(cause);
-
+    private static Throwable handleTransportError(Process proc, @Nullable 
Throwable cause) {
         String output = getProcessOutputTail(proc, 10_000);
 
         if (proc.isAlive()) {
@@ -206,11 +204,14 @@ public class DotNetComputeExecutor {
             proc.destroyForcibly();
         }
 
-        if (cause0 instanceof TraceableException) {
-            TraceableException te = (TraceableException) cause;
+        if (cause != null) {
+            Throwable cause0 = unwrapCause(cause);
+            if (cause0 instanceof TraceableException) {
+                TraceableException te = (TraceableException) cause;
 
-            if (te.code() == Client.PROTOCOL_COMPATIBILITY_ERR) {
-                return cause;
+                if (te.code() == Client.PROTOCOL_COMPATIBILITY_ERR) {
+                    return cause;
+                }
             }
         }
 
@@ -246,7 +247,11 @@ public class DotNetComputeExecutor {
 
             // 2. Start the process. It connects to the server, passes the id, 
and the server knows it is the right one.
             String dotnetBinaryPath = DOTNET_BINARY_PATH;
-            LOG.debug("Starting .NET executor process [executorId={}, 
binaryPath={}]", executorId, dotnetBinaryPath);
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Starting .NET executor process [executorId={}, 
binaryPath={}]", executorId, dotnetBinaryPath);
+            }
+
             Process proc = startDotNetProcess(transport.serverAddress(), 
transport.sslEnabled(), executorId, dotnetBinaryPath);
 
             proc.onExit().thenRun(() -> {
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/Executor/JobGenerator.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/Executor/JobGenerator.cs
index ba0981171a1..55e4ad921b0 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/Executor/JobGenerator.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/Executor/JobGenerator.cs
@@ -72,14 +72,39 @@ public static class JobGenerator
             }
             """);
 
-    public static string EmitJob(TempDir tempDir, string asmName, 
[StringSyntax("C#")] string jobCode)
+    public static string EmitGetReferencedIgniteAssemblyJob(TempDir tempDir, 
string asmName, string? igniteDllPath = null) =>
+        EmitJob(
+            tempDir,
+            asmName,
+            """
+            public class GetReferencedIgniteAssemblyJob : IComputeJob<string, 
string>
+            {
+                public ValueTask<string> ExecuteAsync(IJobExecutionContext 
context, string arg, CancellationToken cancellationToken)
+                {
+                    foreach (var asm in 
Assembly.GetExecutingAssembly().GetReferencedAssemblies())
+                    {
+                        if (asm.FullName.Contains("Apache.Ignite", 
StringComparison.Ordinal))
+                        {
+                            return ValueTask.FromResult(asm.FullName);
+                        }
+                    }
+                    
+                    return ValueTask.FromResult(string.Empty);
+                }
+            }
+            """,
+            igniteDllPath);
+
+    public static string EmitJob(TempDir tempDir, string asmName, 
[StringSyntax("C#")] string jobCode, string? igniteDllPath = null)
     {
         var targetFile = Path.Combine(tempDir.Path, $"{asmName}.dll");
+        igniteDllPath ??= typeof(IgniteClient).Assembly.Location;
 
         AssemblyGenerator.EmitClassLib(
             targetFile,
             $$"""
               using System;
+              using System.Reflection;
               using System.Threading;
               using System.Threading.Tasks;
               using Apache.Ignite.Compute;
@@ -88,7 +113,8 @@ public static class JobGenerator
               {
                   {{jobCode}}
               }
-              """);
+              """,
+            igniteDllPath);
 
         return targetFile;
     }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeCompatibilityTests.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeCompatibilityTests.cs
new file mode 100644
index 00000000000..65b8e0161d9
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeCompatibilityTests.cs
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Tests.Compute;
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Executor;
+using Ignite.Compute;
+using NUnit.Framework;
+using TestHelpers;
+
+[Platform("Linux", Reason = "File locking on Windows prevents build.")]
+public class PlatformComputeCompatibilityTests : IgniteTestsBase
+{
+    private const string JobAssemblyName = 
nameof(PlatformComputeCompatibilityTests);
+
+    private const string FutureIgniteVersion = "11.22.33";
+
+    private DeploymentUnit _unit;
+
+    [OneTimeSetUp]
+    public async Task UnitDeploy()
+    {
+        using var igniteBuildDir = new TempDir();
+        using var jobBuildDir = new TempDir();
+
+        // Build Ignite with some unlikely future version.
+        BuildIgniteWithVersion(igniteBuildDir.Path, FutureIgniteVersion);
+
+        var jobDllPath = JobGenerator.EmitGetReferencedIgniteAssemblyJob(
+            jobBuildDir,
+            asmName: JobAssemblyName,
+            igniteDllPath: Path.Combine(igniteBuildDir.Path, 
"Apache.Ignite.dll"));
+
+        _unit = await 
ManagementApi.UnitDeploy($"unit-{JobAssemblyName}-{Guid.NewGuid()}", "1.0.0", 
[jobDllPath]);
+    }
+
+    [OneTimeTearDown]
+    public async Task UnitUndeploy() => await 
ManagementApi.UnitUndeploy(_unit);
+
+    [Test]
+    public async Task TestDotNetJobCompiledAgainstNewIgniteVersion()
+    {
+        var jobDesc = new JobDescriptor<string, string>(
+            JobClassName: $"TestNamespace.GetReferencedIgniteAssemblyJob, 
{JobAssemblyName}",
+            DeploymentUnits: [_unit],
+            Options: new JobExecutionOptions(ExecutorType: 
JobExecutorType.DotNetSidecar));
+
+        var nodes = await Client.GetClusterNodesAsync();
+        var target = JobTarget.Node(nodes.Single(x => x.Name == 
ComputeTests.PlatformTestNodeRunner));
+
+        var jobExec = await Client.Compute.SubmitAsync(target, jobDesc, 
"test1");
+        var result = await jobExec.GetResultAsync();
+
+        // Verify that the job references a future Ignite version but still 
works.
+        StringAssert.StartsWith($"Apache.Ignite, 
Version={FutureIgniteVersion}", result);
+    }
+
+    private static void BuildIgniteWithVersion(string targetPath, string 
version)
+    {
+        // Copy the Ignite solution and override build props to skip 
GitVersioning.
+        var slnDirCopy = 
Path.Combine(Directory.GetParent(TestUtils.SolutionDir)!.FullName, 
Guid.NewGuid().ToString());
+        using var disposeSln = new DisposeAction(() => 
Directory.Delete(slnDirCopy, true));
+
+        CopyFilesAndDirectories(sourcePath: TestUtils.SolutionDir, targetPath: 
slnDirCopy);
+
+        var buildPropsOverride = """
+                                 <Project>
+                                     <PropertyGroup>
+                                         <LangVersion>12</LangVersion>
+                                         
<EnableNETAnalyzers>true</EnableNETAnalyzers>
+                                         <Nullable>enable</Nullable>
+                                         <AnalysisMode>None</AnalysisMode>
+                                     </PropertyGroup>
+                                 </Project>
+                                 """;
+
+        File.WriteAllText(Path.Combine(slnDirCopy, "Directory.Build.props"), 
buildPropsOverride);
+
+        // Build the solution with the specified version.
+        var process = new Process
+        {
+            StartInfo = new ProcessStartInfo
+            {
+                FileName = "dotnet",
+                ArgumentList =
+                {
+                    "publish",
+                    "-c", "Release",
+                    "-o", targetPath,
+                    "/p:Version=" + version,
+                    "/p:VersionSuffix=" + version
+                },
+                CreateNoWindow = true,
+                UseShellExecute = false,
+                WorkingDirectory = Path.Combine(slnDirCopy, "Apache.Ignite"),
+                RedirectStandardOutput = true,
+                RedirectStandardError = true,
+                RedirectStandardInput = true
+            }
+        };
+
+        if (!process.Start())
+        {
+            throw new InvalidOperationException("Failed to start process: " + 
process.StartInfo.FileName);
+        }
+
+        var output = GetOutput();
+        Console.WriteLine(output);
+
+        if (!process.WaitForExit(TimeSpan.FromSeconds(120)))
+        {
+            throw new TimeoutException($"Process did not complete in time: 
{GetOutput()}");
+        }
+
+        if (process.ExitCode != 0)
+        {
+            throw new InvalidOperationException($"Process failed with exit 
code {process.ExitCode}: {output}");
+        }
+
+        string GetOutput() => process.StandardOutput.ReadToEnd() + 
process.StandardError.ReadToEnd();
+    }
+
+    private static void CopyFilesAndDirectories(string sourcePath, string 
targetPath)
+    {
+        foreach (var dir in Directory.GetDirectories(sourcePath, "*", 
SearchOption.AllDirectories))
+        {
+            Directory.CreateDirectory(GetTargetPath(dir));
+        }
+
+        foreach (var file in Directory.GetFiles(sourcePath, "*", 
SearchOption.AllDirectories))
+        {
+            File.Copy(file, GetTargetPath(file));
+        }
+
+        string GetTargetPath(string path)
+        {
+            var relative = Path.GetRelativePath(sourcePath, path);
+
+            return Path.Combine(targetPath, relative);
+        }
+    }
+}
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeTests.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeTests.cs
index ecc12c6b73f..5626b36e0e3 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeTests.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Compute/PlatformComputeTests.cs
@@ -119,7 +119,8 @@ public class PlatformComputeTests : IgniteTestsBase
         var jobExec = await Client.Compute.SubmitAsync(target, desc, "arg");
         var ex = Assert.ThrowsAsync<IgniteException>(async () => await 
jobExec.GetResultAsync());
 
-        Assert.AreEqual(".NET job failed: Type 'MyNamespace.MyJob' not found 
in the specified deployment units.", ex.Message);
+        StringAssert.StartsWith(".NET job failed: Failed to load type 
'MyNamespace.MyJob'", ex.Message);
+        StringAssert.Contains("Could not resolve type 'MyNamespace.MyJob' in 
assembly 'Apache.Ignite", ex.Message);
         Assert.AreEqual("IGN-COMPUTE-9", ex.CodeAsString);
     }
 
@@ -131,7 +132,8 @@ public class PlatformComputeTests : IgniteTestsBase
         var jobExec = await Client.Compute.SubmitAsync(target, 
DotNetJobs.Echo, "Hello world!");
 
         var ex = Assert.ThrowsAsync<IgniteException>(async () => await 
jobExec.GetResultAsync());
-        StringAssert.StartsWith(".NET job failed: Could not load file or 
assembly 'Apache.Ignite.Tests", ex.Message);
+        StringAssert.StartsWith(".NET job failed: Failed to load type 
'Apache.Ignite.Tests.Compute.DotNetJobs+EchoJob", ex.Message);
+        StringAssert.Contains("Could not load file or assembly 
'Apache.Ignite.Tests", ex.Message);
         Assert.AreEqual("IGN-COMPUTE-9", ex.CodeAsString);
     }
 
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerPlatformReceiverTests.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerPlatformReceiverTests.cs
index bfcaa7a090d..2c04fd0435e 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerPlatformReceiverTests.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/DataStreamerPlatformReceiverTests.cs
@@ -110,7 +110,8 @@ public class DataStreamerPlatformReceiverTests : 
IgniteTestsBase
             receiverArg: "arg");
 
         var ex = Assert.ThrowsAsync<DataStreamerException>(async () => await 
resStream.SingleAsync());
-        Assert.AreEqual(".NET job failed: Type 'BadClass' not found in the 
specified deployment units.", ex.Message);
+        StringAssert.StartsWith(".NET job failed: Failed to load type 
'BadClass'", ex.Message);
+        StringAssert.Contains("Could not resolve type 'BadClass' in assembly 
'Apache.Ignite", ex.Message);
         Assert.AreEqual(1, ex.FailedItems.Count);
     }
 
@@ -134,10 +135,9 @@ public class DataStreamerPlatformReceiverTests : 
IgniteTestsBase
 
         var ex = Assert.ThrowsAsync<DataStreamerException>(async () => await 
task);
 
-        Assert.AreEqual(
-            ".NET job failed: Could not load file or assembly 'BadAssembly, 
Culture=neutral, PublicKeyToken=null'. " +
-            "The system cannot find the file specified.",
-            ex.Message.Trim());
+        StringAssert.Contains(".NET job failed: Failed to load type 'MyClass, 
BadAssembly'", ex.Message);
+        StringAssert.Contains("Could not load file or assembly 'BadAssembly", 
ex.Message);
+        StringAssert.Contains("The system cannot find the file specified.", 
ex.Message);
 
         Assert.AreEqual(1, ex.FailedItems.Count);
     }
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/AssemblyGenerator.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/AssemblyGenerator.cs
index a6c6237556e..6d4b9b177d7 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/AssemblyGenerator.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/AssemblyGenerator.cs
@@ -26,7 +26,7 @@ using Microsoft.CodeAnalysis.CSharp;
 
 internal static class AssemblyGenerator
 {
-    internal static void EmitClassLib(string targetFile, [StringSyntax("C#")] 
string code)
+    internal static void EmitClassLib(string targetFile, [StringSyntax("C#")] 
string code, string referencePath)
     {
         var assemblyName = Path.GetFileNameWithoutExtension(targetFile);
         var syntaxTree = CSharpSyntaxTree.ParseText(code);
@@ -38,7 +38,7 @@ internal static class AssemblyGenerator
             MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
             MetadataReference.CreateFromFile(Path.Combine(refDir, 
"System.dll")),
             MetadataReference.CreateFromFile(Path.Combine(refDir, 
"System.Runtime.dll")),
-            
MetadataReference.CreateFromFile(typeof(IgniteClient).Assembly.Location),
+            MetadataReference.CreateFromFile(referencePath)
         };
 
         var compilation = CSharpCompilation.Create(
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/ManagementApi.cs 
b/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/ManagementApi.cs
index 905bac05e0c..4c4d3a51782 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/ManagementApi.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/TestHelpers/ManagementApi.cs
@@ -41,7 +41,7 @@ public static class ManagementApi
         PropertyNameCaseInsensitive = true
     };
 
-    public static async Task UnitDeploy(string unitId, string unitVersion, 
IList<string> unitContent)
+    public static async Task<DeploymentUnit> UnitDeploy(string unitId, string 
unitVersion, IList<string> unitContent)
     {
         // See DeployUnitClient.java
         var url = GetUnitUrl(unitId, unitVersion);
@@ -82,14 +82,21 @@ public static class ManagementApi
             timeoutMs: 5000,
             () => $"Failed to deploy unit {unitId} version {unitVersion}: 
{GetUnitStatusString()}");
 
+        return new DeploymentUnit(unitId, unitVersion);
+
         string? GetUnitStatusString() =>
             GetUnitStatus(unitId).GetAwaiter().GetResult()?
                 .SelectMany(x => x.VersionToStatus)
                 .StringJoin();
     }
 
-    public static async Task UnitUndeploy(DeploymentUnit unit)
+    public static async Task UnitUndeploy(DeploymentUnit? unit)
     {
+        if (unit == null)
+        {
+            return;
+        }
+
         using var client = new HttpClient();
         await client.DeleteAsync(GetUnitUrl(unit.Name, unit.Version).Uri);
     }
@@ -101,12 +108,10 @@ public static class ManagementApi
         var unitId0 = unitId ?? TestContext.CurrentContext.Test.FullName;
         var unitVersion0 = unitVersion ?? 
DateTime.Now.TimeOfDay.ToString(@"m\.s\.f");
 
-        await UnitDeploy(
+        return await UnitDeploy(
             unitId: unitId0,
             unitVersion: unitVersion0,
             unitContent: [testsDll]);
-
-        return new DeploymentUnit(unitId0, unitVersion0);
     }
 
     private static async Task<DeploymentUnitStatus[]?> GetUnitStatus(string 
unitId)
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/DeploymentUnitLoader.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/DeploymentUnitLoader.cs
index 25a6016055f..a2fc69d1bd6 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/DeploymentUnitLoader.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/DeploymentUnitLoader.cs
@@ -26,6 +26,10 @@ using System.Runtime.Loader;
 /// </summary>
 internal static class DeploymentUnitLoader
 {
+    private static readonly Assembly IgniteAssembly = 
typeof(DeploymentUnitLoader).Assembly;
+
+    private static readonly string IgniteAssemblyName = 
IgniteAssembly.GetName().Name!;
+
     /// <summary>
     /// Creates a new job load context for the specified deployment unit paths.
     /// </summary>
@@ -42,6 +46,13 @@ internal static class DeploymentUnitLoader
 
     private static Assembly? ResolveAssembly(IReadOnlyList<string> paths, 
AssemblyName name, AssemblyLoadContext ctx)
     {
+        if (name.Name == IgniteAssemblyName)
+        {
+            // Compute job might be built against a different version of 
Ignite, so we end up here.
+            // Redirect to the current Ignite assembly.
+            return IgniteAssembly;
+        }
+
         foreach (var path in paths)
         {
             var dllName = $"{name.Name}.dll";
diff --git 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/JobLoadContext.cs
 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/JobLoadContext.cs
index 786ba9dac00..13ed4c60b6b 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/JobLoadContext.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite/Internal/Compute/Executor/JobLoadContext.cs
@@ -74,9 +74,18 @@ internal readonly record struct 
JobLoadContext(AssemblyLoadContext AssemblyLoadC
         }
     }
 
-    private static Type LoadType(string typeName, AssemblyLoadContext ctx) =>
-        Type.GetType(typeName, ctx.LoadFromAssemblyName, null)
-        ?? throw new InvalidOperationException($"Type '{typeName}' not found 
in the specified deployment units.");
+    private static Type LoadType(string typeName, AssemblyLoadContext ctx)
+    {
+        try
+        {
+            return Type.GetType(typeName, ctx.LoadFromAssemblyName, null, 
throwOnError: true)
+                   ?? throw new InvalidOperationException($"Type '{typeName}' 
not found in the specified deployment units.");
+        }
+        catch (Exception e)
+        {
+            throw new InvalidOperationException($"Failed to load type 
'{typeName}' from the specified deployment units: {e.Message}", e);
+        }
+    }
 
     // Simple lookup by name. Will throw in a case of ambiguity.
     private static Type FindInterface(Type type, Type interfaceType) =>
diff --git a/modules/platforms/dotnet/Directory.Build.props 
b/modules/platforms/dotnet/Directory.Build.props
index 3c01c4eb53d..26edc212f56 100644
--- a/modules/platforms/dotnet/Directory.Build.props
+++ b/modules/platforms/dotnet/Directory.Build.props
@@ -41,6 +41,8 @@
 
         <EnableNETAnalyzers>true</EnableNETAnalyzers>
         <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
+
+        
<GitVersionBaseDirectory>$(MSBuildThisFileDirectory)</GitVersionBaseDirectory>
     </PropertyGroup>
 
     <ItemGroup>

Reply via email to