Add release process: version, CHANGELOG, /whats-new page#68
Conversation
Single-source-of-truth version (<Version> in Directory.Build.props) inherited by all
.csproj files. .NET SDK auto-derives AssemblyInformationalVersionAttribute and appends
the git SHA, which the toolbar reads at runtime to render "v{version} . {commit}".
CHANGELOG.md follows Keep-a-Changelog; user-facing wording only (New Features /
Bugfixes), no internal refactor talk, no [Unreleased] placeholder. Version headings
link to GitHub releases pages.
CLAUDE.md (project + global) documents the release workflow: release-prep PR to
develop bumps Version and adds the dated CHANGELOG section before the develop -> main
direct merge + tag + gh release create. Updating the changelog is mandatory.
Initial version: 0.6.0 (Bundesliga + four European leagues + per-competition Elo set).
| </MudLayout> | ||
|
|
||
| @code { | ||
| static readonly string _versionFull = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion; |
There was a problem hiding this comment.
Null-forgiving ! in a static field initializer — unrecoverable crash if attribute is absent
GetCustomAttribute<AssemblyInformationalVersionAttribute>() returns null when the attribute isn't present on the assembly (e.g. <GenerateAssemblyInfo>false</GenerateAssemblyInfo>, a trimmer root that drops it, or the component being loaded in a test/mock host). Because this is a static readonly initializer the resulting NullReferenceException is wrapped in TypeInitializationException — a permanent failure that poisons the type for the lifetime of the WASM module. Every subsequent navigation attempt then throws the same TypeInitializationException with no error-boundary recovery possible.
Prefer a null-conditional + fallback so a missing attribute degrades gracefully instead of killing the whole layout:
| static readonly string _versionFull = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion; | |
| static readonly string _versionFull = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "0.0.0"; |
There was a problem hiding this comment.
🟡 Partial — done in 175d24d: null-coalesce applied (?.InformationalVersion ?? "unknown"). Took the bot's instinct (static-init poisoning is severe), but used "unknown" instead of the suggested "0.0.0" — a missing attribute should be visibly anomalous rather than mimic a real version. Matches the fallback in MtgCsvHelper's AppInfo.
| // Rewrites every absolute http(s) anchor to open in a new tab. Without this, clicking a PR or | ||
| // compare link inside the rendered CHANGELOG would navigate the whole Blazor SPA away from the app. |
There was a problem hiding this comment.
Convention: multi-line // comment block — cap at one line; the why belongs in the commit message.
| // Rewrites every absolute http(s) anchor to open in a new tab. Without this, clicking a PR or | |
| // compare link inside the rendered CHANGELOG would navigate the whole Blazor SPA away from the app. | |
| // Rewrites external anchors to open in a new tab so SPA navigation isn't hijacked. |
There was a problem hiding this comment.
✅ Done in 175d24d: collapsed to the one-liner you suggested. (My own rule, doubly so when responding to bot review comments — thanks for the catch.)
Review summaryClean, purposeful PR — the release-process plumbing (single-source version, CHANGELOG, Findings (inline above)
Latent note (no action required now)
Things investigated and found clean
|
- MainLayout.razor: null-coalesce InformationalVersion lookup; a missing attribute would otherwise throw TypeInitializationException in the static initializer and poison the layout for the session. Fallback "unknown" makes the failure visible rather than displaying a fake version. - WhatsNew.razor: collapse two-line comment to one line per the inline-comments rule.
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Markdig" Version="1.0.0" /> |
There was a problem hiding this comment.
Build-breaking: Markdig 1.0.0 does not exist on NuGet.
Markdig (by xoofx) uses 0.x.y versioning — the latest stable release is in the 0.40.x range. There is no 1.0.0 on NuGet. dotnet restore will fail to resolve the package, blocking every build of this project (and Web/MAUI which reference it).
| <PackageReference Include="Markdig" Version="1.0.0" /> | |
| <PackageReference Include="Markdig" Version="0.40.0" /> |
There was a problem hiding this comment.
❌ Skipped — the claim is wrong. NuGet's flatcontainer API for markdig lists 1.0.0-preview.1, 1.0.0, 1.0.1, 1.1.0-1.1.3, 1.2.0 as stable releases (https://api.nuget.org/v3-flatcontainer/markdig/index.json). Markdig did spend years on 0.x versioning but went 1.0 in mid-2025. Our build resolves the package against nuget.org without overrides — staying on 1.0.0 for parity with MtgCsvHelper.
Toolbar shows "v{version} . {commit}" between the brand and the existing icons; the
caption links to a new /whats-new page that fetches CHANGELOG.md from wwwroot and
renders it through Markdig. External links open in a new tab.
CHANGELOG.md is staged into wwwroot at build time by FantasyFootball.Web.csproj
(StageRepoMarkdown AfterTargets=Build); the staged copy is gitignored. HttpClient
with HostEnvironment.BaseAddress is registered in Program.cs so the page can fetch
the file.
Styles for the rendered content live in app.css (.changelog-content rules) rather
than scoped CSS isolation, because the MudPaper wrapper's rendered <div> doesn't
pick up the scope attribute that ::deep selectors require.
175d24d to
013137e
Compare
|
|
||
| Bump this on each release; do not put <Version> in individual .csproj files. | ||
| --> | ||
| <Version>0.6.0</Version> |
There was a problem hiding this comment.
FantasyFootball.Maui.csproj still has <Version>$(ApplicationDisplayVersion)</Version> (line 23), which evaluates to 0.5.0 — it will override this inherited value because a property set inside the project file takes precedence over Directory.Build.props. Both the MAUI assembly stamp and ApplicationDisplayVersion stay at 0.5.0 while every other project picks up 0.6.0. Needs to be updated for each release along with Directory.Build.props.
Summary
Sets up a release process modelled on MtgCsvHelper: a single-source-of-truth version, a user-facing CHANGELOG, and an in-app
/whats-newpage that renders it.Directory.Build.propsat repo root —<Version>0.6.0</Version>inherited by every.csproj. The .NET SDK auto-appends the git SHA toAssemblyInformationalVersionAttribute, which the toolbar reads at runtime.CHANGELOG.mdat repo root — Keep-a-Changelog, user-facing wording only (New Features/Bugfixes).0.6.0entry covers Bundesliga, the four European leagues, per-competition Elo set, "Restore historical competitions" button, and the historical-tournament restore fix./whats-newpage — Markdig-rendered CHANGELOG behind av0.6.0 · {commit}link in the toolbar. External links open in new tabs.<Version>+ dates a new CHANGELOG section (mandatory — never tag without it); then direct mergedevelop→main(no PR), tag,gh release create.Test plan
dotnet buildclean (was clean locally)./whats-newrenders the 0.6.0 + prior version entries with proper spacing; PR links open in new tabs; the version-tag link goes to the GitHub releases page for the tag.v0.6.0 · {commit}between the brand and the help/dark-mode icons; hover shows the tooltip; click navigates to/whats-new.gitignorecorrectly excludes the stagedsrc/FantasyFootball.Web/wwwroot/CHANGELOG.mdcopy.