Skip to content

Commit 5586826

Browse files
committed
chore(versions): refresh pins and make versions-pin show its work
- pin sweep: claude-code 2.1.220, cctrace 0.23.0, gemini 0.52.0, grok 0.2.112, kimi 0.29.2, ccx v0.12.0, playwright 1.62.0, cloakbrowser wrapper 0.5.2 (which is also the Chromium pin) - update-version-pins.sh: grouped per-tool output with old -> new, fetch failures called out instead of silently keeping the old value, and a summary line - versions-pin CHANGELOG=1 prints upstream changelogs for the tools that actually moved, via the release-utils registry -- release notes stop being archaeology - keep the soft-fail contract: `new_val=$(fetch ...) || true`. A failing command substitution in a plain assignment takes its exit status and set -e kills the run, which had made the fetch-failed branch dead code and turned one dead registry into an aborted sweep - always write versions.env, even when nothing moved. The heredoc is a second copy of the file layout, so the rewrite is what proves no pin was dropped; skipping it left that guard unexercised on exactly the runs where every fetch failed - say "N fetches failed, upstream state unknown" instead of claiming "all pins up-to-date" when nothing was actually checked Verified: full round-trip byte-identical under a simulated total registry outage; tests/version-upgrade.sh green.
1 parent 68e9d91 commit 5586826

3 files changed

Lines changed: 230 additions & 36 deletions

File tree

Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@ versions:
294294
./scripts/version-report.sh
295295

296296
versions-pin:
297-
@bash ./scripts/update-version-pins.sh
298-
@echo "✅ Updated $(VERSION_PINS_FILE)"
297+
@./scripts/update-version-pins.sh $(if $(filter 1 yes true,$(CHANGELOG)),--changelog)
299298

300299
toolchains:
301300
@bash ./scripts/toolchain-report.sh
@@ -459,6 +458,7 @@ help:
459458
@echo " versions-up Build both images with latest upstream agent versions"
460459
@echo " ONLY=cctrace upgrades one tool, rest stay pinned"
461460
@echo " versions-pin Refresh $(VERSION_PINS_FILE) from upstream"
461+
@echo " CHANGELOG=1 also shows changelogs for updated tools"
462462
@echo " scripts List repo helper scripts"
463463
@echo " commands Alias for help"
464464
@echo " test Test main image"
@@ -511,6 +511,7 @@ help:
511511
@echo " make toolchains # Show pinned toolchain inventory"
512512
@echo " make scripts # List helper scripts"
513513
@echo " make versions-pin # Refresh shared pin file"
514+
@echo " make versions-pin CHANGELOG=1 # Refresh pins + show changelogs"
514515
@echo " make versions # Check current versions"
515516
@echo " make PLAYWRIGHT_VERSION=1.60.0 build-rust # Override rust browser tooling"
516517
@echo " make versions-up # Upgrade to latest upstream versions"

scripts/update-version-pins.sh

100644100755
Lines changed: 219 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
11
#!/usr/bin/env bash
22
# update-version-pins.sh - Refresh shared version pins from upstream sources
3+
#
4+
# Verbose TUI with real-time fetch progress, grouped version comparison,
5+
# and optional changelog display for updated tools.
36

47
set -euo pipefail
58

69
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
710
# shellcheck disable=SC1091
811
source "$SCRIPT_DIR/version-pins.sh"
12+
# shellcheck disable=SC1091
13+
source "$SCRIPT_DIR/release-utils.sh"
914

1015
DRY_RUN=0
16+
SHOW_CHANGELOG=0
17+
IS_TTY=0
18+
[[ -t 1 ]] && IS_TTY=1
19+
20+
N_UPDATED=0
21+
N_UNCHANGED=0
22+
N_FAILED=0
23+
UPDATED_VARS=()
1124

