launcher: side-by-side versions.json fallback + empty-state UX

The version dropdown sat silently empty when the GitHub manifest
fetch 404'd (private repo, network down, etc.) and there was no
DORKNET_LOCAL_MANIFEST to fall back to. Two fixes:

- csproj copies the repo-root versions.json next to dorknet.exe on
  build, and LocalOverrides.GetLocalManifestPath checks that path
  as a fallback after the env var. Dev checkouts against a private
  repo now Just Work — no env vars needed.
- When the manifest can't be loaded, RefreshManifestAsync now
  inserts a disabled placeholder "(no versions - check internet or
  set DORKNET_LOCAL_MANIFEST)" in the dropdown so the user sees the
  problem at a glance instead of a silent blank.
- LocalOverrides.DescribeManifestSource shows in Settings whether
  the active manifest came from the env var, the side-by-side
  fallback, or the live GitHub fetch.
This commit is contained in:
Alexa
2026-05-27 12:05:21 +03:00
parent a5f1d74a8e
commit de98ea694f
3 changed files with 59 additions and 8 deletions
+31 -5
View File
@@ -26,14 +26,21 @@ public static class LocalOverrides
public const string ManifestEnvVar = "DORKNET_LOCAL_MANIFEST";
public const string ReleasesEnvVar = "DORKNET_LOCAL_RELEASES";
/// <summary>Returns the configured manifest path if the env var is
/// set AND the file exists; null otherwise. Caller decides whether
/// to fall back to the network.</summary>
/// <summary>Returns a local manifest path to use instead of the
/// GitHub fetch. Prefers <c>DORKNET_LOCAL_MANIFEST</c> when set; if
/// that's unset, falls back to a side-by-side <c>versions.json</c>
/// next to <c>dorknet.exe</c> (so dev checkouts against a private
/// repo work without env-var wiring — the launcher's csproj copies
/// the repo-root versions.json to its bin output on build).</summary>
public static string? GetLocalManifestPath()
{
var path = Environment.GetEnvironmentVariable(ManifestEnvVar);
if (string.IsNullOrEmpty(path)) return null;
return File.Exists(path) ? path : null;
if (!string.IsNullOrEmpty(path) && File.Exists(path)) return path;
var sideBySide = Path.Combine(AppContext.BaseDirectory, "versions.json");
if (File.Exists(sideBySide)) return sideBySide;
return null;
}
/// <summary>Returns the configured releases-root if the env var is
@@ -87,4 +94,23 @@ public static class LocalOverrides
? $"{envVar} -> {raw} [active]"
: $"{envVar} -> {raw} [SET BUT NOT FOUND on disk]");
}
/// <summary>Best-effort description of where the manifest is
/// actually coming from this session. Mainly for the Settings UI
/// so the user can tell at a glance "side-by-side dev manifest"
/// vs "live GitHub fetch" vs "broken env-var path".</summary>
public static string DescribeManifestSource()
{
var env = Environment.GetEnvironmentVariable(ManifestEnvVar);
if (!string.IsNullOrEmpty(env))
{
return File.Exists(env)
? $"manifest source: {env} (env var)"
: $"manifest source: {env} (env var, NOT FOUND — falling back)";
}
var sideBySide = Path.Combine(AppContext.BaseDirectory, "versions.json");
if (File.Exists(sideBySide))
return $"manifest source: {sideBySide} (side-by-side fallback)";
return "manifest source: live GitHub fetch (no local override)";
}
}