From 9c6e8140586141f78ea6b7d0a19eb3cf3b6d8e4f Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Sat, 2 May 2026 15:09:42 +0200 Subject: [PATCH] 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. --- .github/workflows/ci-image.yml | 138 +++++++++++++++++++++++++++++++++ Meta/Docker/ci/Dockerfile | 77 ++++++++++++++++++ Meta/Docker/ci/VERSION | 1 + 3 files changed, 216 insertions(+) create mode 100644 .github/workflows/ci-image.yml create mode 100644 Meta/Docker/ci/Dockerfile create mode 100644 Meta/Docker/ci/VERSION diff --git a/.github/workflows/ci-image.yml b/.github/workflows/ci-image.yml new file mode 100644 index 0000000000..8d77ca42dc --- /dev/null +++ b/.github/workflows/ci-image.yml @@ -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" diff --git a/Meta/Docker/ci/Dockerfile b/Meta/Docker/ci/Dockerfile new file mode 100644 index 0000000000..00482d1820 --- /dev/null +++ b/Meta/Docker/ci/Dockerfile @@ -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/* diff --git a/Meta/Docker/ci/VERSION b/Meta/Docker/ci/VERSION new file mode 100644 index 0000000000..ab361b422d --- /dev/null +++ b/Meta/Docker/ci/VERSION @@ -0,0 +1 @@ +2026.05.0