From 15737321ac9ac9dfa1a559cde739470baeee0fcb Mon Sep 17 00:00:00 2001 From: Mathias Date: Sat, 29 Aug 2026 10:07:27 +0200 Subject: [PATCH] fix(check): fail when the manifest is behind the tag, not merely different MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `task check` asserted plugin.json's version EQUALS the latest tag. `git describe` is local-only, so any checkout that had not fetched the tag CI just pushed went red on a commit that was correct — three times in one backlog session, on the loop push → CI tags and pushes the bump → pull → red until you also fetch tags. The fault the assertion exists for is the manifest failing to move: a release whose manifest still names the old version, so no installed host ever sees the update. That is manifest < tag. Ahead is normal and is now quiet. Ordering needed a real comparison, and bin/doctor had already grown one for #30, so it moves to bin/newer-version with its own tests rather than existing twice. Seven cases including the lexical trap (0.8.10 beats 0.8.9) and a missing component counting as zero. Verified by mutation: manifest 0.1.0 against tag v0.9.0 exits 201 naming both versions; manifest 9.9.9 exits 0; the real tree exits 0. Closes #34 Bump-Type: patch --- Taskfile.yml | 10 +++++++-- bin/doctor | 12 +++-------- bin/newer-version | 26 +++++++++++++++++++++++ tests/newer_version_test.sh | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 11 deletions(-) create mode 100755 bin/newer-version create mode 100644 tests/newer_version_test.sh diff --git a/Taskfile.yml b/Taskfile.yml index 23382ac..3d4a879 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -104,8 +104,14 @@ tasks: if [ -f '{{.PLUGIN_MANIFEST}}' ]; then manifest_v=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' '{{.PLUGIN_MANIFEST}}' | head -1) tag_v=$(git -C '{{.TASKFILE_DIR}}' describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo '') - if [ -n "$tag_v" ] && [ "$manifest_v" != "$tag_v" ]; then - echo "ERROR: plugin.json version ($manifest_v) != latest tag ($tag_v)" >&2 + # Ordering, not equality. The fault this guards is the manifest + # failing to MOVE — a release tag whose manifest still names the old + # version, so no installed host ever sees the update. A manifest + # ahead of the newest tag this checkout knows about is the normal + # state between the tagger's bump commit and a tag fetch (#34). + if [ -n "$tag_v" ] && [ "$manifest_v" != "$tag_v" ] \ + && [ "$(sh '{{.TASKFILE_DIR}}/bin/newer-version' "$manifest_v" "$tag_v")" = "$tag_v" ]; then + echo "ERROR: plugin.json version ($manifest_v) is behind the latest tag ($tag_v) — installed hosts never see the update" >&2 fail=1 fi else diff --git a/bin/doctor b/bin/doctor index 2cdac4e..55b7221 100755 --- a/bin/doctor +++ b/bin/doctor @@ -252,16 +252,10 @@ manifest_version() { } # newer_version a b — echoes whichever sorts higher on major.minor.patch. +# Lives in bin/newer-version because `task check` needs the same ordering (#34) +# and a second copy is a second thing to get wrong. newer_version() { - printf '%s\n%s\n' "$1" "$2" | awk -F. ' - { raw[NR] = $0; for (i = 1; i <= 3; i++) n[NR, i] = ($i == "" ? 0 : $i + 0) } - END { - for (i = 1; i <= 3; i++) { - if (n[1, i] > n[2, i]) { print raw[1]; exit } - if (n[1, i] < n[2, i]) { print raw[2]; exit } - } - print raw[1] - }' + sh "$SELF_DIR/newer-version" "$1" "$2" } installed_v='' diff --git a/bin/newer-version b/bin/newer-version new file mode 100755 index 0000000..147f18b --- /dev/null +++ b/bin/newer-version @@ -0,0 +1,26 @@ +#!/bin/sh +# newer-version A B — echo whichever of two dotted versions sorts higher. +# Equal versions echo A. Missing components count as zero, so 0.9 == 0.9.0. +# +# Exists because two callers need ordering rather than equality: +# - bin/doctor, comparing the installed plugin generation against the newest +# manifest on the host (#30); +# - task check, which must fail when plugin.json is BEHIND the latest tag and +# stay quiet when it is ahead — the normal state on a checkout that has not +# fetched the tag CI just pushed (#34). +# +# sort -V would do it in one line and is not portable enough for this fleet. + +set -u + +[ $# -eq 2 ] || { echo "usage: newer-version A B" >&2; exit 2; } + +printf '%s\n%s\n' "$1" "$2" | awk -F. ' + { raw[NR] = $0; for (i = 1; i <= 3; i++) n[NR, i] = ($i == "" ? 0 : $i + 0) } + END { + for (i = 1; i <= 3; i++) { + if (n[1, i] > n[2, i]) { print raw[1]; exit } + if (n[1, i] < n[2, i]) { print raw[2]; exit } + } + print raw[1] + }' diff --git a/tests/newer_version_test.sh b/tests/newer_version_test.sh new file mode 100644 index 0000000..d94cd89 --- /dev/null +++ b/tests/newer_version_test.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Tests for bin/newer-version. +# +# Extracted from bin/doctor (#30) once `task check` needed the same comparison +# (#34): check asserted plugin.json == latest tag, which is red on any checkout +# that has not fetched the tag the CI tagger just pushed. The assertion it +# actually wants is "not behind", and ordering needs a real comparison. + +set -u + +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +SUT="$ROOT/bin/newer-version" + +pass=0 +fail=0 + +if [ ! -f "$SUT" ]; then + printf 'FAIL: %s does not exist\n' "$SUT" + exit 1 +fi + +# expect