CI: Introduce build of new CI Docker image

The goal is to have a stable, reproducible build environment for Linux
amd64 and arm64 that we can simply pull in for CI build jobs. We've seen
that apt repositories can be temporarily unreachable or build dependency
versions are silently updated causing builds to fail. A nice benefit of
this is that we can skip most of the environment setup, which takes a
couple of minutes each run.
This commit is contained in:
Jelle Raaijmakers 2026-05-02 15:09:42 +02:00 committed by Jelle Raaijmakers
parent 5f6a91b38a
commit 9c6e814058
3 changed files with 216 additions and 0 deletions

138
.github/workflows/ci-image.yml vendored Normal file
View file

@ -0,0 +1,138 @@
name: Build CI Image
on:
pull_request:
paths:
- '.github/workflows/ci-image.yml'
- 'Meta/Docker/ci/**'
- 'rust-toolchain.toml'
push:
branches: [master]
paths:
- '.github/workflows/ci-image.yml'
- 'Meta/Docker/ci/**'
- 'rust-toolchain.toml'
workflow_dispatch:
schedule:
# Keep image build breakage visible before dependency changes block regular CI.
- cron: '0 0 * * 1'
permissions:
contents: read
packages: write
env:
IMAGE_NAME: ghcr.io/ladybirdbrowser/ladybird-ci
jobs:
prepare:
if: github.repository == 'LadybirdBrowser/ladybird'
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
ci_image_version: ${{ steps.ci-image-version.outputs.ci_image_version }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Read image version
id: ci-image-version
shell: bash
run: |
ci_image_version="$(tr -d '[:space:]' < Meta/Docker/ci/VERSION)"
if ! [[ "${ci_image_version}" =~ ^[0-9]{4}\.[0-9]{2}\.[0-9]+$ ]]; then
echo "Invalid CI image version: ${ci_image_version}"
exit 1
fi
echo "ci_image_version=${ci_image_version}" >> "${GITHUB_OUTPUT}"
- name: Require version bump for Dockerfile changes
if: github.event_name == 'pull_request'
shell: bash
run: |
changed_files="$(git diff --name-only "${{ github.event.pull_request.base.sha }}...HEAD")"
if grep -Eqx 'Meta/Docker/ci/Dockerfile|rust-toolchain.toml' <<< "${changed_files}" \
&& ! grep -qx 'Meta/Docker/ci/VERSION' <<< "${changed_files}"; then
echo "Meta/Docker/ci/VERSION must change when CI image inputs change."
exit 1
fi
build:
if: github.repository == 'LadybirdBrowser/ladybird'
needs: prepare
name: ${{ matrix.platform }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: blacksmith-8vcpu-ubuntu-2404
arch_suffix: amd64
- platform: linux/arm64
runner: blacksmith-8vcpu-ubuntu-2404-arm
arch_suffix: arm64
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Prepare Docker context
shell: bash
run: cp rust-toolchain.toml Meta/Docker/ci/rust-toolchain.toml
- name: Login to GitHub Container Registry
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and publish image
uses: docker/build-push-action@v6
with:
context: Meta/Docker/ci
file: Meta/Docker/ci/Dockerfile
platforms: ${{ matrix.platform }}
push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
tags: |
${{ env.IMAGE_NAME }}:${{ needs.prepare.outputs.ci_image_version }}-${{ matrix.arch_suffix }}
${{ env.IMAGE_NAME }}:sha-${{ github.sha }}-${{ matrix.arch_suffix }}
labels: |
org.opencontainers.image.source=https://github.com/LadybirdBrowser/ladybird
org.opencontainers.image.description=Ladybird Linux CI build image
org.opencontainers.image.version=${{ needs.prepare.outputs.ci_image_version }}
publish-manifest:
if: github.repository == 'LadybirdBrowser/ladybird' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
needs: [prepare, build]
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Publish multi-arch manifests
shell: bash
run: |
docker buildx imagetools create \
--tag "${IMAGE_NAME}:${{ needs.prepare.outputs.ci_image_version }}" \
--tag "${IMAGE_NAME}:sha-${{ github.sha }}" \
--tag "${IMAGE_NAME}:latest" \
"${IMAGE_NAME}:${{ needs.prepare.outputs.ci_image_version }}-amd64" \
"${IMAGE_NAME}:${{ needs.prepare.outputs.ci_image_version }}-arm64"

77
Meta/Docker/ci/Dockerfile Normal file
View file

@ -0,0 +1,77 @@
FROM ubuntu:24.04
ARG GCC_VERSION=14
ARG LLVM_VERSION=21
ENV DEBIAN_FRONTEND=noninteractive
ENV RUSTUP_HOME=/opt/rustup
ENV CARGO_HOME=/opt/cargo
ENV PATH=/opt/cargo/bin:$PATH
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
COPY rust-toolchain.toml /tmp/rust-toolchain.toml
RUN apt-get update -o APT::Update::Error-Mode=any \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg \
lsb-release \
software-properties-common \
&& UBUNTU_RELEASE="$(lsb_release -cs)" \
&& curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor -o /usr/share/keyrings/llvm-snapshot.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-snapshot.gpg] http://apt.llvm.org/${UBUNTU_RELEASE}/ llvm-toolchain-${UBUNTU_RELEASE}-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list \
&& curl -fsSL https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor -o /usr/share/keyrings/kitware-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ ${UBUNTU_RELEASE} main" > /etc/apt/sources.list.d/kitware.list \
&& apt-get update -o APT::Update::Error-Mode=any \
&& apt-get install -y --no-install-recommends \
autoconf \
autoconf-archive \
automake \
bison \
build-essential \
ccache \
clang-${LLVM_VERSION} \
clang++-${LLVM_VERSION} \
clang-tools-${LLVM_VERSION} \
cmake \
file \
fonts-liberation2 \
g++-${GCC_VERSION} \
gcc-${GCC_VERSION} \
git \
jq \
libcurl4-openssl-dev \
libdrm-dev \
libegl1-mesa-dev \
libgl1-mesa-dev \
libpulse-dev \
libssl-dev \
libstdc++-${GCC_VERSION}-dev \
libtool \
libxkbcommon-dev \
lld-${LLVM_VERSION} \
llvm-${LLVM_VERSION} \
nasm \
ninja-build \
pkg-config \
pulseaudio \
python3 \
python3-venv \
qt6-base-dev \
qt6-tools-dev-tools \
tar \
unzip \
xz-utils \
zip \
zstd \
&& rustup_init="$(mktemp)" \
&& curl -fsSL https://sh.rustup.rs -o "${rustup_init}" \
&& chmod +x "${rustup_init}" \
&& rust_toolchain="$(grep -E '^channel = ' /tmp/rust-toolchain.toml | cut -d '"' -f 2)" \
&& "${rustup_init}" -y --no-modify-path --profile minimal --default-toolchain "${rust_toolchain}" \
&& rm "${rustup_init}" \
&& rustup component add rustfmt clippy \
&& rm -rf /opt/rustup/downloads /opt/rustup/tmp /opt/cargo/registry /tmp/rust-toolchain.toml \
&& rm -rf /var/lib/apt/lists/*

1
Meta/Docker/ci/VERSION Normal file
View file

@ -0,0 +1 @@
2026.05.0