From 8e4c9b1e8a1dcd408efda5dc7eb934c37ee23a42 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sat, 10 Oct 2026 21:18:37 +0200 Subject: [PATCH] README: the Go badge reads go.mod instead of restating it The badge has now been corrected three times for the same reason, and the third time it was corrected and then UNDONE: on 2026-10-10 Renovate force-pushed its branch over a commit that had fixed the badge and gone green on every lane, reinstating 1.27.1 against a 1.27.2 directive. A number a human has to retype is a number that comes back. So it stops being retyped. shields.io's github/go-mod/go-version endpoint reads the directive out of the repository -- verified against this one, where it renders "Go: v1.27.1" -- which takes the badge out of the maintenance loop entirely, the same way the release badge stopped naming a version in #801. The guard gets STRICTER, not weaker, in three ways: A dynamic badge is not compared against a value, because it cannot disagree with one. Instead the test now fails if a LITERAL badge is left beside it -- that would be a second, rottable copy, which is the thing being removed. The number of hand-written statements required drops from 2 to 1 only when the badge is dynamic. Demanding two would force somebody to add a rottable number back. It never drops to 0: a README that states the minimum nowhere would pass a check for "no WRONG statement" by saying nothing at all, and a reader deciding whether they can depend on the module needs the number on the page. No badge of either kind is still a hard failure. Checked in four directions, and one of the four controls was itself wrong, which is worth recording: setting the directive to a nonexistent 1.27.9 made `go test` refuse to START, so the guard looked green when it had simply never run. The file had changed -- which is what I had verified -- and the test had not executed, which is what I had not. Redone with 1.27.2 it fails correctly: README states a Go minimum of 1.27.1 in "Go 1.27.1+"; go.mod requires 1.27.2 Co-Authored-By: Claude Opus 5 --- README.md | 2 +- readme_go_floor_test.go | 55 ++++++++++++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 055a4517..1f310002 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Docs](https://img.shields.io/badge/docs-mkdocs--material-9B1C2E)](https://go-embedded-ruby.github.io/docs/) [![License](https://img.shields.io/badge/license-BSD--3--Clause-blue)](LICENSE) -[![Go](https://img.shields.io/badge/go-1.27.1%2B-00ADD8)](https://go.dev/dl/) +[![Go](https://img.shields.io/github/go-mod/go-version/go-embedded-ruby/ruby?label=go&color=00ADD8)](https://go.dev/dl/) [![Release](https://img.shields.io/github/v/release/go-embedded-ruby/ruby?label=release&color=blue)](https://github.com/go-embedded-ruby/ruby/releases/latest) [![ruby/spec](https://img.shields.io/badge/ruby%2Fspec-23%2C511%20examples%20passing-1a7f37)](#runtime-conformance--rubyspec) diff --git a/readme_go_floor_test.go b/readme_go_floor_test.go index 6ee6acca..8d822356 100644 --- a/readme_go_floor_test.go +++ b/readme_go_floor_test.go @@ -34,14 +34,34 @@ func TestReadmeGoBadgeMatchesTheModuleFloor(t *testing.T) { if err != nil { t.Fatalf("reading README.md: %v", err) } - // The badge is shields.io with the `+` URL-encoded: go-1.27.1%2B - badge := regexp.MustCompile(`badge/go-(\d+\.\d+(?:\.\d+)?)%2B`).FindSubmatch(readme) - if badge == nil { - t.Fatalf("README.md has no Go version badge; expected one claiming %s", floor) - } - if got := string(badge[1]); got != floor { - t.Errorf("README Go badge claims %s, go.mod requires %s\n update the badge to go-%s%%2B", - got, floor, floor) + // A badge that READS go.mod cannot go stale, so it is not checked for a + // value: the answer to a number that rots is to stop restating it, not to + // keep correcting it. shields.io's github/go-mod/go-version endpoint renders + // the directive directly -- verified against this repository, where it shows + // "Go: v1.27.1" -- which takes the badge out of the maintenance loop. + // + // What turned that from a preference into a fix: on 2026-10-10 Renovate + // FORCE-PUSHED its branch over a commit that had corrected the literal badge + // and gone green on every lane, reinstating 1.27.1 against a 1.27.2 + // directive. A number a human has to retype is a number that comes back. + dynamic := regexp.MustCompile(`img\.shields\.io/github/go-mod/go-version/`).Match(readme) + literalBadge := regexp.MustCompile(`badge/go-(\d+\.\d+(?:\.\d+)?)%2B`).FindSubmatch(readme) + switch { + case dynamic: + // Nothing to compare against. Guard the replacement instead: a literal + // badge left beside the dynamic one would be a second, rottable copy. + if literalBadge != nil { + t.Errorf("README has BOTH a dynamic Go badge and a literal one claiming %s; "+ + "the literal one can go stale, which is the thing the dynamic one removes", + literalBadge[1]) + } + case literalBadge == nil: + t.Fatalf("README.md has no Go version badge, literal or dynamic; expected one claiming %s", floor) + default: + if got := string(literalBadge[1]); got != floor { + t.Errorf("README Go badge claims %s, go.mod requires %s\n update the badge to go-%s%%2B", + got, floor, floor) + } } // The badge is not the only place the README states the floor, and a guard @@ -64,8 +84,21 @@ func TestReadmeGoBadgeMatchesTheModuleFloor(t *testing.T) { m[1], strings.TrimSpace(m[0]), floor) } } - if found < 2 { - t.Errorf("found %d statements of the Go minimum in README.md, expected at least 2 "+ - "(the badge and the prose requirement); if one was removed on purpose, lower this", found) + // How many hand-written statements must remain depends on whether the badge + // is still one of them. With a dynamic badge there is exactly one copy left + // to keep honest, and demanding two would force someone to add a second + // rottable number back -- which is the opposite of the point. + // + // The floor is 1 either way, never 0: a README that states the minimum + // NOWHERE in prose would pass a check for "no wrong statement" by saying + // nothing, and a reader deciding whether they can depend on the module needs + // the number on the page. + want := 2 + if dynamic { + want = 1 + } + if found < want { + t.Errorf("found %d statement(s) of the Go minimum in README.md, expected at least %d "+ + "(dynamic badge: %v); if one was removed on purpose, lower this", found, want, dynamic) } }