mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
## What
Adds `build/` — a Dockerfile with `mesa-build`, `nestri-build`,
`os-base`, `runtime`, `runtime_prod` and `runtime_debug` stages, plus
the `etc/` overlay, `mkimage.sh` and a `Makefile` — laid out the way
[borealis](https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/main/project-borealis)
lays out its own `build/`. This moves the guest rootfs formula into this
repo, targeting the four open guest components already here: `nescope`,
`neshub`, `neswire`, `nescapture`.
## Two structural properties worth calling out
- **No privileged host chroot.** A bare `chroot` into a hand-extracted
rootfs needs `/proc`, `/sys`, `/dev` bind-mounted in first. `os-base`
here is `FROM artixlinux/artixlinux:base-openrc` directly with `pacman
-S` as plain `RUN` steps — a Docker build step already has its own
`/proc`/`/sys`/`/dev`.
- **No host-side ownership bug to guard against.** `COPY --from=` runs
as root inside the build with no invoking-user uid in the loop.
## Scope boundary
**Deliberately excludes Proton and Valve's `steamclient.so`** — both
closed, and `CLAUDE.md` forbids closed content in this repo.
`runtime_prod`, tagged `nestrilabs/nestri:base`, is a complete,
bootable, Steam-less image — and also the shared foundation other builds
start from. Whatever layers Proton/Steam on top of it is a closed build
outside this repo, by design.
## Known gap
Nothing starts a payload yet — `nesinit` isn't open code — so
`/etc/init.d/nescope` boots it in plain-compositor mode (no command
after `--`) rather than running a game. Real and testable, just not a
full session yet. Details in `build/README.md`.
## Status
Built and tagged locally as `nestrilabs/nestri:base` (podman, no
`--no-cache` issues, greptile's three findings all fixed and verified
against a real build). Not yet packed into a disk image or run inside
nesbox.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
The PR adds a multi-stage Artix/OpenRC guest-rootfs build for the open
Nestri components, with production and debug image flavors.
- Builds patched Mesa and the Rust workspace in dedicated builder
stages.
- Assembles and configures the bootable guest environment and OpenRC
services.
- Packs a selected container image into an ext4 root filesystem while
retaining rootless container storage access.
<h3>Confidence Score: 5/5</h3>
The PR appears safe to merge.
No blocking failure remains.
<h3>Important Files Changed</h3>
| Filename | Overview |
|----------|----------|
| build/Dockerfile | Defines the complete multi-stage build, overlays
repository-root-relative configuration paths, and creates production and
debug runtime targets. |
| build/Makefile | Provides consistent image build and packing targets
using a repository-root context and matching image tags. |
| build/scripts/mkimage.sh | Keeps container-runtime operations in the
invoking user's storage while escalating only filesystem creation and
mounting operations. |
| build/etc/conf.d/nestri-user-env | Supplies the shared service
environment and export function required by the OpenRC service scripts.
|
| build/etc/init.d/guest-net | Configures optional guest networking from
kernel parameters or stable defaults. |
| build/etc/init.d/neswire | Starts the audio sink after its
dependencies and pins it as the PipeWire default once the graph is
ready. |
<h3>Flowchart</h3>
```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Arch builder] --> B[Mesa build]
A --> C[Nestri workspace build]
D[Artix OpenRC base] --> E[Common runtime]
B --> E
C --> E
F[build/etc overlay] --> E
E --> G[runtime_prod]
E --> H[runtime_debug]
G --> I[Container image]
H --> J[Debug container image]
I --> K[mkimage.sh]
J --> K
K --> L[ext4 rootfs]
```
<sub>Reviews (6): Last reviewed commit: ["refactor(build): rename the
published
im..."](6fecc8cc31)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=58981739)</sub>
<!-- /greptile_comment -->
67 lines
3.1 KiB
Makefile
67 lines
3.1 KiB
Makefile
SHELL := /bin/bash
|
|
.PHONY: build build-debug image image-debug clean help
|
|
|
|
CONTAINER_RT := $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null)
|
|
ifeq ($(CONTAINER_RT),)
|
|
$(error "Neither docker nor podman found in PATH")
|
|
endif
|
|
|
|
# The Dockerfile COPYs from apps/ and crates/, so the build context is the
|
|
# repo root, not this directory — same reason borealis's build/ *is* its own
|
|
# context: here the guest source lives one level up instead of inside build/.
|
|
CONTEXT := ..
|
|
# `ghcr.io/nestrilabs/nestri/base` (a package name with a `/` in it, matching
|
|
# how every other image this org has published is named) is a deliberate
|
|
# name, not just a tag: this is the image other builds start FROM —
|
|
# nesbox's jailer image extracts Mesa/virgl from it to keep the guest and
|
|
# host sides of the virtio-gpu native-context protocol on the same patched
|
|
# Mesa, and closed downstream builds layer Proton/Steam on top of it
|
|
# elsewhere. `runtime_prod`/`runtime_debug` already are exactly that: distro
|
|
# packages plus our own Mesa/nestri artifacts overlaid on top, nothing
|
|
# stripped that a downstream COPY --from= would miss — no separate
|
|
# unstripped tag is needed for this.
|
|
#
|
|
# The registry host is part of the name on purpose, including for local
|
|
# builds: a bare `nestrilabs/nestri` has no host, so anyone who pulls
|
|
# instead of building locally resolves it against Docker Hub by default,
|
|
# where it does not exist. One name, always pullable, beats a local-only
|
|
# short name and a different published one.
|
|
IMAGE_NAME := ghcr.io/nestrilabs/nestri/base
|
|
OUTPUT_DIR := output
|
|
ROOTFS_SIZE ?= 5G
|
|
FORCE_REBUILD ?=
|
|
|
|
build:
|
|
DOCKER_BUILDKIT=1 $(CONTAINER_RT) build $(if $(FORCE_REBUILD),--no-cache,) \
|
|
-f Dockerfile -t $(IMAGE_NAME):latest --target runtime_prod $(CONTEXT)
|
|
|
|
build-debug:
|
|
DOCKER_BUILDKIT=1 $(CONTAINER_RT) build $(if $(FORCE_REBUILD),--no-cache,) \
|
|
-f Dockerfile -t $(IMAGE_NAME):debug --target runtime_debug $(CONTEXT)
|
|
|
|
# Not `sudo make image`/sudo'd in here: mkimage.sh runs as you and escalates
|
|
# only the specific commands that need root. Under rootless Podman, `make
|
|
# build` stores the image in *your* storage — sudo-ing the whole script would
|
|
# have root's podman look for that tag in its own, separate storage and fail
|
|
# to find it.
|
|
image: build
|
|
@mkdir -p $(OUTPUT_DIR)
|
|
bash scripts/mkimage.sh $(IMAGE_NAME):latest $(OUTPUT_DIR)/rootfs.ext4 $(ROOTFS_SIZE)
|
|
|
|
image-debug: build-debug
|
|
@mkdir -p $(OUTPUT_DIR)
|
|
bash scripts/mkimage.sh $(IMAGE_NAME):debug $(OUTPUT_DIR)/rootfs-debug.ext4 $(ROOTFS_SIZE)
|
|
|
|
clean:
|
|
rm -rf $(OUTPUT_DIR)
|
|
|
|
help:
|
|
@echo "Usage:"
|
|
@echo " make build Build the runtime_prod container image"
|
|
@echo " make build-debug Build the runtime_debug container image"
|
|
@echo " make image Build + pack runtime_prod into output/rootfs.ext4"
|
|
@echo " make image-debug Build + pack runtime_debug into output/rootfs-debug.ext4"
|
|
@echo " make clean Remove output/"
|
|
@echo " make FORCE_REBUILD=1 ... Rebuild from scratch, no layer cache"
|
|
@echo " make ROOTFS_SIZE=8G image Override the packed image size (default 5G)"
|