mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-09 20:46:13 +00:00
* chore(docker): pin FFmpeg 8.0.1 in the Docker images and host CI Install one pinned static BtbN build (n8.0.1-66, month-end release) in Dockerfile.test, the CLI render image, the Cloud Run image and the k8s example, and bump the host CI install-ffmpeg-linux action from 7.1.3 to the same build. Regression baselines and the shipped images now use the same FFmpeg version instead of Debian's apt ffmpeg 5.1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(producer): regenerate six regression baselines on FFmpeg 8.0.1 The Docker test image now renders with FFmpeg 8.0.1 instead of Debian's 5.1. Six fixtures fail against the 5.1 baselines; everything else passes unchanged, including vfr-screen-recording. - style-2-prod, style-10-prod: FFmpeg 8 honors the MP3 gapless header, so the probed SFX duration is 1.0s instead of 1.044898s. Regenerate output.mp4 and compiled.html. - vignelli-stacking: one checkpoint (8.06s) now shows source frame 201 of the 25fps a-roll, which is the right frame for that time; the old baseline showed frame 202. - missing-host-comp-id, variables-prod, speed-ramp: AAC encoder output differs enough to drop the RMS-envelope correlation on flat signals below 0.9. Sample correlation against the source stays at 0.9999. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cli): read the ffmpeg version from n-prefixed release builds Release-branch ffmpeg builds, including the BtbN build now pinned in CI, print "ffmpeg version n8.0.1-66-...". extractMajorVersion skipped the "n8" token and returned 0 (and 1 for n7.1.3), so the CLI telemetry smoke check failed. parseToolVersion fell back to the whole banner line. Accept an optional "n" before the version number in both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cli): keep the preflight comment share flat Fold the n-prefix note into the existing doc line instead of adding new comment lines, which the comment ratchet rejects. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(docker): pick the FFmpeg build per arch and fail on a bad binary Dockerfile.test, the k8s example and the Cloud Run image always extracted the amd64 archive on multiarch bases, and the piped version check passed even when the binary could not run. All four images now pick linux64 or linuxarm64 by TARGETARCH, fail on any other arch, and grep both ffmpeg and ffprobe for the pinned version. style-2-prod and style-10-prod compiled.html go back to the previous golden with only the SFX data-duration/data-end changed, instead of a full recompile that also picked up unrelated compiler output. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
191 lines
9.7 KiB
Docker
191 lines
9.7 KiB
Docker
# HyperFrames distributed render — Cloud Run image.
|
|
#
|
|
# One container image, three roles (plan / renderChunk / assemble). Cloud
|
|
# Workflows POSTs a JSON body with an `Action` field; `dist/server.js`
|
|
# dispatches to the matching `@hyperframes/producer/distributed` primitive.
|
|
#
|
|
# Build context is the REPOSITORY ROOT (not this package dir) because the
|
|
# package depends on the `@hyperframes/producer` workspace and renders with
|
|
# the same chrome-headless-shell + fonts + ffmpeg the production renderer
|
|
# uses. The example smoke script and `hyperframes cloudrun deploy` both
|
|
# build with the repo root as context:
|
|
#
|
|
# docker build -f packages/gcp-cloud-run/Dockerfile -t <image> .
|
|
#
|
|
# NOTE: this Dockerfile only builds from a full hyperframes monorepo checkout
|
|
# — it COPYs sibling workspace packages (core/engine/producer). It is NOT
|
|
# buildable from the published npm tarball alone; install the repo (or use
|
|
# `hyperframes cloudrun deploy`, which builds from your checkout via Cloud
|
|
# Build) to produce the image.
|
|
#
|
|
# Unlike the AWS Lambda adapter there is no ZIP-size ceiling and no runtime
|
|
# Chrome decompression step — the binary lives in the image at a fixed path.
|
|
|
|
FROM node:22.23.3-bookworm-slim AS beginframe-contract
|
|
|
|
# ── System dependencies (identical set to the producer's render image) ───────
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
curl \
|
|
unzip \
|
|
xz-utils \
|
|
libgbm1 \
|
|
libnss3 \
|
|
libatk-bridge2.0-0 \
|
|
libdrm2 \
|
|
libxcomposite1 \
|
|
libxdamage1 \
|
|
libxrandr2 \
|
|
libcups2 \
|
|
libasound2 \
|
|
libpangocairo-1.0-0 \
|
|
libxshmfence1 \
|
|
libgtk-3-0 \
|
|
fonts-liberation \
|
|
fonts-noto-color-emoji \
|
|
fonts-noto-cjk \
|
|
fonts-noto-core \
|
|
fonts-noto-extra \
|
|
fonts-noto-ui-core \
|
|
fonts-freefont-ttf \
|
|
fonts-dejavu-core \
|
|
fontconfig \
|
|
&& rm -rf /var/lib/apt/lists/* \
|
|
&& apt-get clean \
|
|
&& fc-cache -fv
|
|
|
|
# ── FFmpeg (pinned) ──────────────────────────────────────────────────────────
|
|
# One pinned static build (BtbN n8.0.1) instead of Debian's apt ffmpeg, so
|
|
# regression baselines and the shipped images use the same FFmpeg version.
|
|
# Bump the URL and sha256 together, and keep in sync with the other Dockerfiles
|
|
# and .github/actions/install-ffmpeg-linux/action.yml.
|
|
ARG TARGETARCH
|
|
ARG FFMPEG_RELEASE=https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-02-28-12-59
|
|
ARG FFMPEG_BUILD=ffmpeg-n8.0.1-66-g27b8d1a017
|
|
ARG FFMPEG_SHA256_AMD64=26d2ecd0cefba99f9d1ea92252a3a4f743a58df4c1c3cf9f8bbaaa40b1fca5b0
|
|
ARG FFMPEG_SHA256_ARM64=d72cc324cb65dd92a9ffe699cc95ccbacfef4def8d3ad9fe2b5164a6a71b9595
|
|
RUN case "$TARGETARCH" in \
|
|
amd64) FF_ARCH=linux64; FF_SHA256="$FFMPEG_SHA256_AMD64" ;; \
|
|
arm64) FF_ARCH=linuxarm64; FF_SHA256="$FFMPEG_SHA256_ARM64" ;; \
|
|
*) echo "unsupported TARGETARCH '$TARGETARCH'" >&2; exit 1 ;; \
|
|
esac \
|
|
&& curl -fsSL -o /tmp/ffmpeg.tar.xz "$FFMPEG_RELEASE/$FFMPEG_BUILD-$FF_ARCH-gpl-8.0.tar.xz" \
|
|
&& echo "$FF_SHA256 /tmp/ffmpeg.tar.xz" | sha256sum -c - \
|
|
&& tar -xJf /tmp/ffmpeg.tar.xz -C /usr/local/bin --strip-components=2 --wildcards '*/bin/ffmpeg' '*/bin/ffprobe' \
|
|
&& rm /tmp/ffmpeg.tar.xz \
|
|
&& ffmpeg -version | grep -F "ffmpeg version ${FFMPEG_BUILD#ffmpeg-}" \
|
|
&& ffprobe -version | grep -F "ffprobe version ${FFMPEG_BUILD#ffmpeg-}"
|
|
|
|
# ── chrome-headless-shell (deterministic BeginFrame capture) ─────────────────
|
|
# Pinned deliberately. The build-stage contract probe below launches this
|
|
# exact executable and requires a renderer-ready warm-up plus a PNG-returning
|
|
# HeadlessExperimental.beginFrame before the image can build.
|
|
RUN npx --yes @puppeteer/browsers install chrome-headless-shell@148.0.7778.167 \
|
|
--path /opt/puppeteer \
|
|
&& CHS="$(find /opt/puppeteer/chrome-headless-shell -name chrome-headless-shell -type f | head -n1)" \
|
|
&& mkdir -p /opt/chrome \
|
|
&& ln -s "$CHS" /opt/chrome/chrome-headless-shell \
|
|
&& /opt/chrome/chrome-headless-shell --version
|
|
|
|
# The chromium.ts resolver reads this first; pointing the engine straight at
|
|
# the symlinked binary avoids the runtime cache scan.
|
|
ENV HYPERFRAMES_CHROME_PATH=/opt/chrome/chrome-headless-shell
|
|
ENV PRODUCER_HEADLESS_SHELL_PATH=/opt/chrome/chrome-headless-shell
|
|
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
|
ENV CONTAINER=true
|
|
|
|
WORKDIR /app
|
|
|
|
# Install bun (the repo's package manager / build driver). Pin the version:
|
|
# the container runs `bun dist/server.js` and relies on bun's ESM `require`
|
|
# interop to load the producer bundle, so a future bun release changing that
|
|
# behaviour shouldn't silently break the next image rebuild. Bump deliberately.
|
|
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.4.2"
|
|
ENV PATH="/root/.bun/bin:$PATH"
|
|
|
|
# Install workspace dependencies. Copy manifests first for layer caching.
|
|
COPY package.json bun.lock ./
|
|
COPY packages/core/package.json packages/core/package.json
|
|
COPY packages/engine/package.json packages/engine/package.json
|
|
COPY packages/player/package.json packages/player/package.json
|
|
COPY packages/producer/package.json packages/producer/package.json
|
|
COPY packages/cli/package.json packages/cli/package.json
|
|
COPY packages/studio/package.json packages/studio/package.json
|
|
COPY packages/shader-transitions/package.json packages/shader-transitions/package.json
|
|
COPY packages/aws-lambda/package.json packages/aws-lambda/package.json
|
|
COPY packages/gcp-cloud-run/package.json packages/gcp-cloud-run/package.json
|
|
COPY packages/lint/package.json packages/lint/package.json
|
|
COPY packages/parsers/package.json packages/parsers/package.json
|
|
COPY packages/sdk/package.json packages/sdk/package.json
|
|
COPY packages/sdk-playground/package.json packages/sdk-playground/package.json
|
|
COPY packages/inspector/package.json packages/inspector/package.json
|
|
COPY packages/studio-server/package.json packages/studio-server/package.json
|
|
COPY scripts/package-subpaths.mjs scripts/package-subpaths.mjs
|
|
# A corrupt tarball extraction can print an error and then hang instead of
|
|
# exiting (seen in Dockerfile.test, run 35468046055). Bound each attempt and
|
|
# retry once, so a prod deploy build fails fast instead of hanging.
|
|
RUN timeout 300 bun install --frozen-lockfile \
|
|
|| timeout 300 bun install --frozen-lockfile
|
|
|
|
# Fail the image build closed if the packaged executable cannot perform the
|
|
# same BeginFrame operation the distributed renderer relies on. Generate the
|
|
# argument list with the production engine helper so a launcher-flag regression
|
|
# invalidates this layer and fails the contract instead of testing a duplicate
|
|
# hard-coded profile.
|
|
COPY packages/parsers/ packages/parsers/
|
|
COPY packages/engine/ packages/engine/
|
|
COPY packages/aws-lambda/scripts/probe-beginframe.ts packages/aws-lambda/scripts/probe-beginframe.ts
|
|
RUN bun -e 'import { buildChromeArgs } from "./packages/engine/src/services/browserManager.ts"; await Bun.write("/tmp/hf-gcp-chrome-args.json", JSON.stringify(buildChromeArgs({ width: 800, height: 600, captureMode: "beginframe", platform: "linux" }, { browserGpuMode: "software" })))' \
|
|
&& bun packages/aws-lambda/scripts/probe-beginframe.ts \
|
|
--executable-path /opt/chrome/chrome-headless-shell \
|
|
--launch-args-json /tmp/hf-gcp-chrome-args.json
|
|
|
|
# Reports whether this exact pinned binary gets a hardware WebGPU adapter. A software-only
|
|
# host reports "no usable adapter" without failing the build; its WebGPU compositions refuse
|
|
# to render at run time. A real script file, not an inline `bun -e` eval: the eval's
|
|
# synthetic `/app/[eval]` path can't resolve the workspace-hoisted `puppeteer-core`.
|
|
COPY packages/aws-lambda/scripts/probe-webgpu-adapter.ts packages/aws-lambda/scripts/probe-webgpu-adapter.ts
|
|
RUN bun packages/aws-lambda/scripts/probe-webgpu-adapter.ts \
|
|
--executable-path /opt/chrome/chrome-headless-shell
|
|
|
|
# CI targets `beginframe-contract` directly, so pin/probe changes do not need
|
|
# the full monorepo build merely to verify browser capability. The deployable
|
|
# image continues from the already-proven binary and installed workspaces.
|
|
FROM beginframe-contract AS runtime
|
|
|
|
# Copy source for the packages the render path needs.
|
|
COPY packages/core/ packages/core/
|
|
COPY packages/engine/ packages/engine/
|
|
COPY packages/producer/ packages/producer/
|
|
COPY packages/gcp-cloud-run/ packages/gcp-cloud-run/
|
|
COPY packages/lint/ packages/lint/
|
|
COPY packages/parsers/ packages/parsers/
|
|
COPY packages/sdk/ packages/sdk/
|
|
COPY packages/sdk-playground/ packages/sdk-playground/
|
|
COPY packages/studio-server/ packages/studio-server/
|
|
|
|
# Build workspace dependencies before the producer bundle. The published
|
|
# workspace packages are not prebuilt in a fresh clone, so producer's esbuild
|
|
# resolution otherwise fails on parsers/core compiler entrypoints. Core's build
|
|
# also generates the embedded font data, so glyph layout matches production.
|
|
RUN bun run --cwd packages/parsers build \
|
|
&& bun run --cwd packages/lint build \
|
|
&& bun run --cwd packages/studio-server build \
|
|
&& bun run --cwd packages/core build \
|
|
&& bun run --cwd packages/core build:hyperframes-runtime:modular \
|
|
&& bun run --cwd packages/sdk build \
|
|
&& bun run --cwd packages/sdk-playground build \
|
|
&& bun run --cwd packages/engine build \
|
|
&& bun run --cwd packages/producer build \
|
|
&& bun run --cwd packages/gcp-cloud-run build
|
|
|
|
# Cloud Run injects PORT (default 8080). The server reads it.
|
|
ENV PORT=8080
|
|
EXPOSE 8080
|
|
|
|
WORKDIR /app/packages/gcp-cloud-run
|
|
# Run under bun, not node. The repo is bun-native and `@hyperframes/producer`'s
|
|
# bundled `distributed.js` relies on a `require` being available at runtime
|
|
# (its esbuild interop shim); bun provides one in ESM, bare `node` does not.
|
|
CMD ["bun", "dist/server.js"]
|