Three independent reasons Rust CI stayed slow despite sccache, fixed together because
they share the same measurement.
1. sccache only ever covered RUSTC. Every C/C++ dependency in the tree — aws-lc-sys,
openh264-sys2's vendored C++, the CMake-built libopus behind audiopus_sys — was
compiled from scratch on every job of every workflow. CMAKE_{C,CXX}_COMPILER_LAUNCHER
plus CC_/CXX_x86_64_unknown_linux_gnu route both build-script styles (cc-rs and
cmake-rs) through the same shared cache.
The CC_* vars are JOB-scoped in ci.yml and deb.yml, never workflow-scoped: the
arm64 cross image sets its own CC_x86_64_unknown_linux_gnu=pf-host-cc, the wrapper
that keeps ffmpeg-sys-next's host probe off the arm64 include dirs. Overwriting it
would surface as a header mismatch rather than as a CI config error.
2. Linking is cacheable by nothing, and these jobs relink the host, client, session,
cli, worker and tray on every run — twice per push for rpm (f43 + f44). The four
Linux builder images now install mold and carry a $CARGO_HOME/config.toml that uses
it for x86_64. aarch64 is deliberately left alone (cross driver, already-fast legs).
Each image asserts `mold --version` in its build, so an image can never ship the
flag without the linker: docker.yml goes red and :latest stays on the last good one.
3. THE EXPENSIVE ONE. ci.yml (debug) and deb.yml (release) named a byte-identical
target-cache key, under a comment claiming the release build reused ci.yml's
artifacts. It never could. actions/cache is first-saver-wins on an exact key and
ci.yml is the faster job, so the shared key always held a debug-only target/ — and,
worse, deb.yml could then never save its own, because the key was taken. Every
canary .deb has been a from-scratch release build for as long as both keys existed.
Same collision on the arm64 pair, and a third participant in
linux-client-screenshots.yml. Split into -debug-/-release- key families; that job
reads deb's tree via restore-keys but keeps its own exact key so it can never win
the save race and replace a full tree with its single-crate one.
Also: one scripts/ci/ensure-sccache.sh replaces ten copy-pasted bootstrap blocks that
had already drifted into two dialects (GNU tar --wildcards vs bsdtar), every Rust job
now ends with --show-stats so a cache regression is visible instead of just "CI got
slower", and deb.yml's web install joins every other CI install on --ignore-scripts.
No behaviour change to any artifact: same compilers, same flags, same outputs.
85 lines
5.7 KiB
Docker
85 lines
5.7 KiB
Docker
# Arch CI builder: base-devel + every dependency arch.yml's two makepkg legs used to
|
|
# pacman-install per run (~1 GB of mirror traffic each time) + bun + sccache + nodejs
|
|
# (JS actions exec node INSIDE the job container — the same lesson as android-ci).
|
|
# Content-keyed and rebuilt only when the ci/ tree changes (docker.yml `builders`).
|
|
#
|
|
# docker build -f ci/arch-ci.Dockerfile -t punktfunk-arch-ci ci
|
|
#
|
|
# ROLLING-RELEASE TRADEOFF, on purpose: packages now build against the Arch snapshot
|
|
# from the last image rebuild instead of a fresh -Syu per run. That is the same staleness
|
|
# the gamescope cache already embraces ("a stale binary against newer system libs is the
|
|
# same risk the distro's own package carries between rebuilds"), and any ci/ edit — or
|
|
# bumping the date in this line (refreshed: 2026-08-08) — re-keys and re-snapshots it.
|
|
#
|
|
# ⚠ That staleness has a sharp edge, and 2026-08-08 is why the date above moved: this snapshot is
|
|
# what decides which FFmpeg the HOST links, and arch.yml deliberately runs no -Syu, so the builder
|
|
# stayed frozen on ffmpeg 8 (libavcodec 62) even after Arch shipped 2:9.0-5 (libavcodec 63) to
|
|
# every user. A canary built from the old snapshot therefore CANNOT satisfy the soname dep that
|
|
# packaging/arch/PKGBUILD now derives from the link (libavcodec.so=62-64 against a box that has
|
|
# 63-64), so it would simply refuse to install rather than start. Re-keying this image is the step
|
|
# that makes the ffmpeg-9 bump actually reach the package — a Cargo.toml bump alone does nothing
|
|
# here. Whenever Arch moves to an FFmpeg major, bump the date in the same commit.
|
|
#
|
|
# ⚠ AND KNOW WHY THAT WAS NOT ENOUGH: bumping this date only helps once docker.yml has actually
|
|
# republished the image, and nothing sequences the two workflows. v0.25.0 was tagged four minutes
|
|
# after the ffmpeg-9 merge, so the release build still pulled the FFmpeg-8 `:latest` and published
|
|
# a punktfunk-host that no up-to-date Arch box could install — which blocks the user's ENTIRE
|
|
# `pacman -Syu`, not just our package. arch.yml therefore no longer trusts this image on that one
|
|
# axis: it compares the builder's libav sonames against the repos before building (and `-Syu`s
|
|
# itself if they differ), and refuses to publish anything a pristine-db `pacman -U --print` says
|
|
# is unsatisfiable. This file staying current is still the CHEAP path — those guards are the
|
|
# backstop, not the plan.
|
|
FROM docker.io/library/archlinux:base-devel
|
|
|
|
# One transaction: the main build/runtime deps (first list) + the gamescope companion's
|
|
# deps (second list) — both copied verbatim from what arch.yml installed in-job, where
|
|
# they now no-op as `--needed` guards.
|
|
# vulkan-headers rides the first list only because arch.yml's copy does; the package it actually
|
|
# serves is the gamescope companion (packaging/gamescope/PKGBUILD makedepends). punktfunk itself
|
|
# needs no system Vulkan headers — pyrowave-sys bindgens its own vendored copy and ash dlopens the
|
|
# loader — but arch.yml builds gamescope with `makepkg -d`, so an absent makedepend would not be
|
|
# reported as a missing dependency, only as a compile failure. Keep it.
|
|
RUN pacman -Syu --noconfirm --needed \
|
|
git nodejs rust clang cmake ninja nasm pkgconf python vulkan-headers \
|
|
gtk4 libadwaita sdl3 ffmpeg pipewire wayland libxkbcommon opus libei \
|
|
mesa libglvnd unzip libarchive \
|
|
glslang libcap libdrm libinput libx11 libxcomposite libxdamage libxext \
|
|
libxmu libxrender libxres libxtst libxxf86vm libavif libdecor \
|
|
hwdata luajit seatd sdl2-compat vulkan-icd-loader \
|
|
xcb-util-errors xcb-util-wm xorg-xwayland \
|
|
meson glm wayland-protocols benchmark libxcursor \
|
|
# mold: link-phase accelerator (sccache cannot cache linking). makepkg links the release
|
|
# host, client, worker and tray on every arch.yml run. Wired via cargo-config-mold.toml
|
|
# below. It does NOT affect the gamescope companion leg — that is meson + its own linker,
|
|
# and its `-static-libstdc++` link is untouched.
|
|
mold \
|
|
&& pacman -Scc --noconfirm
|
|
|
|
# bun builds the punktfunk-web console + the punktfunk-scripting runner AND is vendored
|
|
# as their runtime (PF_WITH_WEB=1 / PF_WITH_SCRIPTING=1); it's AUR-only on Arch, so
|
|
# bootstrap the official binary — once, here, instead of per run.
|
|
RUN curl -fsSL https://bun.sh/install | bash \
|
|
&& install -m0755 /root/.bun/bin/bun /usr/local/bin/bun \
|
|
&& rm -rf /root/.bun \
|
|
&& bun --version
|
|
|
|
# Shared compile cache: jobs set RUSTC_WRAPPER=sccache (backend = RustFS S3 on the LAN,
|
|
# see .gitea/workflows — the env lives there so dev use of this image stays uncached).
|
|
ARG SCCACHE_VERSION=0.10.0
|
|
RUN curl -fsSL "https://github.com/mozilla/sccache/releases/download/v${SCCACHE_VERSION}/sccache-v${SCCACHE_VERSION}-x86_64-unknown-linux-musl.tar.gz" \
|
|
| tar -xz --wildcards --strip-components=1 -C /usr/local/bin '*/sccache' \
|
|
&& sccache --version
|
|
|
|
# CARGO_HOME is declared here only so this image agrees with what arch.yml already sets at job
|
|
# level (and so `cargo` finds the config below when the image is used by hand). The workflow still
|
|
# passes CARGO_HOME explicitly across the `sudo -u builder env …` boundary, which strips ambient
|
|
# env — that is why the C/C++ sccache wiring has to be re-exported there by name while THIS file,
|
|
# being a file, crosses the boundary for free.
|
|
ENV CARGO_HOME=/usr/local/cargo
|
|
RUN mkdir -p /usr/local/cargo && chmod -R a+w /usr/local/cargo
|
|
|
|
# Link x86_64 with mold — see cargo-config-mold.toml's header for the rustflags traps, and
|
|
# rust-ci.Dockerfile for why the `mold --version` assertion sits next to the COPY.
|
|
COPY cargo-config-mold.toml /usr/local/cargo/config.toml
|
|
RUN mold --version && test -r /usr/local/cargo/config.toml
|