diff --git a/app/MindWork AI Studio/MindWork AI Studio.csproj b/app/MindWork AI Studio/MindWork AI Studio.csproj
index 87449d584..f712831d9 100644
--- a/app/MindWork AI Studio/MindWork AI Studio.csproj
+++ b/app/MindWork AI Studio/MindWork AI Studio.csproj
@@ -122,13 +122,26 @@
-
+
+
+
+ $(NetCoreRoot)sdk/$(NETCoreSdkVersion)/.version
+
+
+
+
+
+
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 0 ])
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 1 ])
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 2 ])
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 3 ])
- $([System.String]::Copy( $(Metadata) ).Split( ';' )[ 4 ])
+ $(NETCoreSdkVersion) (commit $([System.String]::Copy( $(DotnetSdkVersionLines) ).Split( ';' )[ 0 ].Substring( 0, 10 )))
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 5 ])
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 6 ])
$([System.String]::Copy( $(Metadata) ).Split( ';' )[ 7 ])
@@ -157,11 +170,10 @@
<_Parameter3>$(MetaBuild)
<_Parameter3_TypeName>System.UInt32
<_Parameter4>$(MetaDotnetSdkVersion)
- <_Parameter5>$(MetaDotnetVersion)
- <_Parameter6>$(MetaRustVersion)
- <_Parameter7>$(MetaMudBlazorVersion)
- <_Parameter8>$(MetaTauriVersion)
- <_Parameter9>$(MetaAppCommitHash)
+ <_Parameter5>$(MetaRustVersion)
+ <_Parameter6>$(MetaMudBlazorVersion)
+ <_Parameter7>$(MetaTauriVersion)
+ <_Parameter8>$(MetaAppCommitHash)
<_Parameter1>$(MetaArchitecture)
diff --git a/app/MindWork AI Studio/Pages/Information.razor.cs b/app/MindWork AI Studio/Pages/Information.razor.cs
index cef2f5665..1b457a33c 100644
--- a/app/MindWork AI Studio/Pages/Information.razor.cs
+++ b/app/MindWork AI Studio/Pages/Information.razor.cs
@@ -89,7 +89,7 @@ public partial class Information : MSGComponentBase
private string VersionRust => $"{T("Used Rust compiler")}: v{META_DATA.RustVersion}";
- private string VersionDotnetRuntime => $"{T("Used .NET runtime")}: v{META_DATA.DotnetVersion}";
+ private string VersionDotnetRuntime => $"{T("Used .NET runtime")}: v{DotnetRuntimeVersion.Current}";
private string VersionDotnetSdk => $"{T("Used .NET SDK")}: v{META_DATA.DotnetSdkVersion}";
diff --git a/app/MindWork AI Studio/Tools/Metadata/DotnetRuntimeVersion.cs b/app/MindWork AI Studio/Tools/Metadata/DotnetRuntimeVersion.cs
new file mode 100644
index 000000000..e6e1a9636
--- /dev/null
+++ b/app/MindWork AI Studio/Tools/Metadata/DotnetRuntimeVersion.cs
@@ -0,0 +1,41 @@
+using System.Reflection;
+
+namespace AIStudio.Tools.Metadata;
+
+///
+/// The .NET runtime which runs the app.
+///
+///
+/// Asked while the app runs instead of taken from metadata.txt: a release brings its own runtime,
+/// and a development build runs on whichever runtime the developer installed.
+///
+public static class DotnetRuntimeVersion
+{
+ ///
+ /// How many characters of the commit we show, as metadata.txt does.
+ ///
+ private const int COMMIT_LENGTH = 10;
+
+ ///
+ /// Gets the version of the runtime together with the commit it was built from, such as
+ /// 10.0.14 (commit 0427b9abbe).
+ ///
+ public static string Current { get; } = Describe(Environment.Version, typeof(object).Assembly.GetCustomAttribute()?.InformationalVersion);
+
+ ///
+ /// Describes a version of the runtime together with the commit which the informational version
+ /// of its core library names after a plus sign, such as 10.0.14+0427b9abbead3f….
+ ///
+ /// The version of the runtime.
+ /// The informational version of the core library, if any.
+ /// The version with its commit, or the version alone when the commit is unknown.
+ internal static string Describe(Version version, string? informationalVersion)
+ {
+ if (string.IsNullOrWhiteSpace(informationalVersion))
+ return version.ToString();
+
+ var plusIndex = informationalVersion.IndexOf('+');
+ var commit = plusIndex < 0 ? string.Empty : informationalVersion[(plusIndex + 1)..];
+ return commit.Length < COMMIT_LENGTH ? version.ToString() : $"{version} (commit {commit[..COMMIT_LENGTH]})";
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/Metadata/MetaDataAttribute.cs b/app/MindWork AI Studio/Tools/Metadata/MetaDataAttribute.cs
index 366688297..0ad585825 100644
--- a/app/MindWork AI Studio/Tools/Metadata/MetaDataAttribute.cs
+++ b/app/MindWork AI Studio/Tools/Metadata/MetaDataAttribute.cs
@@ -7,7 +7,6 @@ public class MetaDataAttribute(
string buildTime,
uint buildNum,
string dotnetSdkVersion,
- string dotnetVersion,
string rustVersion,
string mudBlazorVersion,
string tauriVersion,
@@ -20,8 +19,9 @@ public class MetaDataAttribute(
public uint BuildNum => buildNum;
- public string DotnetVersion => dotnetVersion;
-
+ ///
+ /// Gets the .NET SDK which built the app. The .NET runtime which runs it is .
+ ///
public string DotnetSdkVersion => dotnetSdkVersion;
public string RustVersion => rustVersion;
diff --git a/app/Tests/Tools/Metadata/DotnetVersionTests.cs b/app/Tests/Tools/Metadata/DotnetVersionTests.cs
new file mode 100644
index 000000000..125a86377
--- /dev/null
+++ b/app/Tests/Tools/Metadata/DotnetVersionTests.cs
@@ -0,0 +1,41 @@
+using System.Reflection;
+
+using AIStudio.Tools.Metadata;
+
+namespace AIStudio.Tests.Tools.Metadata;
+
+///
+/// Checks the versions of .NET which the information page shows.
+///
+///
+/// The page names the runtime which runs the app and the SDK which built it, each with the commit it
+/// was built from. Both may differ from what metadata.txt says: a release brings its own runtime, a
+/// development build runs on whatever is installed, and a local build may use another SDK than the
+/// pipeline.
+///
+[TestFixture]
+public sealed class DotnetVersionTests
+{
+ private const string VERSION_WITH_COMMIT = @"^\d+\.\d+\.\d+\S* \(commit [0-9a-f]{10}\)$";
+
+ [TestCase("10.0.14+0427b9abbead3fa64c6655eb4290c537397131fa", ExpectedResult = "10.0.14 (commit 0427b9abbe)", TestName = "A runtime with its commit")]
+ [TestCase("10.0.0-rc.2.25502.107+89c8f6a112d37d2ea8b77821e56d170a1bccdc5a", ExpectedResult = "10.0.14 (commit 89c8f6a112)", TestName = "A runtime whose informational version has a label")]
+ [TestCase("10.0.14", ExpectedResult = "10.0.14", TestName = "A runtime without a commit")]
+ [TestCase("10.0.14+0427b9", ExpectedResult = "10.0.14", TestName = "A runtime with a cut-off commit")]
+ [TestCase("", ExpectedResult = "10.0.14", TestName = "A runtime without an informational version")]
+ [TestCase(null, ExpectedResult = "10.0.14", TestName = "A runtime whose core library names no version")]
+ public string TheRuntimeIsDescribedWithItsCommit(string? informationalVersion) => DotnetRuntimeVersion.Describe(new Version(10, 0, 14), informationalVersion);
+
+ [Test]
+ public void TheRuntimeIsTheOneRunningTheApp()
+ {
+ Assert.That(DotnetRuntimeVersion.Current, Does.StartWith($"{Environment.Version} ").And.Match(VERSION_WITH_COMMIT));
+ }
+
+ [Test]
+ public void TheSdkIsNamedWithItsCommit()
+ {
+ var metaData = typeof(DotnetRuntimeVersion).Assembly.GetCustomAttribute();
+ Assert.That(metaData?.DotnetSdkVersion, Does.Match(VERSION_WITH_COMMIT));
+ }
+}
\ No newline at end of file