mirror of
https://github.com/hanzoai/chat.git
synced 2026-09-30 20:51:17 +00:00
The release ended RED on a build that had already shipped. `deploy.yml` pushed
the image, tagged it, pinned universe — and then failed on
`git push origin HEAD:main`, because the forge refuses an Actions-token push to
a protected branch. Its five-attempt loop was written for a race ("main moved
under us") and was retrying a refusal it could never win.
The write-back kept a COPY in sync, and the copy has no authority: imgver takes
`max(declared, published) + 1`, so once an image is published the registry
carries the floor and package.json can only restate it. What the file does buy
— a deliberate jump to, say, 1.1.0 — is a human editing it, which max() still
honours. This repo's own comment already said a stale floor "breaks nothing. It
just loses."
So the commit half is gone and the TAG half stays: `git push origin v${v}`
succeeds (v1.0.150 is on the forge), and the pre-build stamp stays too, so the
tree that gets tagged is byte-identical to the tree that was built.
hanzo.yml's `version-drift` gate goes with it, and NOT as collateral — the two
are one mechanism. That gate's stated job was "to prove the write-back landed",
so keeping it after removing the writer would have been a gate that goes red on
every push and PR, forever, about a number nothing can fix. `scripts/version.sh`
loses `check` and its two helpers for the same reason; `published` remains and
still answers 1.0.150.
Same resolution hanzo-apps/app reached for the identical pairing, so the fleet
now has one answer to it rather than two.
79 lines
3.7 KiB
Bash
Executable File
79 lines
3.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# The version of ghcr.io/hanzoai/chat — is the declared one still true?
|
|
#
|
|
# scripts/version.sh published # the highest version already shipped
|
|
#
|
|
# `check` is GONE with the write-back it existed to police. It compared the
|
|
# declared number against the published one and failed when declared was behind
|
|
# — which is the PERMANENT state now that deploy.yml no longer commits to main
|
|
# (the forge refuses an Actions-token push to a protected branch). imgver takes
|
|
# `max(declared, published) + 1`, so a lagging declaration loses to the registry
|
|
# and costs nothing; the gate would only have gone red forever about a copy with
|
|
# no authority.
|
|
#
|
|
# THIS SCRIPT DOES NOT COMPUTE THE NEXT NUMBER, and that is deliberate.
|
|
# hanzoai/ci's `imgver` does, it is the fleet's ONE implementation for container
|
|
# repos, and .hanzo/workflows/deploy.yml calls it. A second computation here
|
|
# would be a second answer. What was missing was never a computation — it was a
|
|
# WRITER and a CHECK.
|
|
#
|
|
# THE DEFECT, MEASURED 2026-08-06: package.json said 1.0.77 while
|
|
# ghcr.io/hanzoai/chat was on 1.0.113 and universe pinned it. 36 patches.
|
|
# imgver takes max(declared, published) + 1, so `declared` is a floor — and a
|
|
# floor nothing ever writes back sinks one patch per release, forever. deploy.yml
|
|
# now commits the number in the same commit it tags; this proves it stuck.
|
|
#
|
|
# THE INVARIANT, one sentence: the declared version must never be BEHIND the
|
|
# highest published version. Equal is the steady state (CI writes it); AHEAD is
|
|
# a human raising the series on purpose, which is imgver's documented affordance
|
|
# ("the human bumped it — publish exactly that") and must keep working. So the
|
|
# test is `>=`, never `==`.
|
|
#
|
|
# NO LINE PIN, unlike hanzoai/app's copy. That repo pins `1.42.` because orphan
|
|
# tags (v2.2.1, v1.75.0) sit ABOVE its deploy line and a plain `sort -V` lets
|
|
# them hijack the floor. Here all 126 off-line tags are 0.9.x — strictly BELOW —
|
|
# so nothing can hijack anything, and an unconstrained max is both correct and
|
|
# identical to what imgver itself does. Pinning a line here would instead BREAK
|
|
# the legitimate 1.1.0 series bump imgver exists to allow.
|
|
#
|
|
# ENV: GH_PAT (or GITHUB_TOKEN) to read the registry floor — the same read
|
|
# imgver does. Without one the scan degrades to git tags alone and SAYS so.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
semver='^[0-9]+\.[0-9]+\.[0-9]+$'
|
|
|
|
git_max() {
|
|
# Folded in so the check still has an answer on a runner with no token. Safe
|
|
# in the union direction: deploy.yml creates the tag only AFTER the image is
|
|
# verified pullable, so a git tag can never name a version the registry lacks.
|
|
git tag -l | sed 's/^v//' | grep -E "$semver" | sort -V | tail -1 || true
|
|
}
|
|
|
|
cont_max() {
|
|
# The GitHub Packages API, not the registry v2 tags list: an anonymous ghcr
|
|
# pull token can fetch a manifest by name but returns an EMPTY tag list, so a
|
|
# v2 read would silently report "nothing published". Same call, same package,
|
|
# same parse as imgver — one definition of "published", two readers.
|
|
local tok="${GH_PAT:-${GITHUB_TOKEN:-}}"
|
|
[ -n "$tok" ] || { echo "version.sh: no GH_PAT/GITHUB_TOKEN — registry floor NOT read" >&2; return 0; }
|
|
curl -fsSL -H "Authorization: Bearer $tok" \
|
|
-H "Accept: application/vnd.github+json" \
|
|
'https://api.github.com/orgs/hanzoai/packages/container/chat/versions?per_page=100' 2>/dev/null \
|
|
| jq -r '.[].metadata.container.tags[]?' 2>/dev/null \
|
|
| sed 's/^v//' | grep -E "$semver" | sort -V | tail -1 || true
|
|
}
|
|
|
|
published() {
|
|
printf '%s\n%s\n%s\n' "0.0.0" "$(git_max)" "$(cont_max)" \
|
|
| grep -E "$semver" | sort -V | tail -1
|
|
}
|
|
|
|
|
|
case "${1:-published}" in
|
|
published) published ;;
|
|
|
|
*) echo "usage: scripts/version.sh [published]" >&2; exit 2 ;;
|
|
esac
|