Skip to content

README pre-release badges (GitHub + NuGet) show latest stable instead of latest pre-release #83

Description

@ptr727

Problem

The template's README.md includes two pre-release badges that don't behave as their labels imply:

[prereleaseversion-shield]: https://img.shields.io/github/v/release/<owner>/<repo>?include_prereleases&label=GitHub%20Pre-Release&logo=github
[nugetprereleaseversion-shield]: https://img.shields.io/nuget/vpre/<package>?logo=nuget&label=NuGet%20Pre-Release&color=orange

Both endpoints use "include pre-releases as candidates, return the latest by date / highest semver" semantics — not "show pre-releases only". After every develop → main release merge, Nerdbank.GitVersioning publishes a full release with a height higher than any preceding pre-release, and within seconds both pre-release badges converge to the same value as their stable-only counterparts.

Repro from a downstream consumer

Observed in ptr727/LanguageTags (which was synced to this template on 2026-05-12 via LanguageTags#130). After the develop→main release merge in LanguageTags#144 landed today at 2026-05-13T15:39:59Z:

Badge Displayed Actual latest pre-release
GitHub Pre-Release v1.2.42 (full release from #144) 1.2.40-g0f69a1b0a0
NuGet Pre-Release v1.2.42 (full release from #144) 1.2.40-g0f69a1b0a0

gh api 'repos/ptr727/LanguageTags/releases?per_page=8' shows the candidate ordering by published_at:

Published Tag prerelease
2026-05-13T15:42:00Z 1.2.42 false
2026-05-13T15:00:27Z 1.2.40-g0f69a1b0a0 true
2026-05-13T14:47:38Z 1.2.39-g377b7c0b67 true
2026-05-13T03:01:06Z 1.2.41 false

Shields' include_prereleases and vpre both pick 1.2.42 because it's latest by date / highest semver.

Fix

Applied downstream in ptr727/LanguageTags#145 — same recipe applies here:

1. GitHub Pre-Release badge — add filter=*-g*

Shields' github/v/release endpoint supports a filter glob. Nerdbank.GitVersioning's pre-release tags carry a -g<10-hex> short-sha suffix (e.g. 1.2.40-g0f69a1b0a0); stable tags don't. So filter=*-g* cleanly selects pre-releases.

Edit the link-reference definition in README.md:

-[prereleaseversion-shield]: https://img.shields.io/github/v/release/<owner>/<repo>?include_prereleases&label=GitHub%20Pre-Release&logo=github
+[prereleaseversion-shield]: https://img.shields.io/github/v/release/<owner>/<repo>?include_prereleases&filter=*-g*&label=GitHub%20Pre-Release&logo=github

Verified live against LanguageTags: the filtered URL renders v1.2.40-g0f69a1b0a0 correctly.

2. NuGet Pre-Release badge — drop it

The NuGet shield endpoint accepts only the path-level v (stable) vs vpre (latest including pre-releases) — there's no filter parameter and the docs confirm only the standard styling/label/color/cache/link parameters. I tested https://img.shields.io/nuget/vpre/<package>?filter=*-g* and the filter is silently ignored.

Escape hatches that don't work:

  • dynamic/json shield with JSONPath filter predicate ($[?(@.prerelease==true)]) → shields.io returns "query not supported".
  • dynamic/json with $.versions[-1] against the NuGet flat-container index → NuGet sorts pre-releases ahead of their stable counterparts in semver order, so the last entry is the highest stable. Same wrong answer.

No clean shields.io fix exists. Drop both the badge usage in the README body and the link-reference definition:

 [![GitHub Release][releaseversion-shield]][releases-link]\
 [![GitHub Pre-Release][prereleaseversion-shield]][releases-link]\
-[![NuGet Release][nugetreleaseversion-shield]][nuget-link]\
-[![NuGet Pre-Release][nugetprereleaseversion-shield]][nuget-link]
+[![NuGet Release][nugetreleaseversion-shield]][nuget-link]
 [nuget-link]: https://www.nuget.org/packages/<package>/
 [nugetreleaseversion-shield]: https://img.shields.io/nuget/v/<package>?logo=nuget&label=NuGet%20Release
-[nugetprereleaseversion-shield]: https://img.shields.io/nuget/vpre/<package>?logo=nuget&&label=NuGet%20Pre-Release&color=orange

Note the second-to-last token &&label= (double-ampersand typo) in the existing template line — would be removed alongside.

The GitHub Pre-Release badge with the filter fix carries the develop-side pre-release signal on its own. The NuGet pre-release data is structurally redundant with the NuGet Release badge whenever a higher stable exists, which is the steady state after every release-on-every-push cycle.

Verification recipe

After the edits, hit the new shield URLs directly and confirm the rendered value:

# Using a downstream repo with current pre-releases (LanguageTags works as proof):
# GitHub Pre-Release with filter → expect a tag containing "-g<sha>"
curl -sS 'https://img.shields.io/github/v/release/ptr727/LanguageTags?include_prereleases&filter=*-g*' | grep -oE '1\.[0-9]+\.[0-9]+-g[0-9a-f]+'

# GitHub Release (unchanged) → expect a clean semver tag with no -g suffix
curl -sS 'https://img.shields.io/github/v/release/ptr727/LanguageTags' | grep -oE '1\.[0-9]+\.[0-9]+(?=<)'

Or use any markdown previewer on the README and confirm the two badges show distinct values.

Acceptance

  • README.md prereleaseversion-shield URL includes &filter=*-g*.
  • README.md nugetprereleaseversion-shield reference definition removed.
  • README.md body block of badges drops the NuGet Pre-Release usage and ends the chain on NuGet Release (without trailing \ on the last badge in the chain).
  • Rendered README shows three pre-release-aware badges instead of four: GitHub Release, GitHub Pre-Release (now actually pre-release), NuGet Release.

Cross-references

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions