blob: a7565235738f64331dff258dd796a82de03c0acc [file]
#!/bin/sh
# Bump the harness pins to the latest published versions: the npm harnesses in
# images/harness/package.json, with package-lock.json regenerated beside it, and pi and Node
# in images/harness/Dockerfile. Prints one line per change, and a line for each override in
# package.json that a bump made stale or that now holds a harness back; exits 0 whether or
# not anything changed. Needs npm and curl; without Node on the host, run it in node:24
# (images/harness/README.md). A registry or nodejs.org that does not answer is an error,
# never "nothing to bump".
set -eu
dir="$(cd "$(dirname "$0")/../images/harness" && pwd)"
df="$dir/Dockerfile"
command -v npm >/dev/null 2>&1 || {
echo "bump-harness-versions: needs npm; run it in node:24, as images/harness/README.md says" >&2
exit 1
}
latest() { # npm package
v=$(npm view "$1@latest" version) || { echo "bump-harness-versions: npm view $1 failed" >&2; exit 1; }
[ -n "$v" ] || { echo "bump-harness-versions: the registry gave no version for $1" >&2; exit 1; }
echo "$v"
}
bump() { # ARG name, npm package
cur=$(sed -n "s/^ARG $1=\(.*\)$/\1/p" "$df")
new=$(latest "$2") || exit 1
if [ "$new" != "$cur" ]; then
sed -i "s/^ARG $1=.*$/ARG $1=$new/" "$df"
echo "$1: $cur -> $new ($2)"
fi
}
moved=0
bump_npm() { # npm package, as package.json's dependencies name it
cur=$(node -p 'require(process.argv[1]).dependencies[process.argv[2]]' "$dir/package.json" "$1")
new=$(latest "$1") || exit 1
if [ "$new" != "$cur" ]; then
# package.json and the lock are written together, or neither is
(cd "$dir" && npm install --package-lock-only --save-exact --no-fund --no-audit "$1@$new" >/dev/null)
echo "$1: $cur -> $new"
moved=1
fi
}
bump_npm @anthropic-ai/claude-code
bump_npm @openai/codex
bump_npm @google/gemini-cli
bump_npm @github/copilot
bump_npm opencode-ai
bump_npm @qwen-code/qwen-code
bump_npm @xai-official/grok
if [ "$moved" = 1 ]; then
# An override holds a package under one harness at a version the harness does not ask for,
# and npm says nothing when it applies. Say when one is no longer needed, or holds back a
# version the harness now asks for.
node -e '
const [pj, lock] = process.argv.slice(1).map(f => require(f));
const parts = v => v.split(".").map(Number);
const older = (a, b) => { const x = parts(a), y = parts(b);
for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] < y[i]; return false; };
for (const [parent, held] of Object.entries(pj.overrides || {})) {
if (typeof held !== "object") continue;
const p = lock.packages["node_modules/" + parent] || {};
const asks = { ...p.dependencies, ...p.optionalDependencies, ...p.peerDependencies };
for (const [dep, v] of Object.entries(held)) {
const a = asks[dep];
if (a === undefined) console.log(`override: ${parent} no longer depends on ${dep}; remove the override`);
else if (!/^\d+\.\d+\.\d+$/.test(a)) console.log(`override: ${parent} now asks for ${dep} ${a}, and the override holds ${v}; check it`);
else if (!older(a, v)) console.log(`override: ${parent} now asks for ${dep} ${a}, and the override holds ${v}; remove the override`);
}
}' "$dir/package.json" "$dir/package-lock.json"
fi
bump PI @earendil-works/pi-coding-agent
# Node: the latest release on the LTS line the Dockerfile is on
cur=$(sed -n 's/^ARG NODE_VERSION=\(.*\)$/\1/p' "$df")
major=${cur%%.*}
index=$(curl -fsSL https://nodejs.org/dist/index.json) || {
echo "bump-harness-versions: nodejs.org's release index did not answer" >&2
exit 1
}
new=$(printf '%s' "$index" | tr -d '\n' | sed 's/},{/}\n{/g' | grep "\"version\":\"v$major\." | head -1 | sed 's/.*"version":"v\([^"]*\)".*/\1/' || true)
[ -n "$new" ] || { echo "bump-harness-versions: no Node $major release in nodejs.org's index" >&2; exit 1; }
if [ "$new" != "$cur" ]; then
sed -i "s/^ARG NODE_VERSION=.*$/ARG NODE_VERSION=$new/" "$df"
echo "NODE_VERSION: $cur -> $new"
fi