Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ versions:
./scripts/version-report.sh

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

toolchains:
@bash ./scripts/toolchain-report.sh
Expand Down Expand Up @@ -459,6 +458,7 @@ help:
@echo " versions-up Build both images with latest upstream agent versions"
@echo " ONLY=cctrace upgrades one tool, rest stay pinned"
@echo " versions-pin Refresh $(VERSION_PINS_FILE) from upstream"
@echo " CHANGELOG=1 also shows changelogs for updated tools"
@echo " scripts List repo helper scripts"
@echo " commands Alias for help"
@echo " test Test main image"
Expand Down Expand Up @@ -511,6 +511,7 @@ help:
@echo " make toolchains # Show pinned toolchain inventory"
@echo " make scripts # List helper scripts"
@echo " make versions-pin # Refresh shared pin file"
@echo " make versions-pin CHANGELOG=1 # Refresh pins + show changelogs"
@echo " make versions # Check current versions"
@echo " make PLAYWRIGHT_VERSION=1.60.0 build-rust # Override rust browser tooling"
@echo " make versions-up # Upgrade to latest upstream versions"
Expand Down
245 changes: 219 additions & 26 deletions scripts/update-version-pins.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
#!/usr/bin/env bash
# update-version-pins.sh - Refresh shared version pins from upstream sources
#
# Verbose TUI with real-time fetch progress, grouped version comparison,
# and optional changelog display for updated tools.

set -euo pipefail

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

DRY_RUN=0
SHOW_CHANGELOG=0
IS_TTY=0
[[ -t 1 ]] && IS_TTY=1

N_UPDATED=0
N_UNCHANGED=0
N_FAILED=0
UPDATED_VARS=()

usage() {
cat <<'EOF'
Usage: update-version-pins.sh [--dry-run]
Usage: update-version-pins.sh [OPTIONS]

Refresh shared version pins from upstream sources and rewrite versions.env.
Only versions we intentionally pin are updated here.

Options:
--dry-run Preview changes without writing versions.env
--changelog Show changelogs for updated tools
-h, --help Show this help
EOF
}

# ── Fetch helpers (soft-fail: empty string on error) ─────────────────────

fetch_npm_version() {
curl -fsSL --max-time 10 \
"https://registry.npmjs.org/-/package/$1/dist-tags" 2>/dev/null | \
Expand All @@ -43,13 +62,130 @@ fetch_latest_commit() {
git ls-remote "$1" "$2" 2>/dev/null | awk 'NR == 1 { print $1 }'
}

refresh_pin() {
local var_name=$1 fetched=$2
if [[ -n "$fetched" ]]; then
printf -v "$var_name" '%s' "$fetched"
# ── Pin: fetch one version and display result ────────────────────────────
# Usage: pin NAME VAR_NAME FETCH_TYPE [FETCH_ARGS...]

pin() {
local name=$1 var=$2 fetch_type=$3
shift 3
local old_val=${!var:-}
local pad
pad=$(printf '%-16s' "$name")

# Live progress (tty only -- overwritten when done)
if [[ $IS_TTY -eq 1 ]]; then
echo -en " ${CYAN}│${RESET} ${DIM}⟳ ${pad} fetching...${RESET}"
fi

# Fetch upstream. `|| true` is load-bearing: a failing command
# substitution in a plain assignment takes its exit status, and under
# `set -e` that aborts the whole run -- which would make the fetch-failed
# branch below unreachable and kill the soft-fail contract. One dead
# registry must degrade to a warning, not end the sweep.
local new_val=""
case $fetch_type in
go) new_val=$(fetch_go_version) || true ;;
npm) new_val=$(fetch_npm_version "$1") || true ;;
git-tag) new_val=$(fetch_latest_git_tag "$1") || true ;;
git-commit) new_val=$(fetch_latest_commit "$1" "$2") || true ;;
esac

# Clear fetching line
if [[ $IS_TTY -eq 1 ]]; then
echo -en "\r\033[K"
fi

# Fetch failed -- keep old value, warn
if [[ -z "$new_val" ]]; then
echo -e " ${CYAN}│${RESET} ${YELLOW}!${RESET} ${WHITE}${pad}${RESET} ${DIM}${old_val:-?}${RESET} ${YELLOW}(fetch failed)${RESET}"
N_FAILED=$((N_FAILED + 1))
return
fi

# Update variable in caller's scope
printf -v "$var" '%s' "$new_val"

