diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
deleted file mode 100644
index 427db0e..0000000
--- a/.github/workflows/master.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-name: release
-
-on:
- workflow_dispatch:
-
-jobs:
- build:
- runs-on: windows-2025
- steps:
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
- with:
- dotnet-version: "10.x"
- - uses: nuget/setup-nuget@fd55a6f3b34392fa83fde1454582407d8c714123 # 4.0.0
- - uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # 3.0.0
- with:
- msbuild-architecture: x64
-
- - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # 4.0.1
- id: changes
- with:
- filters: |
- buildprops:
- - 'Directory.Build.props'
-
- - name: Update version
- if: steps.changes.outputs.buildprops == 'false'
- run: |
- (Get-Content Directory.Build.props) | % {
- $m = [regex]::match($_, '(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?');
- if(!$m.Success -or $m.Groups[4].Success -or $m.Groups[5].Success) { $_; }
- else { $_ -replace $m.Value, ("{0}.{1}.{2}-pre${{ github.run_number }}" -f $m.Groups[1].Value,$m.Groups[2].Value,([convert]::ToInt32($m.Groups[3].Value)+1)); }
- } | Set-Content Directory.Build.props
-
- - name: Restore application packages
- run: dotnet restore LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj
-
- - name: Build application
- run: dotnet build LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj -c Release --no-restore -p:Platform=x64
-
- - name: Publish WinUI application
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
- with:
- name: LibreHardwareMonitor.Windows.WinUI
- path: |
- bin/Release/net10.0-windows10.0.19041.0
-
- - name: Restore library packages
- run: dotnet restore LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj
-
- - name: Build x64 libraries
- run: dotnet build LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release --no-restore -p:Platform=x64
-
- - name: Run library tests
- run: dotnet test LibreHardwareMonitorLib.Tests\LibreHardwareMonitorLib.Tests.csproj -c Release -p:Platform=x64
-
- - name: Run WinUI tests
- run: dotnet test LibreHardwareMonitor.Windows.WinUI.Tests\LibreHardwareMonitor.Windows.WinUI.Tests.csproj -c Release -p:Platform=x64
-
- - name: Publish x64 libraries
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
- with:
- name: LibreHardwareMonitorLib (x64)
- path: |
- bin/Release/x64
-
- - name: Build reference libraries
- run: dotnet build LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release --no-restore -p:BuildOnlyRefs=true
-
- - name: Pack main nupkg
- run: dotnet pack LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release -p:PackMainNuget=true --no-build --output bin\Release
-
- - name: Publish nupkg
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
- with:
- name: LibreHardwareMonitorLib-nupkg
- path: |
- bin/Release/LibreHardwareMonitorLib.*.nupkg
-
- - name: Publish to NuGet
- if: github.repository == 'LibreHardwareMonitor/LibreHardwareMonitor'
- run: nuget push bin\Release\LibreHardwareMonitorLib.*.nupkg -Source 'https://api.nuget.org/v3/index.json' -ApiKey ${{secrets.NUGET_API_KEY}}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..74d46cb
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,146 @@
+name: release
+
+on:
+ push:
+ tags:
+ - "v*"
+ workflow_dispatch:
+ inputs:
+ version:
+ description: "Version/tag to create (e.g. v1.2.3)"
+ required: false
+ type: string
+
+permissions:
+ contents: write
+
+jobs:
+ build:
+ runs-on: windows-2025-vs2026
+ steps:
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
+
+ - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
+ with:
+ dotnet-version: "10.x"
+
+ - uses: nuget/setup-nuget@fd55a6f3b34392fa83fde1454582407d8c714123 # 4.0.0
+
+ - uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # 3.0.0
+ with:
+ msbuild-architecture: x64
+
+ - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # 4.0.1
+ id: changes
+ with:
+ filters: |
+ buildprops:
+ - 'Directory.Build.props'
+
+ - name: Update version
+ run: |
+ $inputVersion = "${{ inputs.version }}"
+ if ($inputVersion) {
+ $cleanVersion = $inputVersion -replace '^v', ''
+ (Get-Content Directory.Build.props) | % {
+ $m = [regex]::match($_, '(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?');
+ if($m.Success) { $_ -replace $m.Value, "$cleanVersion"; }
+ else { $_; }
+ } | Set-Content Directory.Build.props
+ } elseif ('${{ steps.changes.outputs.buildprops }}' -eq 'false') {
+ (Get-Content Directory.Build.props) | % {
+ $m = [regex]::match($_, '(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?');
+ if(!$m.Success -or $m.Groups[4].Success -or $m.Groups[5].Success) { $_; }
+ else { $_ -replace $m.Value, ("{0}.{1}.{2}-pre${{ github.run_number }}" -f $m.Groups[1].Value,$m.Groups[2].Value,([convert]::ToInt32($m.Groups[3].Value)+1)); }
+ } | Set-Content Directory.Build.props
+ }
+
+ - name: Get Version
+ id: get_version
+ run: |
+ $content = Get-Content Directory.Build.props
+ $m = [regex]::match($content, '([^<]+)')
+ if ($m.Success) {
+ $version = $m.Groups[1].Value
+ } else {
+ $version = "1.0.0-draft"
+ }
+ echo "VERSION=$version" >> $env:GITHUB_OUTPUT
+ echo "Detected version: $version"
+
+ - name: Determine Tag
+ id: tag_name
+ run: |
+ $inputVersion = "${{ inputs.version }}"
+ if ($inputVersion) {
+ $tag = $inputVersion
+ if ($tag -notmatch '^v') {
+ $tag = "v" + $tag
+ }
+ } elseif ("${{ github.ref_type }}" -eq "tag") {
+ $tag = "${{ github.ref_name }}"
+ } else {
+ $tag = "v${{ steps.get_version.outputs.VERSION }}"
+ }
+ echo "TAG=$tag" >> $env:GITHUB_OUTPUT
+ echo "Release tag: $tag"
+
+ - name: Restore application packages
+ run: dotnet restore LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj
+
+ - name: Build application
+ run: dotnet build LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj -c Release --no-restore -p:Platform=x64
+
+ - name: Restore library packages
+ run: dotnet restore LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj
+
+ - name: Build x64 libraries
+ run: dotnet build LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release --no-restore -p:Platform=x64
+
+ - name: Run library tests
+ run: dotnet test LibreHardwareMonitorLib.Tests\LibreHardwareMonitorLib.Tests.csproj -c Release -p:Platform=x64
+
+ - name: Run WinUI tests
+ run: dotnet test LibreHardwareMonitor.Windows.WinUI.Tests\LibreHardwareMonitor.Windows.WinUI.Tests.csproj -c Release -p:Platform=x64
+
+ - name: Publish WinUI application
+ run: dotnet publish LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj -c Release -r win-x64 -p:Platform=x64 -p:PublishSingleFile=true -p:PublishSelfContained=true --self-contained true --no-restore -o publish/app
+
+ - name: Stage Release Assets
+ run: |
+ New-Item -ItemType Directory -Force -Path release-staging
+
+ # Copy application binary (single file, self-contained, with .NET version)
+ Copy-Item publish/app/LibreHardwareMonitor.Windows.WinUI.exe release-staging/LibreHardwareMonitor.Windows.WinUI-net10.0.exe
+
+ # Copy library DLL (net10.0 build)
+ Copy-Item bin/Release/x64/net10.0/LibreHardwareMonitorLib.dll release-staging/LibreHardwareMonitorLib.dll
+
+ # Create source code ZIP
+ git archive --format=zip --output release-staging/LibreHardwareMonitor-source.zip HEAD
+
+ # Generate SHA256 checksums
+ $sha256 = [System.Security.Cryptography.SHA256]::Create()
+ Get-ChildItem release-staging | Where-Object { $_.Name -ne "SHA256SUMS.txt" } | ForEach-Object {
+ $fileStream = [System.IO.File]::OpenRead($_.FullName)
+ $hashBytes = $sha256.ComputeHash($fileStream)
+ $fileStream.Close()
+ $hash = [System.BitConverter]::ToString($hashBytes).Replace("-", "").ToLower()
+ "{0} {1}" -f $hash, $_.Name
+ } | Set-Content release-staging/SHA256SUMS.txt
+
+ # Log hashes for visibility
+ Get-Content release-staging/SHA256SUMS.txt
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@c062e0e6d8b788de3acf9a2f64f336f74783b400 # v2.0.8
+ with:
+ tag_name: ${{ steps.tag_name.outputs.TAG }}
+ name: ${{ steps.tag_name.outputs.TAG }}
+ draft: false
+ prerelease: ${{ contains(steps.tag_name.outputs.TAG, '-pre') }}
+ files: |
+ release-staging/LibreHardwareMonitor.Windows.WinUI-net10.0.exe
+ release-staging/LibreHardwareMonitorLib.dll
+ release-staging/LibreHardwareMonitor-source.zip
+ release-staging/SHA256SUMS.txt
diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/status-checks.yml
similarity index 84%
rename from .github/workflows/pull-requests.yml
rename to .github/workflows/status-checks.yml
index 092ff31..8fc3e3a 100644
--- a/.github/workflows/pull-requests.yml
+++ b/.github/workflows/status-checks.yml
@@ -1,4 +1,4 @@
-name: pull requests
+name: status-checks
on:
pull_request:
@@ -7,7 +7,7 @@ on:
jobs:
build:
- runs-on: windows-2025
+ runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
@@ -59,22 +59,8 @@ jobs:
- name: Run WinUI tests
run: dotnet test LibreHardwareMonitor.Windows.WinUI.Tests\LibreHardwareMonitor.Windows.WinUI.Tests.csproj -c Release -p:Platform=x64
- - name: Publish x64 libraries
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
- with:
- name: LibreHardwareMonitorLib (x64)
- path: |
- bin/Release/x64
-
- name: Build reference libraries
run: dotnet build LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release --no-restore -p:BuildOnlyRefs=true
- name: Pack main nupkg
run: dotnet pack LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj -c Release -p:PackMainNuget=true --no-build --output bin\Release
-
- - name: Publish nupkg
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # 7.0.1
- with:
- name: LibreHardwareMonitorLib-nupkg
- path: |
- bin/Release/LibreHardwareMonitorLib.*.nupkg
diff --git a/LibreHardwareMonitor.Windows.WinUI.Tests/Services/AppSettingsTests.cs b/LibreHardwareMonitor.Windows.WinUI.Tests/Services/AppSettingsTests.cs
index e77489f..1953206 100644
--- a/LibreHardwareMonitor.Windows.WinUI.Tests/Services/AppSettingsTests.cs
+++ b/LibreHardwareMonitor.Windows.WinUI.Tests/Services/AppSettingsTests.cs
@@ -18,7 +18,7 @@ public class AppSettingsTests : IDisposable
// Use reflection to instantiate AppSettings with custom filename
var ctor = typeof(AppSettings).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null);
- _settings = (AppSettings)ctor.Invoke(new object[] { _tempFile });
+ _settings = (AppSettings)ctor!.Invoke(new object[] { _tempFile });
}
public void Dispose()
@@ -109,7 +109,7 @@ public class AppSettingsTests : IDisposable
// Create new instance pointing to same file
var ctor = typeof(AppSettings).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null);
- var loadedSettings = (AppSettings)ctor.Invoke(new object[] { _tempFile });
+ var loadedSettings = (AppSettings)ctor!.Invoke(new object[] { _tempFile });
loadedSettings.Load();
diff --git a/LibreHardwareMonitor.Windows.WinUI.Tests/Utilities/SensorFormatterTests.cs b/LibreHardwareMonitor.Windows.WinUI.Tests/Utilities/SensorFormatterTests.cs
index ca9dc74..81ca386 100644
--- a/LibreHardwareMonitor.Windows.WinUI.Tests/Utilities/SensorFormatterTests.cs
+++ b/LibreHardwareMonitor.Windows.WinUI.Tests/Utilities/SensorFormatterTests.cs
@@ -221,6 +221,7 @@ public class SensorFormatterTests
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
var result = SensorFormatter.GetPlotValue(mockSensor.Object, TemperatureUnit.Fahrenheit);
+ Assert.NotNull(result);
Assert.Equal(77.9, result.Value, 1);
}
@@ -243,6 +244,7 @@ public class SensorFormatterTests
var result = SensorFormatter.GetPlotValue(mockSensor.Object, 25.5f, TemperatureUnit.Fahrenheit);
+ Assert.NotNull(result);
Assert.Equal(77.9, result.Value, 1);
}
diff --git a/LibreHardwareMonitor.Windows.WinUI.Tests/ViewModels/SimpleViewModelsTests.cs b/LibreHardwareMonitor.Windows.WinUI.Tests/ViewModels/SimpleViewModelsTests.cs
index 57afbdc..d88101e 100644
--- a/LibreHardwareMonitor.Windows.WinUI.Tests/ViewModels/SimpleViewModelsTests.cs
+++ b/LibreHardwareMonitor.Windows.WinUI.Tests/ViewModels/SimpleViewModelsTests.cs
@@ -11,7 +11,7 @@ public class SimpleViewModelsTests
{
private class TestViewModel : ViewModelBase
{
- private string _testProperty;
+ private string _testProperty = string.Empty;
public string TestProperty
{
get => _testProperty;
@@ -28,7 +28,7 @@ public class SimpleViewModelsTests
public void ViewModelBase_SetProperty_ChangesValueAndRaisesEvent()
{
var vm = new TestViewModel();
- string changedProperty = null;
+ string? changedProperty = null;
vm.PropertyChanged += (sender, args) => changedProperty = args.PropertyName;
// Change value
@@ -57,7 +57,7 @@ public class SimpleViewModelsTests
public void ViewModelBase_OnPropertyChanged_RaisesEvent()
{
var vm = new TestViewModel();
- string changedProperty = null;
+ string? changedProperty = null;
vm.PropertyChanged += (sender, args) => changedProperty = args.PropertyName;
vm.TriggerPropertyChanged("CustomProperty");