1225
usage() {
1326
cat <<'EOF'
14-
Usage: update-version-pins.sh [--dry-run]
27+
Usage: update-version-pins.sh [OPTIONS]
1528
1629
Refresh shared version pins from upstream sources and rewrite versions.env.
17-
Only versions we intentionally pin are updated here.
30+
31+
Options:
32+
--dry-run Preview changes without writing versions.env
33+
--changelog Show changelogs for updated tools
34+
-h, --help Show this help
1835
EOF
1936
}
2037

38+
# ── Fetch helpers (soft-fail: empty string on error) ─────────────────────
39+
2140
fetch_npm_version() {
2241
curl -fsSL --max-time 10 \
2342
"https://registry.npmjs.org/-/package/$1/dist-tags" 2>/dev/null | \
@@ -43,13 +62,130 @@ fetch_latest_commit() {
4362
git ls-remote "$1" "$2" 2>/dev/null | awk 'NR == 1 { print $1 }'
4463
}
4564

46-
refresh_pin() {
47-
local var_name=$1 fetched=$2
48-
if [[ -n "$fetched" ]]; then
49-
printf -v "$var_name" '%s' "$fetched"
65+
# ── Pin: fetch one version and display result ────────────────────────────
66+
# Usage: pin NAME VAR_NAME FETCH_TYPE [FETCH_ARGS...]
67+
68+
pin() {
69+
local name=$1 var=$2 fetch_type=$3
70+
shift 3
71+
local old_val=${!var:-}
72+
local pad
73+
pad=$(printf '%-16s' "$name")
74+
75+
# Live progress (tty only -- overwritten when done)
76+
if [[ $IS_TTY -eq 1 ]]; then
77+
echo -en " ${CYAN}${RESET} ${DIM}${pad} fetching...${RESET}"
78+
fi
79+
80+
# Fetch upstream. `|| true` is load-bearing: a failing command
81+
# substitution in a plain assignment takes its exit status, and under
82+
# `set -e` that aborts the whole run -- which would make the fetch-failed
83+
# branch below unreachable and kill the soft-fail contract. One dead
84+
# registry must degrade to a warning, not end the sweep.
85+
local new_val=""
86+
case $fetch_type in
87+
go) new_val=$(fetch_go_version) || true ;;
88+
npm) new_val=$(fetch_npm_version "$1") || true ;;
89+
git-tag) new_val=$(fetch_latest_git_tag "$1") || true ;;
90+
git-commit) new_val=$(fetch_latest_commit "$1" "$2") || true ;;
91+
esac
92+
93+
# Clear fetching line
94+
if [[ $IS_TTY -eq 1 ]]; then
95+
echo -en "\r\033[K"
96+
fi
97+
98+
# Fetch failed -- keep old value, warn
99+
if [[ -z "$new_val" ]]; then
100+
echo -e " ${CYAN}${RESET} ${YELLOW}!${RESET} ${WHITE}${pad}${RESET} ${DIM}${old_val:-?}${RESET} ${YELLOW}(fetch failed)${RESET}"
101+
N_FAILED=$((N_FAILED + 1))
102+
return
103+
fi
104+
105+
# Update variable in caller's scope
106+
printf -v "$var" '%s' "$new_val"
107+
108+
# Short hash for commits, semver for everything else
109+
local old_disp=$old_val new_disp=$new_val
110+
if [[ $fetch_type == "git-commit" ]]; then
111+
old_disp="${old_val:0:7}"
112+
new_disp="${new_val:0:7}"
113+
fi
114+
115+
if [[ "$old_val" == "$new_val" ]]; then
116+
echo -e " ${CYAN}${RESET} ${DIM}· ${pad} ${new_disp} (up-to-date)${RESET}"
117+
N_UNCHANGED=$((N_UNCHANGED + 1))
118+
else
119+
echo -e " ${CYAN}${RESET} ${GREEN}${RESET} ${WHITE}${pad}${RESET} ${RED}${old_disp:-new}${RESET} ${DIM}->${RESET} ${GREEN}${new_disp}${RESET}"
120+
N_UPDATED=$((N_UPDATED + 1))
121+
UPDATED_VARS+=("${var}|${old_val}|${new_val}")
122+
fi
123+
}
124+
125+
# ── Changelog display ────────────────────────────────────────────────────
126+
127+
registry_tool() {
128+
case $1 in
129+
CLAUDE_CODE_VERSION) echo "claude-code" ;;
130+
CCTRACE_VERSION) echo "cctrace" ;;
131+
CODEX_VERSION) echo "codex" ;;
132+
GEMINI_CLI_VERSION) echo "gemini-cli" ;;
133+
GROK_CLI_VERSION) echo "grok-cli" ;;
134+
KIMI_CODE_VERSION) echo "kimi-code" ;;
135+
CCX_VERSION) echo "ccx" ;;
136+
COPILOT_API_VERSION) echo "copilot-api" ;;
137+
PLAYWRIGHT_VERSION) echo "playwright" ;;
138+
esac
139+
}
140+
141+
show_changelogs() {
142+
local shown=0
143+
for entry in ${UPDATED_VARS[@]+"${UPDATED_VARS[@]}"}; do
144+
IFS='|' read -r var old new <<< "$entry"
145+
146+
local tool
147+
tool=$(registry_tool "$var")
148+
[[ -z "$tool" ]] && continue
149+
150+
local changelog_source
151+
changelog_source=$(get_tool_field "$tool" changelog 2>/dev/null) || true
152+
[[ -z "$changelog_source" ]] && continue
153+
154+
local name
155+
name=$(get_display_name "$tool")
156+
157+
[[ $shown -eq 0 ]] && echo ""
158+
shown=1
159+
160+
section "$name ${old} -> ${new}"
161+
162+
if [[ $IS_TTY -eq 1 ]]; then
163+
echo -en " ${DIM}fetching changelog...${RESET}"
164+
fi
165+
166+
local changes
167+
changes=$(fetch_changelog "$tool" "$old" "$new")
168+
169+
if [[ $IS_TTY -eq 1 ]]; then
170+
echo -en "\r\033[K"
171+
fi
172+
173+
if [[ -n "$changes" ]]; then
174+
echo "$changes" | indent
175+
else
176+
echo -e " ${DIM}(changelog unavailable)${RESET}"
177+
fi
178+
echo ""
179+
done
180+
181+
if [[ $shown -eq 0 ]]; then
182+
echo -e "${DIM}No changelogs available for updated tools.${RESET}"
183+
echo ""
50184
fi
51185
}
52186

