From a4a51d96cef46b1f43df39c2102b1fda8e604ec4 Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 31 Oct 2026 18:26:36 +0000 Subject: [PATCH] fix(ci): ship ffmpeg in the eval image so the marathon design-motion case can run (#3687) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- .github/docker/Dockerfile.ci | 15 +++++++++++++++ .../garrytan-gsta-211-marathon-ffmpeg.md | 5 +++++ test/ci-image-cli-pin.test.ts | 19 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 changelog.d/garrytan-gsta-211-marathon-ffmpeg.md diff --git a/.github/docker/Dockerfile.ci b/.github/docker/Dockerfile.ci index efe5aca83..44b4974a8 100644 --- a/.github/docker/Dockerfile.ci +++ b/.github/docker/Dockerfile.ci @@ -178,6 +178,21 @@ RUN for i in 1 2 3; do \ && fc-cache -f \ && rm -rf /var/lib/apt/lists/* +# ffmpeg (with ffprobe): /design-motion's checks and exports shell out to it +# (lib/motion-encode.ts), and the marathon design-motion-showcase case runs +# that flow end to end. The image had none, so every trial stopped at +# FFMPEG_MISSING (#3687). The build fails if the packaged ffmpeg ever lacks a +# filter or encoder the checks and exports use. +RUN for i in 1 2 3; do \ + apt-get update && apt-get install -y --no-install-recommends ffmpeg && break || \ + (echo "ffmpeg install retry $i/3"; sleep 10); \ + done \ + && rm -rf /var/lib/apt/lists/* \ + && command -v ffprobe \ + && filters="$(ffmpeg -hide_banner -filters)" && encoders="$(ffmpeg -hide_banner -encoders)" \ + && for f in psnr gblur palettegen paletteuse signalstats tile; do echo "$filters" | grep -qw "$f" || { echo "ERROR: ffmpeg lacks the $f filter" >&2; exit 1; }; done \ + && for e in libx264 gif; do echo "$encoders" | grep -qw "$e" || { echo "ERROR: ffmpeg lacks the $e encoder" >&2; exit 1; }; done + # 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 diff --git a/changelog.d/garrytan-gsta-211-marathon-ffmpeg.md b/changelog.d/garrytan-gsta-211-marathon-ffmpeg.md new file mode 100644 index 000000000..3eaec5956 --- /dev/null +++ b/changelog.d/garrytan-gsta-211-marathon-ffmpeg.md @@ -0,0 +1,5 @@ +**The weekly marathon evals can run /design-motion again: the CI eval image now ships ffmpeg (#3687).** The `design-motion-showcase` case joined the marathon tier on 2026-10-26, and its first scheduled run (Actions run 41200316994, main @ 088bfc5) failed 0/3. Every trial built and captured the page, then stopped at `FFMPEG_MISSING`, because the image had no ffmpeg and the skill rightly refuses to export before its checks run. The model did nothing wrong; the case could not pass on that image. + +### Fixed +- **`.github/docker/Dockerfile.ci`** installs Ubuntu's `ffmpeg` (6.1.1, with `ffprobe`). The build fails if that ffmpeg lacks a filter or encoder the motion checks and exports use (`psnr`, `gblur`, `palettegen`, `paletteuse`, `signalstats`, `tile`, `libx264`, `gif`). +- **`test/ci-image-cli-pin.test.ts`** fails the free suite if the image stops installing ffmpeg or stops checking it. diff --git a/test/ci-image-cli-pin.test.ts b/test/ci-image-cli-pin.test.ts index c4a84f21a..1a94c107c 100644 --- a/test/ci-image-cli-pin.test.ts +++ b/test/ci-image-cli-pin.test.ts @@ -256,3 +256,22 @@ describe('CI Node.js installs match the image and are verified (#3354)', () => { expect(unpack).toBeGreaterThan(verify); }); }); + +// Value: protects=the eval image ships an ffmpeg that runs /design-motion's checks and exports; +// fails_when=Dockerfile.ci stops installing ffmpeg or stops failing the build on a missing filter or encoder; +// why_new=the marathon design-motion-showcase case ran 0/3 on an image without ffmpeg (#3687); seam=none; +// no_claim=reads the Dockerfile only; the image build runs the check itself +describe('ci image ffmpeg for the design-motion evals (#3687)', () => { + const lines = logicalLines(fs.readFileSync(DOCKERFILE, 'utf-8')).filter((line) => !/^\s*#/.test(line)); + + test('apt installs ffmpeg, which the marathon design-motion-showcase case needs', () => { + expect(lines.some((line) => /apt-get install\b[^&|;]*\sffmpeg\b/.test(line))).toBe(true); + }); + + test('the build fails when that ffmpeg cannot run the motion checks and exports', () => { + const verify = lines.find((line) => /\bffmpeg\b.*-filters/.test(line)) ?? ''; + for (const need of ['ffprobe', 'psnr', 'gblur', 'palettegen', 'paletteuse', 'signalstats', 'tile', 'libx264', 'gif']) { + expect(verify, `Dockerfile.ci must check ffmpeg for ${need}`).toContain(need); + } + }); +});