ci: the eval image installs zsh, and the macOS lane sources the Codex probe from zsh

Dockerfile.ci adds zsh so the sourced-helper guard runs (and fails loudly
when zsh is missing) inside the eval container too. The macOS named
regressions job sources bin/gstack-codex-probe from zsh in an unrelated
directory and runs _gstack_codex_select_model exec (no paid call), then runs
the sourced-helper guard on the macOS runner. A free test pins both lanes and
runs the macOS step's script locally.
This commit is contained in:
garrytan committed 2026-10-06 20:43:44 +00:00
1 parent 3542322819
commit fa79a3071e
3 files changed
+32 -3

No files matched your search

+3 -1
View File
@@ -33,9 +33,11 @@ RUN printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https:
# 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.
# zsh: the sourced-helper guard sources every skill helper under bash and zsh
# (macOS's default shell) and fails under CI=true when zsh is missing.
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 || \
git curl unzip xz-utils ca-certificates jq bc gpg python3 python3-venv file poppler-utils gcc libc6-dev zsh && break || \
(echo "apt retry $i/3 after failure"; sleep 10); \
done \
&& rm -rf /var/lib/apt/lists/*
+10 -2
View File
@@ -102,7 +102,8 @@ jobs:
# $TMPDIR sits under /var/folders and /var is a symlink to /private/var, so
# code that compares a realpath with an unresolved path passes on Linux's
# /tmp and fails here (#2982). TMPDIR is deliberately left at the runner's
# default, and the job fails if that layout is not symlinked.
# default, and the job fails if that layout is not symlinked. It also sources
# the Codex probe from zsh, macOS's default shell (#3024; no paid call).
macos-named-regressions:
runs-on: macos-latest
timeout-minutes: 20
@@ -124,6 +125,13 @@ jobs:
git config --global user.email "free-tests-ci@gstack.test"
git config --global user.name "Free Tests CI"
git config --global init.defaultBranch main
- name: Codex probe selects a model when sourced from zsh (macOS default shell, no paid call)
working-directory: ${{ runner.temp }}
run: |
set -euo pipefail
out="$(zsh -c 'source "$GITHUB_WORKSPACE/bin/gstack-codex-probe" && _gstack_codex_select_model exec' 2>&1)"
printf '%s\n' "$out"
printf '%s\n' "$out" | grep -E '^CODEX_MODEL: [A-Za-z0-9._:/-]+ \(exec; source: '
- name: Run the named macOS regressions on the default temp root
run: |
set -euo pipefail
@@ -131,7 +139,7 @@ jobs:
resolved="$(realpath "$tmp")"
echo "temp root: $tmp -> $resolved"
test "$resolved" != "${tmp%/}"
files=(test/gstack-design-detect.test.ts browse/test/chromium-profile-isolation.test.ts test/cso-git-hardening.test.ts test/plan-count-file-permission.test.ts test/spec-quality-gate-secret-sink.test.ts test/gstack-memory-ingest.test.ts)
files=(test/gstack-design-detect.test.ts browse/test/chromium-profile-isolation.test.ts test/cso-git-hardening.test.ts test/plan-count-file-permission.test.ts test/spec-quality-gate-secret-sink.test.ts test/gstack-memory-ingest.test.ts test/regression-issue3024-zsh-sourced-helpers.test.ts)
for file in "${files[@]}"; do test -f "$file"; done
bun test "${files[@]}"
@@ -285,4 +285,23 @@ describe('#3024: sourced helpers work under bash and zsh', () => {
fs.rmSync(empty, { recursive: true, force: true });
}
});
test('CI keeps zsh: the free lane and the eval image install it, and the macOS lane sources the probe from zsh', () => {
const workflow = Bun.YAML.parse(fs.readFileSync(path.join(ROOT, '.github/workflows/free-tests.yml'), 'utf8')) as any;
const runs = (job: string) => (workflow.jobs[job].steps as any[]).map(step => String(step.run ?? '')).join('\n');
expect(runs('free-suite')).toMatch(/apt-get install[^\n]*\bzsh\b/);
expect(fs.readFileSync(path.join(ROOT, '.github/docker/Dockerfile.ci'), 'utf8')).toMatch(/apt-get install -y --no-install-recommends \\\n[^\n]*\bzsh\b/);
const mac = workflow.jobs['macos-named-regressions'];
expect(mac['runs-on']).toMatch(/^macos-/);
const step = (mac.steps as any[]).find(s => String(s.run ?? '').includes('_gstack_codex_select_model exec'));
expect(step?.run).toContain(`zsh -c 'source "$GITHUB_WORKSPACE/bin/gstack-codex-probe" && _gstack_codex_select_model exec'`);
expect(runs('macos-named-regressions')).toContain('test/regression-issue3024-zsh-sourced-helpers.test.ts');
if (skipShell('zsh')) return;
const codexHome = fs.mkdtempSync(path.join(os.tmpdir(), 'zsh-helpers-ci-step-'));
try {
const r = run('bash', step.run, { GITHUB_WORKSPACE: ROOT, CODEX_HOME: codexHome });
expect(r.code).toBe(0);
expect(r.stdout).toMatch(/CODEX_MODEL: \S+ \(exec; source: /);
} finally { fs.rmSync(codexHome, { recursive: true, force: true }); }
});
});