# Short hash for commits, semver for everything else
local old_disp=$old_val new_disp=$new_val
if [[ $fetch_type == "git-commit" ]]; then
old_disp="${old_val:0:7}"
new_disp="${new_val:0:7}"
fi

if [[ "$old_val" == "$new_val" ]]; then
echo -e " ${CYAN}│${RESET} ${DIM}· ${pad} ${new_disp} (up-to-date)${RESET}"
N_UNCHANGED=$((N_UNCHANGED + 1))
else
echo -e " ${CYAN}│${RESET} ${GREEN}▲${RESET} ${WHITE}${pad}${RESET} ${RED}${old_disp:-new}${RESET} ${DIM}->${RESET} ${GREEN}${new_disp}${RESET}"
N_UPDATED=$((N_UPDATED + 1))
UPDATED_VARS+=("${var}|${old_val}|${new_val}")
fi
}

# ── Changelog display ────────────────────────────────────────────────────

registry_tool() {
case $1 in
CLAUDE_CODE_VERSION) echo "claude-code" ;;
CCTRACE_VERSION) echo "cctrace" ;;
CODEX_VERSION) echo "codex" ;;
GEMINI_CLI_VERSION) echo "gemini-cli" ;;
GROK_CLI_VERSION) echo "grok-cli" ;;
KIMI_CODE_VERSION) echo "kimi-code" ;;
CCX_VERSION) echo "ccx" ;;
COPILOT_API_VERSION) echo "copilot-api" ;;
PLAYWRIGHT_VERSION) echo "playwright" ;;
esac
}

show_changelogs() {
local shown=0
for entry in ${UPDATED_VARS[@]+"${UPDATED_VARS[@]}"}; do
IFS='|' read -r var old new <<< "$entry"

local tool
tool=$(registry_tool "$var")
[[ -z "$tool" ]] && continue

local changelog_source
changelog_source=$(get_tool_field "$tool" changelog 2>/dev/null) || true
[[ -z "$changelog_source" ]] && continue

local name
name=$(get_display_name "$tool")

[[ $shown -eq 0 ]] && echo ""
shown=1

section "$name ${old} -> ${new}"

if [[ $IS_TTY -eq 1 ]]; then
echo -en " ${DIM}fetching changelog...${RESET}"
fi

local changes
changes=$(fetch_changelog "$tool" "$old" "$new")

if [[ $IS_TTY -eq 1 ]]; then
echo -en "\r\033[K"
fi

if [[ -n "$changes" ]]; then
echo "$changes" | indent
else
echo -e " ${DIM}(changelog unavailable)${RESET}"
fi
echo ""
done

if [[ $shown -eq 0 ]]; then
echo -e "${DIM}No changelogs available for updated tools.${RESET}"
echo ""
fi
}

# ── Write versions.env ───────────────────────────────────────────────────

write_version_pins() {
cat > "$VERSION_PINS_FILE" <<EOF
# Shared image version pins for local and release builds.
Expand Down Expand Up @@ -82,16 +218,13 @@ RUST_TARGETS=$RUST_TARGETS
EOF
}

# ── Arg parsing ──────────────────────────────────────────────────────────