187+
# ── Write versions.env ───────────────────────────────────────────────────
188+
53189
write_version_pins() {
54190
cat > "$VERSION_PINS_FILE" <<EOF
55191
# Shared image version pins for local and release builds.
@@ -82,16 +218,13 @@ RUST_TARGETS=$RUST_TARGETS
82218
EOF
83219
}
84220

221+
# ── Arg parsing ──────────────────────────────────────────────────────────
222+
85223
while [[ $# -gt 0 ]]; do
86224
case $1 in
87-
--dry-run)
88-
DRY_RUN=1
89-
shift
90-
;;
91-
-h|--help)
92-
usage
93-
exit 0
94-
;;
225+
--dry-run) DRY_RUN=1; shift ;;
226+
--changelog) SHOW_CHANGELOG=1; shift ;;
227+
-h|--help) usage; exit 0 ;;
95228
*)
96229
echo "error: unknown option: $1" >&2
97230
usage >&2
@@ -100,28 +233,88 @@ while [[ $# -gt 0 ]]; do
100233
esac
101234
done
102235

236+
# ── Main ─────────────────────────────────────────────────────────────────
237+
103238
main() {
104239
load_version_pins
105240

106-
refresh_pin GO_VERSION "$(fetch_go_version)"
107-
refresh_pin DELTA_VERSION "$(fetch_latest_git_tag https://github.com/dandavison/delta.git)"
108-
refresh_pin CLAUDE_CODE_VERSION "$(fetch_npm_version @anthropic-ai/claude-code)"
109-
refresh_pin CCTRACE_VERSION "$(fetch_npm_version @thevibeworks/cctrace)"
110-
refresh_pin CODEX_VERSION "$(fetch_npm_version @openai/codex)"
111-
refresh_pin GEMINI_CLI_VERSION "$(fetch_npm_version @google/gemini-cli)"
112-
refresh_pin GROK_CLI_VERSION "$(fetch_npm_version @xai-official/grok)"
113-
refresh_pin KIMI_CODE_VERSION "$(fetch_npm_version @moonshot-ai/kimi-code)"
114-
refresh_pin CCX_VERSION "$(fetch_latest_git_tag https://github.com/thevibeworks/ccx.git)"
115-
refresh_pin COPILOT_API_VERSION "$(fetch_latest_commit https://github.com/ericc-ch/copilot-api.git refs/heads/master)"
116-
refresh_pin PLAYWRIGHT_VERSION "$(fetch_npm_version playwright)"
117-
refresh_pin CLOAKBROWSER_WRAPPER_VERSION "$(fetch_npm_version cloakbrowser)"
241+
echo -e "${CYAN}${BOLD}╔══════════════════════════════════════════════════╗${RESET}"
242+
echo -e "${CYAN}${BOLD}║ Refreshing Version Pins ║${RESET}"
243+
echo -e "${CYAN}${BOLD}╚══════════════════════════════════════════════════╝${RESET}"
244+
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S')${RESET}"
245+
[[ $DRY_RUN -eq 1 ]] && echo -e "${YELLOW}(dry run)${RESET}"
246+
echo ""
247+
248+
# ── Toolchains ───────────────────────────────────────────────────────
249+
echo -e " ${CYAN}┌─${BOLD} Toolchains ${RESET}${CYAN}──────────────────────────────────────${RESET}"
250+
251+
pin "Go" GO_VERSION go
252+
pin "delta" DELTA_VERSION git-tag "https://github.com/dandavison/delta.git"
253+
254+
# ── Agent CLIs ───────────────────────────────────────────────────────
255+
echo -e " ${CYAN}${RESET}"
256+
echo -e " ${CYAN}├─${BOLD} Agent CLIs ${RESET}${CYAN}──────────────────────────────────────${RESET}"
257+
258+
pin "Claude Code" CLAUDE_CODE_VERSION npm "@anthropic-ai/claude-code"
259+
pin "cctrace" CCTRACE_VERSION npm "@thevibeworks/cctrace"
260+
pin "Codex" CODEX_VERSION npm "@openai/codex"
261+
pin "Gemini CLI" GEMINI_CLI_VERSION npm "@google/gemini-cli"
262+
pin "Grok CLI" GROK_CLI_VERSION npm "@xai-official/grok"
263+
pin "Kimi Code" KIMI_CODE_VERSION npm "@moonshot-ai/kimi-code"
264+
pin "CCX" CCX_VERSION git-tag "https://github.com/thevibeworks/ccx.git"
265+
pin "Copilot API" COPILOT_API_VERSION git-commit "https://github.com/ericc-ch/copilot-api.git" "refs/heads/master"
266+
267+
# ── Browser Tools ────────────────────────────────────────────────────
268+
echo -e " ${CYAN}${RESET}"
269+
echo -e " ${CYAN}├─${BOLD} Browser Tools ${RESET}${CYAN}───────────────────────────────────${RESET}"
270+
271+
pin "Playwright" PLAYWRIGHT_VERSION npm "playwright"
272+
pin "CloakBrowser" CLOAKBROWSER_WRAPPER_VERSION npm "cloakbrowser"
118273

274+
# ── Summary footer ───────────────────────────────────────────────────
275+
echo -e " ${CYAN}${RESET}"
276+
277+
local parts=()
278+
[[ $N_UPDATED -gt 0 ]] && parts+=("${GREEN}${N_UPDATED} updated${RESET}")
279+
[[ $N_UNCHANGED -gt 0 ]] && parts+=("${DIM}${N_UNCHANGED} unchanged${RESET}")
280+
[[ $N_FAILED -gt 0 ]] && parts+=("${YELLOW}${N_FAILED} failed${RESET}")
281+
local summary=""
282+
for i in "${!parts[@]}"; do
283+
[[ $i -gt 0 ]] && summary+=", "
284+
summary+="${parts[$i]}"
285+
done
286+
echo -e " ${CYAN}└─${RESET} ${summary} ${CYAN}──────────────────────────────────────${RESET}"
287+
echo ""
288+
289+
# ── Changelogs (opt-in) ──────────────────────────────────────────────
290+
if [[ $SHOW_CHANGELOG -eq 1 ]] && [[ $N_UPDATED -gt 0 ]]; then
291+
show_changelogs
292+
fi
293+
294+
# ── Result ───────────────────────────────────────────────────────────
119295
if [[ $DRY_RUN -eq 1 ]]; then
296+
echo -e "${YELLOW}Dry run — would write:${RESET}"
297+
echo ""
120298
emit_version_pins
121299
return 0
122300
fi
123301

302+
# Write even when nothing moved. The heredoc in write_version_pins is a
303+
# second copy of the file layout, so a rewrite is what proves no pin was
304+
# dropped from it; skipping the write when N_UPDATED=0 would leave that
305+
# guard (tests/version-upgrade.sh) unexercised on exactly the runs where
306+
# every fetch failed.
124307
write_version_pins
308+
if [[ $N_UPDATED -eq 0 ]]; then
309+
if [[ $N_FAILED -gt 0 ]]; then
310+
echo -e "${YELLOW}No pins moved, but ${N_FAILED} fetch(es) failed -- upstream state unknown for those.${RESET}"
311+
else
312+
echo -e "${GREEN}All pins up-to-date.${RESET}"
313+
fi
314+
return 0
315+
fi
316+
echo -e "${GREEN}Updated ${VERSION_PINS_FILE##*/}${RESET}"
317+
echo -e "${DIM}Run 'make versions-up' to rebuild images.${RESET}"
125318
}
126319

127320
main "$@"

versions.env

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ DELTA_VERSION=0.19.2
88
TMUX_VERSION=3.6a
99
TMUX_SHA256=b6d8d9c76585db8ef5fa00d4931902fa4b8cbe8166f528f44fc403961a3f3759
1010

11-
CLAUDE_CODE_VERSION=2.1.217
12-
CCTRACE_VERSION=0.19.0
11+
CLAUDE_CODE_VERSION=2.1.220
12+
CCTRACE_VERSION=0.23.0
1313
CODEX_VERSION=0.145.0
14-
GEMINI_CLI_VERSION=0.51.0
15-
GROK_CLI_VERSION=0.2.106
16-
KIMI_CODE_VERSION=0.28.1
17-
CCX_VERSION=v0.11.0
14+
GEMINI_CLI_VERSION=0.52.0
15+
GROK_CLI_VERSION=0.2.112
16+
KIMI_CODE_VERSION=0.29.2
17+
CCX_VERSION=v0.12.0
1818
COPILOT_API_VERSION=0ea08febdd7e3e055b03dd298bf57e669500b5c1
19-
PLAYWRIGHT_VERSION=1.61.1
19+
PLAYWRIGHT_VERSION=1.62.0
2020
# CloakBrowser npm wrapper version. This also pins the Chromium binary:
2121
# the wrapper hardcodes per-arch free-binary versions (linux-x64 146.x.x.5,
2222
# linux-arm64 146.x.x.3), so bumping the wrapper is what moves Chromium.
23-
CLOAKBROWSER_WRAPPER_VERSION=0.4.12
23+
CLOAKBROWSER_WRAPPER_VERSION=0.5.2
2424

2525
RUST_TOOLCHAINS=stable
2626
RUST_DEFAULT_TOOLCHAIN=stable

0 commit comments

Comments
 (0)