Skip to content

Commit 3d2b534

Browse files
Geeven SinghCopilot
andcommitted
Show current version in Settings > Updates section
Adds a read-only 'Current version' row at the top of the Settings dialog's Updates section so users can answer 'what version am I on?' next to the auto-update controls without leaving the dialog. Display value comes from AssemblyInformationalVersionAttribute (which release.yml stamps with the release tag, e.g. 1.6.0 or 1.6.0-rc1), with SourceLink's +gitHash suffix stripped so the displayed value matches a tag the user could navigate to. New utility AppVersionInfo.GetDisplayVersion() with a testable inner overload that takes the raw attribute strings. Falls back to AssemblyVersion (1.6.0.0 shape) when no informational version is stamped, then to 'unknown' as a defensive default. Tests cover all three fallback paths and the +hash stripping behavior. Tests: 1440 passing (+12 from AppVersionInfo theory inline data sets), 1 skipped. 0 warnings, 0 errors in dotnet build -c Release. CHANGELOG [Unreleased] documents the new row. AI-Local-Session: 4519f6b6-393a-4476-8efa-410e5396c3a9 AI-Cloud-Session: 72f9e474-60ab-42c2-b2a0-28fee827cbbb Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4c5adb2 commit 3d2b534

5 files changed

Lines changed: 134 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ body. Keep section headings exact and write notes in Markdown.
1212

1313
## [Unreleased]
1414

15+
### Added
16+
17+
- **Settings → Updates section now shows the running version.** A
18+
read-only "Current version" row at the top of the section
19+
displays the build's version string (e.g. `1.6.0` or `1.6.0-rc1`
20+
for prereleases), so users can answer "what version am I on?"
21+
alongside the auto-update settings without leaving the dialog.
22+
1523
## [1.6.0] - 2026-05-31
1624

