#!/usr/bin/env bash # Create a derived task image that satisfies BenchFlow's official OpenCode # bootstrap checks without requiring each evaluation container to download Node. set -euo pipefail PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) DOCKERFILE="$PROJECT_ROOT/scripts/evaluate/Dockerfile.benchflow-opencode" NODE_VERSION=22.20.0 BASE_IMAGE="" OUTPUT_IMAGE="" usage() { cat <<'EOF' Usage: bash scripts/evaluate/prepare-benchflow-opencode-image.sh \ --base-image \ --tag The base image is never changed. The resulting image contains the exact Node runtime expected by BenchFlow plus opencode-ai@latest under /opt/benchflow. EOF } while [ "$#" -gt 0 ]; do case "$1" in --base-image) BASE_IMAGE=${2:?missing value for --base-image}; shift 2 ;; --tag) OUTPUT_IMAGE=${2:?missing value for --tag}; shift 2 ;; --node-version) NODE_VERSION=${2:?missing value for --node-version}; shift 2 ;; -h|--help) usage; exit 0 ;; *) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;; esac done [ -n "$BASE_IMAGE" ] || { printf '%s\n' '--base-image is required.' >&2; exit 2; } [ -n "$OUTPUT_IMAGE" ] || { printf '%s\n' '--tag is required.' >&2; exit 2; } command -v docker >/dev/null || { printf '%s\n' 'docker is required.' >&2; exit 127; } command -v curl >/dev/null || { printf '%s\n' 'curl is required.' >&2; exit 127; } [ -f "$DOCKERFILE" ] || { printf 'Missing Dockerfile: %s\n' "$DOCKERFILE" >&2; exit 1; } docker image inspect "$BASE_IMAGE" >/dev/null BUILD_CONTEXT=$(mktemp -d "${TMPDIR:-/tmp}/benchflow-opencode-image.XXXXXX") cleanup() { rm -rf -- "$BUILD_CONTEXT"; } trap cleanup EXIT NODE_ARCH=$(uname -m) case "$NODE_ARCH" in x86_64|amd64) NODE_ARCH=x64 ;; aarch64|arm64) NODE_ARCH=arm64 ;; *) printf 'Unsupported architecture: %s\n' "$NODE_ARCH" >&2; exit 2 ;; esac NODE_ARCHIVE="node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" printf 'Downloading Node.js %s for the BenchFlow runtime cache...\n' "$NODE_VERSION" curl --fail --location --retry 3 --output "$BUILD_CONTEXT/node.tar.xz" \ "https://nodejs.org/dist/v${NODE_VERSION}/${NODE_ARCHIVE}" mkdir -p "$BUILD_CONTEXT/node" tar -xJf "$BUILD_CONTEXT/node.tar.xz" -C "$BUILD_CONTEXT/node" --strip-components=1 --no-same-owner printf '%s\n' 'Installing the official opencode-ai package into the runtime cache...' "$BUILD_CONTEXT/node/bin/npm" install --global --prefix "$BUILD_CONTEXT/js-agents" opencode-ai@latest [ -x "$BUILD_CONTEXT/js-agents/bin/opencode" ] || { printf '%s\n' 'opencode-ai installation did not create its executable.' >&2; exit 1; } printf 'Building derived image: %s\n' "$OUTPUT_IMAGE" docker build --build-arg "BASE_IMAGE=$BASE_IMAGE" --tag "$OUTPUT_IMAGE" --file "$DOCKERFILE" "$BUILD_CONTEXT" printf 'Ready: %s\n' "$OUTPUT_IMAGE"