8f6f2ba8d6
/health read the APP_VERSION environment variable directly, but docker-compose passes App__Version, which binds to the App:Version configuration key. The variable under that name never existed in the container, so the endpoint always reported "unknown". Read App:Version through IConfiguration, the approach AdminSystemController already used for the same value. The resolution rule (configured version, else assembly version) moves to a shared BuildMetadata helper rather than being written twice; AdminSystemController now calls it, so the admin page and /health cannot drift apart. Local development is unaffected: nothing sets App:Version there, and the assembly-version fallback still applies. Tests pin the configuration KEY, not just the behaviour, including that an App__Version environment variable binds to App:Version. The original bug failed silently, so a behavioural test alone would not have caught it. Verified against a running backend: App__Version=9.9.9-test reports 9.9.9-test; unset reports the assembly version rather than "unknown". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
40 lines
1.6 KiB
C#
40 lines
1.6 KiB
C#
using System.Reflection;
|
|
|
|
namespace JobTrackerApi.Services;
|
|
|
|
// Build metadata (version, commit, stamp) comes from configuration, populated by
|
|
// docker-compose as App__Version / App__CommitSha / App__BuildStamp.
|
|
//
|
|
// This lives here because two callers need the same answer: the admin system page and
|
|
// the anonymous /health endpoint. /health used to read the APP_VERSION *environment
|
|
// variable* directly, which compose never sets under that name — it passes App__Version,
|
|
// binding to the App:Version configuration key. So /health always reported "unknown".
|
|
// See docs/release-candidate-review.md (N2).
|
|
public static class BuildMetadata
|
|
{
|
|
// Ignore unresolved shell/compose placeholders that would otherwise leak into the
|
|
// admin UI or a health response, e.g. $(git rev-parse --short HEAD) or ${APP_COMMIT_SHA}.
|
|
public static string? Normalize(string? value)
|
|
{
|
|
var trimmed = (value ?? string.Empty).Trim();
|
|
if (string.IsNullOrWhiteSpace(trimmed)) return null;
|
|
|
|
if ((trimmed.StartsWith("$(") && trimmed.EndsWith(")")) || (trimmed.StartsWith("${") && trimmed.EndsWith("}")))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return trimmed;
|
|
}
|
|
|
|
// Configured version, else the assembly version, else "unknown". The assembly fallback
|
|
// is what makes local development useful: nothing sets App:Version there.
|
|
public static string ResolveVersion(IConfiguration cfg)
|
|
{
|
|
var version = Normalize(cfg["App:Version"]);
|
|
if (!string.IsNullOrWhiteSpace(version)) return version;
|
|
|
|
return Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
|
|
}
|
|
}
|