while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=1
shift
;;
-h|--help)
usage
exit 0
;;
--dry-run) DRY_RUN=1; shift ;;
--changelog) SHOW_CHANGELOG=1; shift ;;
-h|--help) usage; exit 0 ;;
*)
echo "error: unknown option: $1" >&2
usage >&2
Expand All @@ -100,28 +233,88 @@ while [[ $# -gt 0 ]]; do
esac
done

# ── Main ─────────────────────────────────────────────────────────────────

main() {
load_version_pins

refresh_pin GO_VERSION "$(fetch_go_version)"
refresh_pin DELTA_VERSION "$(fetch_latest_git_tag https://github.com/dandavison/delta.git)"
refresh_pin CLAUDE_CODE_VERSION "$(fetch_npm_version @anthropic-ai/claude-code)"
refresh_pin CCTRACE_VERSION "$(fetch_npm_version @thevibeworks/cctrace)"
refresh_pin CODEX_VERSION "$(fetch_npm_version @openai/codex)"
refresh_pin GEMINI_CLI_VERSION "$(fetch_npm_version @google/gemini-cli)"
refresh_pin GROK_CLI_VERSION "$(fetch_npm_version @xai-official/grok)"
refresh_pin KIMI_CODE_VERSION "$(fetch_npm_version @moonshot-ai/kimi-code)"
refresh_pin CCX_VERSION "$(fetch_latest_git_tag https://github.com/thevibeworks/ccx.git)"
refresh_pin COPILOT_API_VERSION "$(fetch_latest_commit https://github.com/ericc-ch/copilot-api.git refs/heads/master)"
refresh_pin PLAYWRIGHT_VERSION "$(fetch_npm_version playwright)"
refresh_pin CLOAKBROWSER_WRAPPER_VERSION "$(fetch_npm_version cloakbrowser)"
echo -e "${CYAN}${BOLD}╔══════════════════════════════════════════════════╗${RESET}"
echo -e "${CYAN}${BOLD}║ Refreshing Version Pins ║${RESET}"
echo -e "${CYAN}${BOLD}╚══════════════════════════════════════════════════╝${RESET}"
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S')${RESET}"
[[ $DRY_RUN -eq 1 ]] && echo -e "${YELLOW}(dry run)${RESET}"
echo ""

# ── Toolchains ───────────────────────────────────────────────────────
echo -e " ${CYAN}┌─${BOLD} Toolchains ${RESET}${CYAN}──────────────────────────────────────${RESET}"

pin "Go" GO_VERSION go
pin "delta" DELTA_VERSION git-tag "https://github.com/dandavison/delta.git"

# ── Agent CLIs ───────────────────────────────────────────────────────
echo -e " ${CYAN}│${RESET}"
echo -e " ${CYAN}├─${BOLD} Agent CLIs ${RESET}${CYAN}──────────────────────────────────────${RESET}"

pin "Claude Code" CLAUDE_CODE_VERSION npm "@anthropic-ai/claude-code"
pin "cctrace" CCTRACE_VERSION npm "@thevibeworks/cctrace"
pin "Codex" CODEX_VERSION npm "@openai/codex"
pin "Gemini CLI" GEMINI_CLI_VERSION npm "@google/gemini-cli"
pin "Grok CLI" GROK_CLI_VERSION npm "@xai-official/grok"
pin "Kimi Code" KIMI_CODE_VERSION npm "@moonshot-ai/kimi-code"
pin "CCX" CCX_VERSION git-tag "https://github.com/thevibeworks/ccx.git"
pin "Copilot API" COPILOT_API_VERSION git-commit "https://github.com/ericc-ch/copilot-api.git" "refs/heads/master"

# ── Browser Tools ────────────────────────────────────────────────────
echo -e " ${CYAN}│${RESET}"
echo -e " ${CYAN}├─${BOLD} Browser Tools ${RESET}${CYAN}───────────────────────────────────${RESET}"

pin "Playwright" PLAYWRIGHT_VERSION npm "playwright"
pin "CloakBrowser" CLOAKBROWSER_WRAPPER_VERSION npm "cloakbrowser"

# ── Summary footer ───────────────────────────────────────────────────
echo -e " ${CYAN}│${RESET}"

local parts=()
[[ $N_UPDATED -gt 0 ]] && parts+=("${GREEN}${N_UPDATED} updated${RESET}")
[[ $N_UNCHANGED -gt 0 ]] && parts+=("${DIM}${N_UNCHANGED} unchanged${RESET}")
[[ $N_FAILED -gt 0 ]] && parts+=("${YELLOW}${N_FAILED} failed${RESET}")
local summary=""
for i in "${!parts[@]}"; do
[[ $i -gt 0 ]] && summary+=", "
summary+="${parts[$i]}"
done
echo -e " ${CYAN}└─${RESET} ${summary} ${CYAN}──────────────────────────────────────${RESET}"
echo ""

# ── Changelogs (opt-in) ──────────────────────────────────────────────
if [[ $SHOW_CHANGELOG -eq 1 ]] && [[ $N_UPDATED -gt 0 ]]; then
show_changelogs
fi

# ── Result ───────────────────────────────────────────────────────────
if [[ $DRY_RUN -eq 1 ]]; then
echo -e "${YELLOW}Dry run — would write:${RESET}"
echo ""
emit_version_pins
return 0
fi

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

main "$@"
16 changes: 8 additions & 8 deletions versions.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ DELTA_VERSION=0.19.2
TMUX_VERSION=3.6a
TMUX_SHA256=b6d8d9c76585db8ef5fa00d4931902fa4b8cbe8166f528f44fc403961a3f3759

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

RUST_TOOLCHAINS=stable
RUST_DEFAULT_TOOLCHAIN=stable
Expand Down
Loading