mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-02 17:40:02 +02:00
2.1.284 enables per-turn effort for claude-fable-5-1: in gate census 36626737820, 66 of 84 sessions ran longer than on 2.1.251 (+20% session time, +32% thinking tokens) and 11 cases timed out on unchanged budgets. HOLD SCOPE's 0G step asks its own defer/keep menu; the actor answered it Defer and the assessment then judged that scope question as the rigor decision. The actor now answers that menu Keep and assesses the next one.
166 lines
9.2 KiB
Docker
166 lines
9.2 KiB
Docker
# gstack CI eval runner — pre-baked toolchain + deps
|
|
# Rebuild weekly via ci-image.yml, on Dockerfile changes, or on lockfile changes
|
|
FROM ubuntu:24.04
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# Switch apt sources to Hetzner's public mirror.
|
|
# Ubicloud runners (Hetzner FSN1-DC21) hit reliable connection timeouts to
|
|
# archive.ubuntu.com:80 — observed 90+ second outages on multiple builds.
|
|
# Hetzner's mirror is publicly accessible from any cloud and route-local for
|
|
# Ubicloud, so this fixes both reliability and latency. Ubuntu 24.04 uses
|
|
# the deb822 sources format at /etc/apt/sources.list.d/ubuntu.sources.
|
|
#
|
|
# Using HTTP (not HTTPS) intentionally: the base ubuntu:24.04 image ships
|
|
# without ca-certificates, so HTTPS apt fails with "No system certificates
|
|
# available." Apt's security model verifies via GPG-signed Release files,
|
|
# not TLS, so HTTP here is no weaker than the upstream defaults.
|
|
RUN sed -i \
|
|
-e 's|http://archive.ubuntu.com/ubuntu|http://mirror.hetzner.com/ubuntu/packages|g' \
|
|
-e 's|http://security.ubuntu.com/ubuntu|http://mirror.hetzner.com/ubuntu/packages|g' \
|
|
/etc/apt/sources.list.d/ubuntu.sources
|
|
|
|
# Also make apt itself resilient — per-package retries + generous timeouts.
|
|
# Hetzner's mirror is reliable but individual packages can still blip; the
|
|
# retry config means a single failed fetch doesn't nuke the whole build.
|
|
RUN printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' \
|
|
> /etc/apt/apt.conf.d/80-retries
|
|
|
|
# System deps (retry apt-get update + install as a unit — even Hetzner can blip).
|
|
# Includes xz-utils so the Node.js .tar.xz download below can decompress.
|
|
# python3: bin/gstack-jsonl-merge, gstack-brain-sync, gstack-detach, and other
|
|
# bash bins shell out to it (macOS ships python3; the base image doesn't).
|
|
# file: skill-validation's no-compiled-binaries-in-git check runs `file --mime-type`.
|
|
# poppler-utils: make-pdf's e2e gates hard-require pdftotext/pdffonts/pdfinfo in CI.
|
|
RUN for i in 1 2 3; do \
|
|
apt-get update && apt-get install -y --no-install-recommends \
|
|
git curl unzip xz-utils ca-certificates jq bc gpg python3 python3-venv file poppler-utils gcc libc6-dev && break || \
|
|
(echo "apt retry $i/3 after failure"; sleep 10); \
|
|
done \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
RUN python3 -m venv /tmp/gstack-ci-venv \
|
|
&& /tmp/gstack-ci-venv/bin/python -m pip --version \
|
|
&& rm -rf /tmp/gstack-ci-venv
|
|
|
|
# Direct builds produce the trusted CSO launcher and watchdog. Check the exact
|
|
# static-C capability here so the cached eval image cannot reach a slice without it.
|
|
RUN printf 'int main(void) { return 0; }\n' > /tmp/gstack-cso-cc-probe.c \
|
|
&& cc -std=c11 -static /tmp/gstack-cso-cc-probe.c -o /tmp/gstack-cso-cc-probe \
|
|
&& /tmp/gstack-cso-cc-probe \
|
|
&& rm -f /tmp/gstack-cso-cc-probe.c /tmp/gstack-cso-cc-probe
|
|
|
|
# GitHub CLI
|
|
RUN curl --retry 5 --retry-delay 5 --retry-connrefused -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
| gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg \
|
|
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
| tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
|
&& for i in 1 2 3; do \
|
|
apt-get update && apt-get install -y --no-install-recommends gh && break || \
|
|
(echo "gh install retry $i/3"; sleep 10); \
|
|
done \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Node.js 22 LTS (needed for claude CLI).
|
|
# Install from the official nodejs.org tarball instead of NodeSource's apt setup.
|
|
# NodeSource's setup_22.x script runs its own `apt-get update` + `apt-get install gnupg`,
|
|
# both of which depend on archive.ubuntu.com / security.ubuntu.com being reachable.
|
|
# Ubicloud CI runners frequently can't reach those mirrors (connection timeouts),
|
|
# and "gnupg" was renamed to "gpg" on Ubuntu 24.04 anyway, so NodeSource's script
|
|
# fails before it can add its own repo. Direct tarball download is network-simpler
|
|
# (one host: nodejs.org) and doesn't touch apt at all.
|
|
ENV NODE_VERSION=22.20.0
|
|
RUN curl --retry 5 --retry-delay 5 --retry-connrefused -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" -o /tmp/node.tar.xz \
|
|
&& tar -xJ -C /usr/local --strip-components=1 --no-same-owner -f /tmp/node.tar.xz \
|
|
&& rm -f /tmp/node.tar.xz \
|
|
&& node --version \
|
|
&& npm --version
|
|
|
|
# Bun (install to /usr/local so non-root users can access it).
|
|
# The version MUST be passed as a positional arg — bun.sh/install ignores a
|
|
# BUN_VERSION env var, so the old `| BUN_VERSION=x.y.z bash` form silently
|
|
# installed latest on every image rebuild (observed: 1.3.13/1.3.14 drift vs
|
|
# the 1.3.10 devs ran locally).
|
|
ENV BUN_INSTALL="/usr/local"
|
|
RUN curl --retry 5 --retry-delay 5 --retry-connrefused -fsSL https://bun.sh/install \
|
|
| bash -s "bun-v1.4.0"
|
|
|
|
# Claude CLI — pinned to an EXACT version, same discipline as the bun pin
|
|
# above. The PTY harness (test/helpers/claude-pty-runner.ts) screen-scrapes
|
|
# this CLI's TUI (trust dialog, input prompt, spinner glyphs); an unpinned
|
|
# install rebuilt weekly rode the TUI wherever it drifted, and that drift
|
|
# broke the harness three separate times (welcome-screen wedge on 2.1.233,
|
|
# skillify HOME discovery on 2.1.237, guard/freeze hooks on 2.1.162).
|
|
# Bump deliberately, via a PR that runs the PTY gate against the new TUI.
|
|
# test/ci-image-cli-pin.test.ts fails the free suite if this pin is removed.
|
|
# Stays on 2.1.251. 2.1.284 (tried 2026-09-29) enables per-turn effort for
|
|
# claude-fable-5-1: in gate census run 36626737820, 66 of 84 sessions ran
|
|
# longer than the same cases in run 36606688266 on 2.1.251 (session time
|
|
# +20%, thinking tokens +32%), and 11 cases timed out on unchanged budgets.
|
|
# Bumping it needs its own budget and skill-speed work.
|
|
RUN npm i -g @anthropic-ai/claude-code@2.1.251
|
|
|
|
# Playwright system deps (Chromium) — needed for browse E2E tests
|
|
RUN npx playwright install-deps chromium
|
|
|
|
# Linux has neither Helvetica nor Arial. make-pdf's print CSS stacks fall back
|
|
# to Liberation Sans (metric-compatible Arial clone, SIL OFL 1.1) so PDFs don't
|
|
# render in DejaVu Sans. playwright install-deps happens to pull this in today,
|
|
# but the dep is implicit and could change — install explicitly so upgrades
|
|
# can't silently regress rendering.
|
|
#
|
|
# Xvfb is also installed here so the browse --headed integration tests
|
|
# (headed-xvfb, headed-orphan-cleanup) can exercise the Linux container
|
|
# auto-spawn path on every CI run. Without Xvfb in the image, the most
|
|
# common production --headed path goes untested.
|
|
# fonts-noto-color-emoji: the make-pdf emoji render gate needs a color-emoji
|
|
# fallback font (mirrors make-pdf-gate.yml's Ubuntu setup step).
|
|
RUN for i in 1 2 3; do \
|
|
apt-get update && apt-get install -y --no-install-recommends fonts-liberation fonts-noto-color-emoji fontconfig xvfb x11-utils && break || \
|
|
(echo "fonts-liberation install retry $i/3"; sleep 10); \
|
|
done \
|
|
&& fc-cache -f \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Pre-install dependencies (cached layer — only rebuilds when package.json or
|
|
# bun.lock changes). Copy BOTH so install is deterministic and matches local
|
|
# resolution. Without bun.lock here, bun install resolved transitive deps
|
|
# differently in CI vs local (observed on v1.28.0.0: socks landed but
|
|
# smart-buffer + ip-address didn't make it into the cached node_modules).
|
|
# patches/ rides along: bun.lock's patchedDependencies (playwright-core
|
|
# windowsHide, v1.67) makes install fail without the patch files present —
|
|
# and the workflows' image-tag hash includes patches/** so editing a patch
|
|
# rebuilds this layer.
|
|
COPY package.json bun.lock /workspace/
|
|
COPY patches /workspace/patches
|
|
WORKDIR /workspace
|
|
RUN bun install --frozen-lockfile && rm -rf /tmp/*
|
|
|
|
# Install Playwright Chromium to a shared location accessible by all users
|
|
ENV PLAYWRIGHT_BROWSERS_PATH=/opt/playwright-browsers
|
|
RUN npx playwright install chromium \
|
|
&& chmod -R a+rX /opt/playwright-browsers
|
|
|
|
# Verify everything works
|
|
RUN bun --version && node --version && claude --version && jq --version && gh --version \
|
|
&& python3 --version && command -v file && command -v pdftotext && command -v pdffonts && command -v pdfinfo \
|
|
&& npx playwright --version \
|
|
&& fc-match "Liberation Sans" | grep -qi "Liberation" \
|
|
|| (echo "ERROR: fonts-liberation not installed — make-pdf PDFs will render in DejaVu Sans" && exit 1)
|
|
|
|
# At runtime: checkout overwrites /workspace, but node_modules persists
|
|
# if we move it out of the way and symlink back
|
|
# Save node_modules + package.json snapshot for cache validation at runtime
|
|
RUN mv /workspace/node_modules /opt/node_modules_cache \
|
|
&& cp /workspace/package.json /opt/node_modules_cache/.package.json \
|
|
&& cp /workspace/bun.lock /opt/node_modules_cache/.bun.lock
|
|
|
|
# Claude CLI refuses --dangerously-skip-permissions as root.
|
|
# Create a non-root user for eval runs (GH Actions overrides USER, so
|
|
# the workflow must set options.user or use gosu/su-exec at runtime).
|
|
RUN useradd -m -s /bin/bash runner \
|
|
&& chmod -R a+rX /opt/node_modules_cache \
|
|
&& mkdir -p /home/runner/.gstack && chown -R runner:runner /home/runner/.gstack \
|
|
&& chmod 1777 /tmp \
|
|
&& mkdir -p /home/runner/.bun && chown -R runner:runner /home/runner/.bun
|