Bump to v1.7.0: self-updater release-filtering fix

The client and the relay server are published from the same GitHub repo;
the server's releases use "server-" prefixed tags. RemSoundUpdater hit
/releases/latest, which is repo-wide — when a server release was newest,
the updater fed "server-v2.3" to ParseTag (-> a bogus 0.0.3) and concluded
"up to date", silently skipping real client updates.

CheckForUpdateAsync now lists /releases and picks the highest-versioned
release whose tag is a RemSound client tag (new IsClientReleaseTag: after
an optional leading "v", first char must be a digit). Drafts and
pre-releases are skipped. The server-side updater already filters to
"server-" tags, so client + server coexist in one repo cleanly.

Also rewrites build-release.ps1 with a data-safety check: it publishes to
a fresh staging folder and aborts the release if any logs/, profiles/,
recordings/ folder, .log file or remsound.config.json is present in the
staged output or the finished zip — preventing a repeat of the v1.5/v1.6
zips that shipped with developer logs and profiles.

No wire-format or audio-pipeline changes — v1.5/v1.6/v1.7 interoperate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-05-18 22:05:40 +01:00
co-authored by Claude Opus 4.7
parent 918ca6cac0
commit 6b1ae2d017
5 changed files with 157 additions and 38 deletions
+17
View File
@@ -20,6 +20,23 @@ internal sealed class AboutDialog : Form
/// updates" path.</summary>
private const string ReleaseNotes =
"""
RemSound v1.7
Updater fix. No wire-format or audio-pipeline changes
v1.5, v1.6 and v1.7 peers interoperate.
Bug fix:
* Check for updates now reliably finds RemSound
releases. RemSound and the relay server share one
GitHub repository; the server's releases use
"server-" tags. The updater previously looked only
at the single newest release of any kind, so a
server release could derail it (it would misread
the version and report "up to date"). It now scans
the release list and considers only RemSound client
versions, ignoring server releases, drafts and
pre-releases.
RemSound v1.6
Three reliability fixes. No wire-format or audio-pipeline
+1 -1
View File
@@ -14,7 +14,7 @@
tag_name on the latest GitHub release; bump it on every public release. The
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
is what the About dialog and the updater both read. -->
<Version>1.6.0</Version>
<Version>1.7.0</Version>
</PropertyGroup>
<ItemGroup>
+42 -5
View File
@@ -64,7 +64,13 @@ internal sealed class RemSoundUpdater : IDisposable
{
try
{
var url = $"https://api.github.com/repos/{RepoOwner}/{RepoName}/releases/latest";
// List releases — NOT /releases/latest. The repo also hosts the relay server's
// own "server-vX.Y" releases, and /releases/latest is repo-wide: it hands back
// whichever release is newest by date, server or client. A server release would
// then be fed to ParseTag ("server-v2.3" -> a bogus 0.0.3) and the updater would
// wrongly conclude "up to date". We pull the list and consider ONLY releases
// whose tag is a RemSound client tag (see IsClientReleaseTag). 2026-05-18.
var url = $"https://api.github.com/repos/{RepoOwner}/{RepoName}/releases";
Log?.Invoke($"updater: GET {url}");
using var req = new HttpRequestMessage(HttpMethod.Get, url);
req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
@@ -75,14 +81,30 @@ internal sealed class RemSoundUpdater : IDisposable
return null;
}
await using var stream = await resp.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
var release = await JsonSerializer.DeserializeAsync<GitHubRelease>(stream, JsonOpts, token).ConfigureAwait(false);
if (release?.TagName is null)
var releases = await JsonSerializer.DeserializeAsync<List<GitHubRelease>>(stream, JsonOpts, token).ConfigureAwait(false);
if (releases is null || releases.Count == 0)
{
Log?.Invoke("updater: response had no tag_name");
Log?.Invoke("updater: releases list was empty");
return null;
}
// Highest-versioned RemSound client release. Skip drafts, prereleases, and any
// tag that isn't a client tag (notably the server-vX.Y relay releases).
GitHubRelease? release = null;
var latest = new Version(0, 0, 0);
foreach (var r in releases)
{
if (r.TagName is null || r.Draft || r.Prerelease) continue;
if (!IsClientReleaseTag(r.TagName)) continue;
var v = ParseTag(r.TagName);
if (v > latest) { latest = v; release = r; }
}
if (release?.TagName is null)
{
Log?.Invoke("updater: no RemSound client release found in the releases list");
return null;
}
var latest = ParseTag(release.TagName);
var current = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0);
Log?.Invoke($"updater: current={current.ToString(3)} latest={latest.ToString(3)} ({release.TagName})");
if (latest <= current) return null;
@@ -259,6 +281,19 @@ internal sealed class RemSoundUpdater : IDisposable
/// <summary>Parses a release tag like <c>v1.2</c> or <c>1.2.3</c> into a <see cref="Version"/>.
/// Leading "v" is stripped. Missing minor/build parts get filled with zeros so the result
/// always compares meaningfully against <see cref="Assembly.GetName"/>.Version.</summary>
/// <summary>True if <paramref name="tag"/> is a RemSound client release tag — e.g.
/// <c>v1.6</c>, <c>1.6</c>, <c>1.6.0</c> — rather than something else hosted in the same
/// GitHub repo, notably the relay server's <c>server-vX.Y</c> releases. Test: after an
/// optional leading <c>v</c>, the first character must be a digit. <c>server-v2.3</c>
/// starts with 's' and is rejected; <c>v1.6</c> is accepted. The updater must filter on
/// this because it lists all repo releases and the server publishes into the same repo.</summary>
public static bool IsClientReleaseTag(string? tag)
{
if (string.IsNullOrWhiteSpace(tag)) return false;
var trimmed = tag.TrimStart('v', 'V').Trim();
return trimmed.Length > 0 && char.IsDigit(trimmed[0]);
}
public static Version ParseTag(string tag)
{
if (string.IsNullOrWhiteSpace(tag)) return new Version(0, 0, 0);
@@ -298,6 +333,8 @@ internal sealed class RemSoundUpdater : IDisposable
[JsonPropertyName("tag_name")] public string? TagName { get; set; }
[JsonPropertyName("body")] public string? Body { get; set; }
[JsonPropertyName("html_url")] public string? HtmlUrl { get; set; }
[JsonPropertyName("draft")] public bool Draft { get; set; }
[JsonPropertyName("prerelease")] public bool Prerelease { get; set; }
[JsonPropertyName("assets")] public List<GitHubAsset>? Assets { get; set; }
}