1725
### Added
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using DiffViewer.Utility;
2+
using FluentAssertions;
3+
using Xunit;
4+
5+
namespace DiffViewer.Tests.Utility;
6+
7+
public sealed class AppVersionInfoTests
8+
{
9+
[Theory]
10+
[InlineData("1.6.0", null, "1.6.0")]
11+
[InlineData("1.6.0", "1.6.0.0", "1.6.0")]
12+
[InlineData("1.6.0-rc1", null, "1.6.0-rc1")]
13+
[InlineData("1.6.0-rc1+abc1234", null, "1.6.0-rc1")]
14+
[InlineData("1.6.0+abc1234", null, "1.6.0")]
15+
public void GetDisplayVersionFromValues_PrefersInformational_StripsHashSuffix(
16+
string informational, string? assemblyVersion, string expected)
17+
{
18+
AppVersionInfo.GetDisplayVersionFromValues(informational, assemblyVersion)
19+
.Should().Be(expected);
20+
}
21+
22+
[Theory]
23+
[InlineData(null, "1.6.0.0", "1.6.0.0")]
24+
[InlineData("", "1.6.0.0", "1.6.0.0")]
25+
[InlineData(" ", "1.6.0.0", "1.6.0.0")]
26+
public void GetDisplayVersionFromValues_FallsBackToAssemblyVersion_WhenInformationalMissing(
27+
string? informational, string assemblyVersion, string expected)
28+
{
29+
AppVersionInfo.GetDisplayVersionFromValues(informational, assemblyVersion)
30+
.Should().Be(expected);
31+
}
32+
33+
[Theory]
34+
[InlineData(null, null)]
35+
[InlineData("", null)]
36+
[InlineData(null, "")]
37+
[InlineData("", "")]
38+
public void GetDisplayVersionFromValues_FallsBackToUnknown_WhenBothMissing(
39+
string? informational, string? assemblyVersion)
40+
{
41+
AppVersionInfo.GetDisplayVersionFromValues(informational, assemblyVersion)
42+
.Should().Be("unknown");
43+
}
44+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Reflection;
2+
3+
namespace DiffViewer.Utility;
4+
5+
/// <summary>
6+
/// Reads the running DiffViewer build's version for display in the
7+
/// UI (Settings → Updates section). Two-method shape so the
8+
/// reflection-based read is wrapped around a pure string-handling
9+
/// inner method that's testable in isolation.
10+
///
11+
/// <para>Prefers <see cref="AssemblyInformationalVersionAttribute"/>
12+
/// because that's what <c>release.yml</c> stamps with the release
13+
/// tag (e.g. <c>1.6.0</c> or <c>1.6.0-rc1</c>), matching exactly
14+
/// what users see on the GitHub Releases page. SourceLink may
15+
/// append a <c>+gitHash</c> suffix to the informational version on
16+
/// dev builds; we strip it for display so the value matches a tag
17+
/// the user could navigate to.</para>
18+
/// </summary>
19+
public static class AppVersionInfo
20+
{
21+
/// <summary>
22+
/// User-facing version string for the running DiffViewer build,
23+
/// e.g. <c>"1.6.0"</c> or <c>"1.6.0-rc1"</c>. Falls back to the
24+
/// raw <see cref="System.Reflection.AssemblyName.Version"/> shape
25+
/// (<c>"1.6.0.0"</c>) when no informational version is stamped,
26+
/// and to <c>"unknown"</c> when neither is available (defensive —
27+
/// should not happen in normal builds).
28+
/// </summary>
29+
public static string GetDisplayVersion()
30+
{
31+
var asm = Assembly.GetEntryAssembly();
32+
var info = asm?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
33+
var fallback = asm?.GetName().Version?.ToString();
34+
return GetDisplayVersionFromValues(info, fallback);
35+
}
36+
37+
/// <summary>
38+
/// Testable inner overload. Takes the raw attribute values and
39+
/// produces the display string per the precedence rules
40+
/// described on the class. Public-internal (via
41+
/// <c>InternalsVisibleTo</c>) so the test project can drive it
42+
/// without round-tripping through reflection on a fake assembly.
43+
/// </summary>
44+
internal static string GetDisplayVersionFromValues(string? informationalVersion, string? assemblyVersion)
45+
{
46+
if (!string.IsNullOrWhiteSpace(informationalVersion))
47+
{
48+
// Strip SourceLink's "+gitHash" suffix so the displayed
49+
// value matches a tag the user could navigate to.
50+
var plus = informationalVersion!.IndexOf('+');
51+
return plus > 0 ? informationalVersion[..plus] : informationalVersion;
52+
}
53+
if (!string.IsNullOrWhiteSpace(assemblyVersion))
54+
{
55+
return assemblyVersion!;
56+
}
57+
return "unknown";
58+
}
59+
}

DiffViewer/ViewModels/SettingsViewModel.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CommunityToolkit.Mvvm.Input;
1111
using DiffViewer.Models;
1212
using DiffViewer.Services;
13+
using DiffViewer.Utility;
1314

1415
namespace DiffViewer.ViewModels;
1516

@@ -131,6 +132,15 @@ partial void OnDefaultCloneDestinationChanged(string value) =>
131132
/// <summary>Option list for the UpdateCheckCadence dropdown in the dialog.</summary>
132133
public IReadOnlyList<UpdateCheckCadence> UpdateCheckCadenceOptions { get; } = Enum.GetValues<UpdateCheckCadence>();
133134

135+
/// <summary>
136+
/// Display version of the running DiffViewer build (e.g.
137+
/// <c>"1.6.0"</c>). Read once at construction; does not change
138+
/// at runtime. Shown in the dialog's Updates section as
139+
/// "what version am I on right now" context for the auto-update
140+
/// settings.
141+
/// </summary>
142+
public string CurrentVersion { get; } = AppVersionInfo.GetDisplayVersion();
143+
134144
// Status line
135145
[ObservableProperty] private string _statusMessage = string.Empty;
136146

DiffViewer/Views/SettingsDialog.xaml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,20 @@
288288
<ColumnDefinition Width="*"/>
289289
</Grid.ColumnDefinitions>
290290
<Grid.RowDefinitions>
291-
<RowDefinition/><RowDefinition/><RowDefinition/>
291+
<RowDefinition/><RowDefinition/><RowDefinition/><RowDefinition/>
292292
</Grid.RowDefinitions>
293293

294294
<TextBlock Grid.Row="0" Grid.Column="0" Style="{StaticResource FieldLabel}"
295+
Text="Current version"/>
296+
<TextBlock Grid.Row="0" Grid.Column="1" Margin="0,4"
297+
VerticalAlignment="Center"
298+
FontFamily="Consolas"
299+
Foreground="#333"
300+
Text="{Binding CurrentVersion, Mode=OneTime}"/>
301+
302+
<TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource FieldLabel}"
295303
Text="Auto-update behavior"/>
296-
<ComboBox Grid.Row="0" Grid.Column="1" Margin="0,4"
304+
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,4"
297305
ItemsSource="{Binding AutoUpdateOptions}"
298306
SelectedItem="{Binding AutoUpdate}">
299307
<ComboBox.ItemTemplate>
@@ -303,9 +311,9 @@
303311
</ComboBox.ItemTemplate>
304312
</ComboBox>
305313

306-
<TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource FieldLabel}"
314+
<TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource FieldLabel}"
307315
Text="Check frequency"/>
308-
<ComboBox Grid.Row="1" Grid.Column="1" Margin="0,4"
316+
<ComboBox Grid.Row="2" Grid.Column="1" Margin="0,4"
309317
ItemsSource="{Binding UpdateCheckCadenceOptions}"
310318
SelectedItem="{Binding UpdateCheckCadence}">
311319
<ComboBox.ItemTemplate>
@@ -315,7 +323,7 @@
315323
</ComboBox.ItemTemplate>
316324
</ComboBox>
317325

318-
<CheckBox Grid.Row="2" Grid.Column="1" Margin="0,6,0,0"
326+
<CheckBox Grid.Row="3" Grid.Column="1" Margin="0,6,0,0"
319327
Content="Include pre-release versions"
320328
IsChecked="{Binding IncludePreReleases}"/>
321329
</Grid>

0 commit comments

Comments
 (0)