feat: Init

This commit is contained in:
Wanjohi
2026-04-03 00:33:36 +03:00
parent b743dab332
commit 13db20bad9
499 changed files with 2311 additions and 80039 deletions

484
build/Dockerfile Normal file
View File

@@ -0,0 +1,484 @@
# syntax=docker/dockerfile:1
# =============================================================================
# Nestri Unified Multi-stage Build
# =============================================================================
#
# Build graph (BuildKit runs independent branches in parallel):
#
# initial ─► builder ──┬─► libkrunfw-build ─► libkrun-build ──┐
# ├─► virgl-build ───────────────────────┤
# ├─► mesa-build ─► lib32-mesa-build │
# │ │
# └─► rust-builder ─┬─► gst-wayland-build
# ├─► wl-proxy-build
# ├─► nestrisink-build
# └─► muvm-build ◄─────┘
#
# initial ─► runner ◄── all build stages
#
# Build:
# docker buildx build --target runner -t nestri:latest .
#
# Recommended .dockerignore:
# target/
# .git/
# *.md
#
# =============================================================================
#******************************************************************************
# initial
#******************************************************************************
# Minimal updated Arch Linux base. Every other stage inherits from here.
FROM docker.io/archlinux/archlinux:latest AS initial
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm
#******************************************************************************
# builder
#******************************************************************************
# builder has C/C++ tooling, makepkg, and a non-root build user.
FROM initial AS builder
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
base-devel \
clang \
cmake \
git \
meson \
ninja \
python \
pkg-config
WORKDIR /scratch
ENV ARTIFACTS=/artifacts
RUN mkdir -p "$ARTIFACTS"
# makepkg refuses to run as root
RUN useradd -m builder \
&& echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# Maximise build parallelism
RUN sed -i "s/#MAKEFLAGS=.*/MAKEFLAGS=\"-j$(nproc)\"/" /etc/makepkg.conf
# Let the builder user write to /scratch and /artifacts
RUN chgrp builder /scratch "$ARTIFACTS" \
&& chmod g+ws /scratch "$ARTIFACTS"
#******************************************************************************
# rust-builder
#******************************************************************************
# rust-builder adds the Rust toolchain and common crate build deps.
FROM builder AS rust-builder
ENV CARGO_HOME=/usr/local/cargo
ENV PATH="${CARGO_HOME}/bin:${PATH}"
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
rust \
cargo \
gstreamer \
gst-plugins-base \
gst-plugins-good \
gst-plugins-bad \
libdrm \
libxkbcommon \
wayland \
wayland-protocols \
libinput \
libseccomp \
libcap
# cargo-c is needed by gst-wayland-display (cargo cinstall)
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
cargo install cargo-c
#******************************************************************************
# libkrunfw-build
#******************************************************************************
# Build libkrunfw package with 32-bit support.
FROM builder AS libkrunfw-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
bc \
python-pyelftools \
cpio \
curl \
wget
COPY build/packages/libkrunfw/ /scratch/libkrunfw/
WORKDIR /scratch/libkrunfw
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# libkrun-build
#******************************************************************************
# Build libkrun package (depends on libkrunfw).
FROM builder AS libkrun-build
# Install previously-built libkrunfw
WORKDIR /scratch
COPY --from=libkrunfw-build /artifacts/*.zst pkgs/
RUN pacman -U --noconfirm pkgs/*.zst && rm -rf pkgs
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
rust \
cargo \
patchelf \
clang \
clang18 \
pipewire \
virglrenderer
COPY build/packages/libkrun/ /scratch/libkrun/
WORKDIR /scratch/libkrun
RUN chown -R builder:builder .
USER builder
# NOTE: clang18 required newer versions fail to build libkrun
RUN LIBCLANG_PATH=/usr/lib/llvm18/lib \
makepkg --syncdeps --noconfirm --skippgpcheck \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# virgl-build
#******************************************************************************
# Build virglrenderer package.
FROM builder AS virgl-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
python-yaml \
libepoxy \
mesa \
libva \
libdrm \
vulkan-headers \
vulkan-icd-loader \
libx11 \
glu
COPY build/virglrenderer/ /scratch/virglrenderer/
WORKDIR /scratch/virglrenderer
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# mesa-build
#******************************************************************************
# Build Mesa packages (64-bit).
FROM builder AS mesa-build
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
git \
python-mako \
python-packaging \
python-yaml \
llvm \
rust \
rust-bindgen \
cbindgen \
clang \
glslang \
wayland-protocols \
libvdpau \
libva \
libxrandr
COPY build/mesa/ /scratch/mesa/
WORKDIR /scratch/mesa
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# lib32-mesa-build
#******************************************************************************
# Build 32-bit Mesa packages (depends on 64-bit mesa).
FROM builder AS lib32-mesa-build
# Enable multilib repository for 32-bit dependencies
RUN echo -e "\n[multilib]\nInclude = /etc/pacman.d/mirrorlist" \
>> /etc/pacman.conf
# Install 64-bit mesa packages first
WORKDIR /scratch
COPY --from=mesa-build /artifacts/*.zst pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm \
&& pacman -U --noconfirm pkgs/*.zst \
&& rm -rf pkgs
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed \
git \
python-mako \
python-packaging \
python-yaml \
llvm \
rust \
rust-bindgen \
cbindgen \
clang \
glslang \
wayland-protocols \
libvdpau \
libva \
libxrandr \
lib32-gcc-libs \
lib32-libx11 \
lib32-libdrm \
lib32-llvm \
lib32-expat \
lib32-libelf \
lib32-zstd \
lib32-wayland
COPY build/packages/lib32-mesa/ /scratch/lib32-mesa/
WORKDIR /scratch/lib32-mesa
RUN chown -R builder:builder .
USER builder
RUN makepkg --syncdeps --noconfirm --skippgpcheck --skipinteg \
&& cp *.zst "$ARTIFACTS"
#******************************************************************************
# gst-wayland-build
#******************************************************************************
# Build gst-wayland-display plugin.
FROM rust-builder AS gst-wayland-build
WORKDIR /scratch/gst-wayland-display
RUN git clone https://github.com/games-on-whales/gst-wayland-display.git . \
&& git checkout 67b1183997fd7aaf57398e4b01bd64c4d2433c45
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/gst-wayland-display/target \
cargo cinstall --prefix="${ARTIFACTS}/usr" --release --features cuda
#******************************************************************************
# wl-proxy-build
#******************************************************************************
# Build wl-cross-domain-proxy.
FROM rust-builder AS wl-proxy-build
WORKDIR /scratch/wl-cross-domain-proxy
RUN git clone https://codeberg.org/drakulix/wl-cross-domain-proxy.git . \
&& git checkout c6ce1ca89fb4d6f4f18d3aaf88324d40d4589177
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/wl-cross-domain-proxy/target \
cargo build --release \
&& install -Dm755 target/release/wl-cross-domain-proxy \
"${ARTIFACTS}/usr/bin/wl-cross-domain-proxy"
#******************************************************************************
# nestrisink-build
#******************************************************************************
# Build nestri GStreamer sink plugin.
FROM rust-builder AS nestrisink-build
WORKDIR /scratch/nestrisink
COPY Cargo.toml Cargo.lock ./
COPY crates/ ./crates/
COPY apps/ ./apps/
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/nestrisink/target \
cargo build -p nestri-gst --release \
&& mkdir -p "${ARTIFACTS}/usr/lib/gstreamer-1.0" \
&& cp target/release/libgstnestri.so \
"${ARTIFACTS}/usr/lib/gstreamer-1.0/"
#******************************************************************************
# muvm-build
#******************************************************************************
# Build muvm binary (depends on libkrun + virgl packages).
FROM rust-builder AS muvm-build
# Install custom-built packages that muvm links against
WORKDIR /scratch
COPY --from=libkrunfw-build /artifacts/*.zst pkgs/
COPY --from=libkrun-build /artifacts/*.zst pkgs/
COPY --from=virgl-build /artifacts/*.zst pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm --needed pipewire \
&& pacman -U --noconfirm pkgs/*.zst \
&& rm -rf pkgs
WORKDIR /scratch/muvm
COPY Cargo.toml Cargo.lock ./
COPY crates/ ./crates/
COPY apps/ ./apps/
COPY share/ ./share/
RUN --mount=type=cache,target=${CARGO_HOME}/registry \
--mount=type=cache,target=${CARGO_HOME}/git \
--mount=type=cache,target=/scratch/muvm/target \
cargo build -p muvm --release \
&& install -Dm755 target/release/muvm "${ARTIFACTS}/usr/bin/muvm" \
&& install -Dm755 target/release/muvm-guest "${ARTIFACTS}/usr/bin/muvm-guest"
#******************************************************************************
# runner
#******************************************************************************
# The final runtime image kept as simple as possible.
FROM initial AS runner
# ---- Enable multilib for 32-bit packages ----
RUN echo -e "\n[multilib]\nInclude = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
# ---- Collect and install custom-built .zst packages ----
COPY --from=mesa-build /artifacts/*.zst /tmp/pkgs/
COPY --from=lib32-mesa-build /artifacts/*.zst /tmp/pkgs/
COPY --from=libkrunfw-build /artifacts/*.zst /tmp/pkgs/
COPY --from=libkrun-build /artifacts/*.zst /tmp/pkgs/
COPY --from=virgl-build /artifacts/*.zst /tmp/pkgs/
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -Syu --noconfirm \
&& pacman -U --noconfirm /tmp/pkgs/*.zst \
&& rm -rf /tmp/pkgs
# ---- System runtime packages (single transaction for consistency) ----
RUN --mount=type=cache,sharing=locked,target=/var/cache/pacman/pkg \
pacman -S --noconfirm \
# Core
base systemd sudo bash \
# Steam & Gaming
steam gamescope mangohud seatd \
# Vulkan / Mesa / VA-API
vulkan-icd-loader vulkan-tools lib32-vulkan-icd-loader \
libva-mesa-driver libva-utils \
# Wayland / X
wayland libdrm libxkbcommon \
xorg-xwayland xwayland-satellite \
libinput \
# Audio
pipewire pipewire-pulse pipewire-alsa wireplumber \
lib32-libpulse \
# GStreamer runtime plugins
gstreamer gst-plugins-base \
gst-plugins-good gst-plugins-bad \
gst-plugin-isobmff gst-plugin-va \
gst-plugin-pipewire gst-plugin-qsv \
# Video acceleration
vpl-gpu-rt \
# GUI / Fonts
gtk3 lib32-gtk3 \
ttf-liberation noto-fonts-cjk \
# Networking & System
dbus curl passt openssh hwdata \
jq pacman-contrib lib32-libvdpau
# ---- Copy artifacts from Rust build stages ----
COPY --from=wl-proxy-build /artifacts/usr/ /usr/
COPY --from=gst-wayland-build /artifacts/usr/ /usr/
COPY --from=nestrisink-build /artifacts/usr/ /usr/
COPY --from=muvm-build /artifacts/usr/ /usr/
# ---- muvm-guest multi-call symlinks ----
RUN mkdir -p /opt/bin \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-remote \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-configure-network \
&& ln -sf /usr/bin/muvm-guest /opt/bin/muvm-pwbridge
# ---- User setup ----
ARG NESTRI_USER_PWD=""
ENV NESTRI_USER="nestri" \
NESTRI_UID=1000 \
NESTRI_GID=1000 \
NESTRI_LANG=en_US.UTF-8 \
NESTRI_XDG_RUNTIME_DIR=/run/user/1000 \
NESTRI_HOME=/home/nestri \
NVIDIA_DRIVER_CAPABILITIES=all
RUN mkdir -p "/home/${NESTRI_USER}" \
&& groupadd -g "${NESTRI_GID}" "${NESTRI_USER}" \
&& useradd -d "/home/${NESTRI_USER}" \
-u "${NESTRI_UID}" -g "${NESTRI_GID}" \
-s /bin/bash "${NESTRI_USER}" \
&& echo "${NESTRI_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers \
&& NESTRI_USER_PWD="${NESTRI_USER_PWD:-$(openssl rand -base64 12)}" \
&& echo "Setting password for ${NESTRI_USER}" \
&& echo "${NESTRI_USER}:${NESTRI_USER_PWD}" | chpasswd \
&& mkdir -p "${NESTRI_XDG_RUNTIME_DIR}" \
&& chown "${NESTRI_USER}:${NESTRI_USER}" "${NESTRI_XDG_RUNTIME_DIR}" \
&& usermod -aG input,video,render,seat,wheel,systemd-journal "${NESTRI_USER}"
# ---- Wireplumber suspend disable ----
RUN mkdir -p /run/dbus \
&& sed -i -z \
-e 's/{[[:space:]]*name = node\/suspend-node\.lua,[[:space:]]*type = script\/lua[[:space:]]*provides = hooks\.node\.suspend[[:space:]]*}[[:space:]]*//g' \
-e '/wants = \[/{s/hooks\.node\.suspend\s*//; s/,\s*\]/]/}' \
/usr/share/wireplumber/wireplumber.conf
# ---- Audio & config directories ----
COPY etc/ /etc/
# ---- Steam config ----
RUN mkdir -p "${NESTRI_HOME}/.local/share/Steam/config"
COPY build/steam/config.vdf "${NESTRI_HOME}/.local/share/Steam/config/"
# ---- Mask noisy/broken services ----
RUN systemctl mask \
dm-event.socket \
systemd-firstboot.service \
systemd-homed.service \
systemd-hwdb-update.service \
systemd-network-generator.service \
systemd-networkd.service \
systemd-networkd-wait-online.service \
systemd-remount-fs.service \
systemd-resolved.service \
systemd-timesyncd.service \
systemd-userdbd.service \
getty@tty1.service \
serial-getty@.service
# ---- Systemd units ----
RUN systemctl enable dbus.service \
&& systemctl set-default microvm.target
# ---- Scripts (copied last they change the most) ----
COPY build/usr/bin/ /usr/bin/
# ---- Smoke test ----
RUN command -v steam muvm muvm-guest wl-cross-domain-proxy \
nestri-entry nestri-init
# ---- Final cleanup ----
RUN rm -rf /tmp/*