build: Dockerfile for a reproducible libghostty + Qt frontend build

Multi-stage:

  base       - debian trixie + Qt 6.8 + LayerShellQt + xkbcommon /
               wayland / xcb dev packages
  zig        - Zig 0.15.2 (the project's minimum_zig_version) pinned;
               separate stage so apt churn doesn't bust the toolchain
               layer
  libghostty - zig build test (Debug) then -Doptimize=ReleaseFast,
               with mounted caches for /root/.cache/zig and
               /src/.zig-cache
  qt         - cmake -G Ninja then cmake --install /out
  out        - debian-slim carrying just /out (bin, lib, share/...)

Tests run inside the libghostty stage so a regression there fails the
build cleanly rather than wasting time on the Qt stage.

The image is build-only — runtime needs Qt + Wayland sockets the
container doesn't have. Extract the artifacts with:

    mkdir -p out
    docker run --rm -v "$PWD/out:/host-out" ghastty \
      sh -c 'cp -a /out/. /host-out/'

.dockerignore drops zig-cache, build dirs, agent state and the usual
git/IDE noise so the COPY context stays small.

Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
ntomsic 2026-05-20 14:24:08 -05:00
parent edc00a3eaa
commit 34ac8dae11
2 changed files with 195 additions and 0 deletions

58
.dockerignore Normal file
View File

@ -0,0 +1,58 @@
# Keep the build context small. Anything Docker doesn't need to see
# during `docker build` belongs here — the image's libghostty stage
# COPYs the entire repo, so untracked dev cruft would otherwise bloat
# the layer and bust the build cache on every run.
# Version control + IDE/editor metadata.
.git
.gitignore
.gitattributes
.github
.vscode
.direnv
.envrc
.envrc.local
# Build outputs (we run zig/cmake fresh inside the container).
zig-cache
.zig-cache
zig-out
build
build-cmake
qt/build
CMakeCache.txt
CMakeFiles
*.o
*.so
*.dylib
# Test artefacts and crash dumps.
test/ghostty
test/cases/**/*.actual.png
vgcore.*
/Box_test.ppm
/Box_test_diff.ppm
/ghostty.qcow2
# Flatpak/Nix scratch — not needed in the image.
.flatpak-builder
flatpak/builddir
flatpak/repo
result
result-*
# Per-user agent state from this repo's session, not part of the build.
.claude
.claude-flow
.hive-mind
.mcp.json
.swarm
CLAUDE.md
MEMORY.md
# OS noise.
.DS_Store
*~
*.swp
.*.swp
*.log

137
Dockerfile Normal file
View File

@ -0,0 +1,137 @@
# syntax=docker/dockerfile:1.7
#
# Reproducible build for libghostty + the Qt frontend (ghastty).
#
# Usage:
# docker build -t ghastty .
# docker run --rm -v "$PWD/out:/host-out" ghastty \
# sh -c 'cp -a /out/. /host-out/'
#
# The runtime container does not ship a usable terminal — the Qt
# frontend wants a Wayland/X11 socket from the host. This image is for
# building (and CI testing) only.
#
# Stage layout:
# - base : Debian trixie + the Qt/Wayland deps both stages need
# - zig : pinned Zig toolchain (kept separate so a deps-only
# rebuild doesn't re-fetch Zig)
# - libghostty : zig build of libghostty (-Dapp-runtime=none) + tests
# - qt : cmake build of qt/ against the libghostty artifact
# - out : minimal final stage holding only the built binaries
ARG DEBIAN_VERSION=trixie
# Pinned to the project's minimum_zig_version (build.zig.zon).
ARG ZIG_VERSION=0.15.2
# ---------------------------------------------------------------------
# base — system packages shared across the build stages.
# ---------------------------------------------------------------------
FROM debian:${DEBIAN_VERSION}-slim AS base
ENV DEBIAN_FRONTEND=noninteractive
# Single apt layer so the package cache is dropped before the next
# stage. The list mixes:
# - build tooling (cmake, ninja, pkg-config, gcc, libstdc++-dev)
# - libghostty build deps via Zig (most are vendored; libxml2-dev is
# pulled in by the Sentry/breakpad path on Linux)
# - Qt 6 modules the frontend uses (Gui, Widgets, OpenGL, DBus,
# Multimedia, Svg) plus LayerShellQt
# - native-protocol deps the frontend hits directly (xkbcommon,
# wayland-client, wayland-scanner, xcb)
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
xz-utils \
git \
pkg-config \
cmake \
ninja-build \
build-essential \
libstdc++-14-dev \
qt6-base-dev \
qt6-base-private-dev \
qt6-multimedia-dev \
qt6-svg-dev \
liblayershellqtinterface-dev \
libxkbcommon-dev \
libwayland-dev \
wayland-protocols \
libxcb1-dev \
libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
# ---------------------------------------------------------------------
# zig — fetch and unpack the pinned Zig toolchain.
#
# Kept separate from `base` so changing apt deps does not invalidate
# the (large) Zig download layer.
# ---------------------------------------------------------------------
FROM base AS zig
ARG ZIG_VERSION
RUN set -eux; \
arch="$(dpkg --print-architecture)"; \
case "$arch" in \
amd64) zig_arch=x86_64 ;; \
arm64) zig_arch=aarch64 ;; \
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
esac; \
tarball="zig-${zig_arch}-linux-${ZIG_VERSION}.tar.xz"; \
curl -fsSL -o "/tmp/${tarball}" \
"https://ziglang.org/download/${ZIG_VERSION}/${tarball}"; \
mkdir -p /opt/zig; \
tar -xJf "/tmp/${tarball}" -C /opt/zig --strip-components=1; \
rm "/tmp/${tarball}"; \
ln -s /opt/zig/zig /usr/local/bin/zig; \
zig version
# ---------------------------------------------------------------------
# libghostty — Zig build of the libghostty shared library + tests.
#
# We mount the source tree rather than COPY so a `docker build` run
# does not bake the entire repo into this layer. Caches:
# - /root/.cache/zig : Zig's per-user cache (compiled deps)
# - /src/.zig-cache : project-local cache (incremental rebuilds)
# ---------------------------------------------------------------------
FROM zig AS libghostty
WORKDIR /src
COPY . /src
# `-Dapp-runtime=none` makes the Zig build emit libghostty (the .so
# our Qt frontend links against) and skips the GTK frontend. Tests
# run first so a regression in libghostty fails the build cleanly,
# rather than later in the slower Qt stage.
RUN --mount=type=cache,target=/root/.cache/zig \
--mount=type=cache,target=/src/.zig-cache \
set -eux; \
zig build test -Dapp-runtime=none -Doptimize=Debug; \
zig build -Dapp-runtime=none -Doptimize=ReleaseFast
# ---------------------------------------------------------------------
# qt — CMake build of the Qt frontend against the libghostty artifact.
# ---------------------------------------------------------------------
FROM libghostty AS qt
WORKDIR /src/qt
# The CMake project links against zig-out/lib/ghostty-internal.so and
# materialises libghostty.so as a build-tree symlink (see qt/CMakeLists.txt).
# `--install` lays the binary, .so, .desktop entry and icon into /out
# under the standard FHS layout (bin/, lib/, share/...).
RUN --mount=type=cache,target=/root/.cache/cmake \
set -eux; \
cmake -S /src/qt -B /src/qt/build -G Ninja \
-DCMAKE_BUILD_TYPE=Release; \
cmake --build /src/qt/build --parallel "$(nproc)"; \
cmake --install /src/qt/build --prefix /out
# ---------------------------------------------------------------------
# out — only the final artifacts. Run this image to extract them.
# ---------------------------------------------------------------------
FROM debian:${DEBIAN_VERSION}-slim AS out
COPY --from=qt /out /out
# Default command lists the artifacts so `docker run --rm ghastty`
# is informative without --entrypoint heroics.
CMD ["sh", "-c", "find /out -type f -o -type l | sort"]