mirror of
https://github.com/ps5-linux/ps5-linux-image.git
synced 2026-07-15 21:42:27 +00:00
Initial commit
This commit is contained in:
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
linux_deb/
|
||||||
|
linux-bin/
|
||||||
|
work/
|
||||||
|
work-alpine/
|
||||||
|
output/
|
||||||
|
.git/
|
||||||
|
ps5-linux-patches/
|
||||||
131
README.md
Normal file
131
README.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# PS5 Linux Image Builder
|
||||||
|
|
||||||
|
Builds bootable Linux USB images for PlayStation 5 using Docker containers. Supports Ubuntu 24.04, Ubuntu 26.04, Arch, and Alpine, individually or as a multi-distro image with kexec switching.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Docker (with permission to run `--privileged` containers) — install as per your distro's instructions
|
||||||
|
- ~30GB free disk space
|
||||||
|
|
||||||
|
Once Docker is installed, add your user to the docker group and apply it without logging out:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
newgrp docker
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build a single Ubuntu 24.04 image
|
||||||
|
./build_image.sh --distro ubuntu
|
||||||
|
|
||||||
|
# Build a single Ubuntu 26.04 image
|
||||||
|
./build_image.sh --distro ubuntu2604
|
||||||
|
|
||||||
|
# Build a multi-distro image (ubuntu + ubuntu2604 + arch + alpine)
|
||||||
|
./build_image.sh --distro all
|
||||||
|
```
|
||||||
|
|
||||||
|
The script auto-clones the kernel source, applies PS5 patches, compiles, and builds the image. Subsequent runs reuse cached artifacts automatically. Press Ctrl+C at any time to abort cleanly.
|
||||||
|
|
||||||
|
## Flash to USB
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dd if=output/ps5-ubuntu.img of=/dev/sdX bs=4M status=progress
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Flag | Description | Default |
|
||||||
|
|------|-------------|---------|
|
||||||
|
| `--distro` | `ubuntu`, `ubuntu2604`, `arch`, `alpine`, or `all` | `ubuntu` |
|
||||||
|
| `--kernel` | Path to kernel source directory | auto-clone `v6.19.10` |
|
||||||
|
| `--img-size` | Disk image size in MB | `12000` (`32000` for `all`) |
|
||||||
|
| `--clean` | Remove all cached build artifacts and start fresh | off |
|
||||||
|
|
||||||
|
## Caching
|
||||||
|
|
||||||
|
The build automatically skips stages that have already completed:
|
||||||
|
|
||||||
|
- **Kernel source** — reused if `work/linux/` exists
|
||||||
|
- **Kernel packages** — reused if `.deb`/`.pkg.tar.zst` files exist in `linux-bin/`
|
||||||
|
- **Root filesystem** — reused if chroot directories are populated
|
||||||
|
|
||||||
|
Use `--clean` to wipe everything and rebuild from scratch. The build will also suggest `--clean` if a stage fails.
|
||||||
|
|
||||||
|
## Build Output
|
||||||
|
|
||||||
|
```
|
||||||
|
PS5 Linux Image Builder
|
||||||
|
=======================
|
||||||
|
Distro: all
|
||||||
|
(ubuntu ubuntu2604 arch alpine)
|
||||||
|
Image size: 32000MB
|
||||||
|
Kernel src: /path/to/work/linux
|
||||||
|
|
||||||
|
Stages:
|
||||||
|
1. Kernel cached
|
||||||
|
2. Root filesystem build
|
||||||
|
3. Disk image build
|
||||||
|
|
||||||
|
Logs: /path/to/build.log
|
||||||
|
|
||||||
|
✓ Kernel packages (cached)
|
||||||
|
✓ Build image builder image
|
||||||
|
⠹ Building arch rootfs
|
||||||
|
```
|
||||||
|
|
||||||
|
All verbose output goes to `build.log`. The terminal shows a spinner with live progress.
|
||||||
|
|
||||||
|
## Distributions
|
||||||
|
|
||||||
|
| Distro | Desktop | Kernel format | Init |
|
||||||
|
|--------|---------|---------------|------|
|
||||||
|
| Ubuntu 24.04 (Noble) | GNOME | `.deb` | systemd |
|
||||||
|
| Ubuntu 26.04 (Resolute) | GNOME | `.deb` | systemd |
|
||||||
|
| Arch | Sway | `.pkg.tar.zst` | systemd |
|
||||||
|
| Alpine (3.21) | GNOME | extracted from `.deb` | OpenRC |
|
||||||
|
|
||||||
|
## Multi-distro Image
|
||||||
|
|
||||||
|
`--distro all` builds a 32GB image with 5 partitions:
|
||||||
|
|
||||||
|
| Partition | Type | Label | Content |
|
||||||
|
|-----------|------|-------|---------|
|
||||||
|
| p1 | FAT32 | boot | Shared kernel, per-distro initrds, kexec scripts |
|
||||||
|
| p2 | ext4 | ubuntu | Ubuntu 24.04 rootfs |
|
||||||
|
| p3 | ext4 | ubuntu2604 | Ubuntu 26.04 rootfs |
|
||||||
|
| p4 | ext4 | arch | Arch rootfs |
|
||||||
|
| p5 | ext4 | alpine | Alpine rootfs |
|
||||||
|
|
||||||
|
The boot partition contains kexec scripts to switch between distros at runtime. Ubuntu 24.04 is the default boot target.
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
build_image.sh # Main build script
|
||||||
|
docker/
|
||||||
|
kernel-builder/ # Kernel compilation container
|
||||||
|
kernel-builder-arch/ # Repackages .deb kernel as .pkg.tar.zst
|
||||||
|
image-builder/
|
||||||
|
Dockerfile # Image building container (distrobuilder)
|
||||||
|
entrypoint.sh # Single-distro build logic
|
||||||
|
entrypoint-multi.sh # Multi-distro build logic
|
||||||
|
distros/
|
||||||
|
ubuntu/ # Ubuntu 24.04 (Noble)
|
||||||
|
ubuntu2604/ # Ubuntu 26.04 (Resolute)
|
||||||
|
arch/ # Arch Linux
|
||||||
|
alpine/ # Alpine 3.21
|
||||||
|
shared/ # Kernel postinst hooks (single + multi)
|
||||||
|
boot/
|
||||||
|
cmdline.txt # Kernel cmdline template (__DISTRO__ placeholder)
|
||||||
|
vram.txt # VRAM allocation
|
||||||
|
kexec-{ubuntu,ubuntu2604,arch,alpine}.sh
|
||||||
|
patches/
|
||||||
|
linux.patch # PS5 kernel patches
|
||||||
|
config # Kernel .config
|
||||||
|
work/ # Build artifacts (auto-created)
|
||||||
|
linux-bin/ # Compiled kernel packages
|
||||||
|
output/ # Final .img files
|
||||||
|
```
|
||||||
1
boot/cmdline.txt
Normal file
1
boot/cmdline.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
root=LABEL=__DISTRO__ rw rootwait console=ttyTitania0 console=tty0 video=DP-1:1920x1080@60 mitigations=off idle=halt rootfstype=ext4
|
||||||
6
boot/kexec-alpine.sh
Normal file
6
boot/kexec-alpine.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Switch to Alpine Linux via kexec
|
||||||
|
set -e
|
||||||
|
BOOT=/boot/efi
|
||||||
|
kexec -l "$BOOT/bzImage" --initrd="$BOOT/initrd-alpine.img" --command-line="$(cat $BOOT/cmdline-alpine.txt)"
|
||||||
|
kexec -e
|
||||||
6
boot/kexec-arch.sh
Normal file
6
boot/kexec-arch.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Switch to Arch Linux via kexec
|
||||||
|
set -e
|
||||||
|
BOOT=/boot/efi
|
||||||
|
kexec -l "$BOOT/bzImage" --initrd="$BOOT/initrd-arch.img" --command-line="$(cat $BOOT/cmdline-arch.txt)"
|
||||||
|
kexec -e
|
||||||
6
boot/kexec-ubuntu.sh
Normal file
6
boot/kexec-ubuntu.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Switch to Ubuntu via kexec
|
||||||
|
set -e
|
||||||
|
BOOT=/boot/efi
|
||||||
|
kexec -l "$BOOT/bzImage" --initrd="$BOOT/initrd-ubuntu.img" --command-line="$(cat $BOOT/cmdline-ubuntu.txt)"
|
||||||
|
kexec -e
|
||||||
6
boot/kexec-ubuntu2604.sh
Executable file
6
boot/kexec-ubuntu2604.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Switch to Ubuntu 26.04 via kexec
|
||||||
|
set -e
|
||||||
|
BOOT=/boot/efi
|
||||||
|
kexec -l "$BOOT/bzImage" --initrd="$BOOT/initrd-ubuntu2604.img" --command-line="$(cat $BOOT/cmdline-ubuntu2604.txt)"
|
||||||
|
kexec -e
|
||||||
1
boot/vram.txt
Normal file
1
boot/vram.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0x20000000
|
||||||
343
build_image.sh
Executable file
343
build_image.sh
Executable file
@@ -0,0 +1,343 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
DISTRO="ubuntu"
|
||||||
|
KERNEL_SRC=""
|
||||||
|
CLEAN=false
|
||||||
|
IMG_SIZE=12000
|
||||||
|
|
||||||
|
MULTI_DISTROS="ubuntu ubuntu2604 arch alpine"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 [--distro <distro>] [--kernel <path>] [--img-size <MB>] [--clean]"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " --distro Distribution to build: ubuntu, ubuntu2604, arch, alpine, all (default: ubuntu)"
|
||||||
|
echo " --kernel Path to kernel source directory (default: auto-clone to work/linux/)"
|
||||||
|
echo " --img-size Disk image size in MB (default: 12000, 32000 for --distro all)"
|
||||||
|
echo " --clean Remove all cached build artifacts and start from scratch"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--distro) DISTRO="$2"; shift 2 ;;
|
||||||
|
--kernel) KERNEL_SRC="$2"; shift 2 ;;
|
||||||
|
--img-size) IMG_SIZE="$2"; shift 2 ;;
|
||||||
|
--clean) CLEAN=true; shift ;;
|
||||||
|
-h|--help) usage ;;
|
||||||
|
*) echo "Unknown option: $1"; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
LINUX_REPO="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git"
|
||||||
|
LINUX_DEFAULT_DIR="$SCRIPT_DIR/work/linux"
|
||||||
|
|
||||||
|
# TODO: switch to https://github.com/ps5-linux/ps5-linux-patches.git once repo is public
|
||||||
|
PATCHES_REPO="git@github.com:ps5-linux/ps5-linux-patches.git"
|
||||||
|
PATCHES_DIR="$SCRIPT_DIR/work/ps5-linux-patches"
|
||||||
|
PATCHES_CONFIG=".config"
|
||||||
|
|
||||||
|
if [ -z "$KERNEL_SRC" ]; then
|
||||||
|
KERNEL_SRC="$LINUX_DEFAULT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
KERNEL_OUT="$SCRIPT_DIR/linux-bin"
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/output"
|
||||||
|
CHROOT_DIR="$SCRIPT_DIR/work/chroot"
|
||||||
|
CACHE_DIR="$SCRIPT_DIR/work/cache"
|
||||||
|
CCACHE_DIR="$SCRIPT_DIR/cache/ccache"
|
||||||
|
LOG_FILE="$SCRIPT_DIR/build.log"
|
||||||
|
DOCKER_NAME="ps5-build-$$"
|
||||||
|
|
||||||
|
if [ "$DISTRO" = "all" ] && [ "$IMG_SIZE" = "12000" ]; then
|
||||||
|
IMG_SIZE=32000
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Signal trap: clean up docker containers and background jobs on exit ---
|
||||||
|
BUILD_PID=""
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
echo ""
|
||||||
|
echo "Interrupted. Cleaning up..."
|
||||||
|
docker kill "$DOCKER_NAME" 2>/dev/null || true
|
||||||
|
[ -n "$BUILD_PID" ] && kill "$BUILD_PID" 2>/dev/null || true
|
||||||
|
wait "$BUILD_PID" 2>/dev/null || true
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
trap cleanup INT TERM
|
||||||
|
|
||||||
|
# --- Clean ---
|
||||||
|
if [ "$CLEAN" = true ]; then
|
||||||
|
echo "Cleaning all build artifacts..."
|
||||||
|
# Build artifacts contain root-owned files from Docker — use a container to remove them
|
||||||
|
for dir in "$SCRIPT_DIR/work" "$KERNEL_OUT" "$SCRIPT_DIR/cache"; do
|
||||||
|
if [ -d "$dir" ]; then
|
||||||
|
docker run --rm --privileged -v "$dir":/clean alpine sh -c 'rm -rf /clean/*'
|
||||||
|
rmdir "$dir" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
rm -rf "$OUTPUT_DIR"
|
||||||
|
echo "Done."
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Auto-detect what can be skipped ---
|
||||||
|
SKIP_KERNEL=false
|
||||||
|
SKIP_CHROOT=false
|
||||||
|
|
||||||
|
# Kernel packages already built?
|
||||||
|
if [ "$DISTRO" = "arch" ]; then
|
||||||
|
ls "$KERNEL_OUT"/*.pkg.tar.zst 1>/dev/null 2>&1 && SKIP_KERNEL=true
|
||||||
|
elif [ "$DISTRO" = "all" ]; then
|
||||||
|
ls "$KERNEL_OUT"/*.deb 1>/dev/null 2>&1 && \
|
||||||
|
ls "$KERNEL_OUT"/*.pkg.tar.zst 1>/dev/null 2>&1 && SKIP_KERNEL=true
|
||||||
|
else
|
||||||
|
ls "$KERNEL_OUT"/*.deb 1>/dev/null 2>&1 && SKIP_KERNEL=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Chroot already populated?
|
||||||
|
if [ "$DISTRO" = "all" ]; then
|
||||||
|
SKIP_CHROOT=true
|
||||||
|
for d in $MULTI_DISTROS; do
|
||||||
|
[ -d "$SCRIPT_DIR/work/chroot-$d/bin" ] || SKIP_CHROOT=false
|
||||||
|
done
|
||||||
|
else
|
||||||
|
[ -d "$CHROOT_DIR/bin" ] && SKIP_CHROOT=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DISTRO" = "arch" ]; then
|
||||||
|
PKG_EXT="pkg.tar.zst"
|
||||||
|
else
|
||||||
|
PKG_EXT="deb"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Build plan summary ---
|
||||||
|
echo ""
|
||||||
|
echo "PS5 Linux Image Builder"
|
||||||
|
echo "======================="
|
||||||
|
echo " Distro: $DISTRO"
|
||||||
|
if [ "$DISTRO" = "all" ]; then
|
||||||
|
echo " ($MULTI_DISTROS)"
|
||||||
|
fi
|
||||||
|
echo " Image size: ${IMG_SIZE}MB"
|
||||||
|
if [ -f "$PATCHES_DIR/.config" ]; then
|
||||||
|
LINUX_BRANCH="v$(grep -m1 "^# Linux/" "$PATCHES_DIR/.config" | grep -oP '\d+\.\d+(\.\d+)?')"
|
||||||
|
echo " Kernel: $LINUX_BRANCH"
|
||||||
|
else
|
||||||
|
echo " Kernel: (will fetch)"
|
||||||
|
fi
|
||||||
|
echo " Kernel src: $KERNEL_SRC"
|
||||||
|
echo ""
|
||||||
|
echo "Stages:"
|
||||||
|
if [ "$SKIP_KERNEL" = true ]; then
|
||||||
|
echo " 1. Kernel cached"
|
||||||
|
else
|
||||||
|
if [ -d "$KERNEL_SRC/.git" ]; then
|
||||||
|
echo " 1. Kernel build (source cached)"
|
||||||
|
else
|
||||||
|
echo " 1. Kernel clone + build"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$SKIP_CHROOT" = true ]; then
|
||||||
|
echo " 2. Root filesystem cached"
|
||||||
|
else
|
||||||
|
echo " 2. Root filesystem build"
|
||||||
|
fi
|
||||||
|
echo " 3. Disk image build"
|
||||||
|
echo ""
|
||||||
|
echo "Logs: $LOG_FILE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Logging + stage runner ---
|
||||||
|
: > "$LOG_FILE"
|
||||||
|
|
||||||
|
SPIN_CHARS='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
|
||||||
|
|
||||||
|
run_stage() {
|
||||||
|
local name="$1"
|
||||||
|
shift
|
||||||
|
local status_msg="$name"
|
||||||
|
local spin_i=0
|
||||||
|
|
||||||
|
# Record log position so we only scan new lines for status updates
|
||||||
|
local log_start
|
||||||
|
log_start=$(wc -l < "$LOG_FILE")
|
||||||
|
|
||||||
|
# Run command in background
|
||||||
|
"$@" >> "$LOG_FILE" 2>&1 &
|
||||||
|
BUILD_PID=$!
|
||||||
|
|
||||||
|
# Spinner loop — pick up === status lines from the log
|
||||||
|
while kill -0 "$BUILD_PID" 2>/dev/null; do
|
||||||
|
if (( spin_i % 10 == 0 )); then
|
||||||
|
local new
|
||||||
|
new=$(tail -n +$((log_start + 1)) "$LOG_FILE" 2>/dev/null \
|
||||||
|
| grep -oP '(?<=^=== ).*(?= ===$)' | tail -1)
|
||||||
|
[ -n "$new" ] && status_msg="$new"
|
||||||
|
fi
|
||||||
|
printf "\r %s %-60s" "${SPIN_CHARS:spin_i%${#SPIN_CHARS}:1}" "$status_msg"
|
||||||
|
spin_i=$((spin_i + 1))
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
local rc=0
|
||||||
|
wait "$BUILD_PID" || rc=$?
|
||||||
|
BUILD_PID=""
|
||||||
|
|
||||||
|
if [ $rc -eq 0 ]; then
|
||||||
|
printf "\r ✓ %-60s\n" "$name"
|
||||||
|
else
|
||||||
|
printf "\r ✗ %-60s\n" "$status_msg"
|
||||||
|
echo ""
|
||||||
|
echo "Build failed at: $status_msg"
|
||||||
|
echo "Logs: $LOG_FILE"
|
||||||
|
echo "Try running with --clean to start fresh."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Setup directories ---
|
||||||
|
mkdir -p "$KERNEL_OUT" "$OUTPUT_DIR" "$CHROOT_DIR" "$CACHE_DIR" "$CCACHE_DIR"
|
||||||
|
if [ "$DISTRO" = "all" ]; then
|
||||||
|
for d in $MULTI_DISTROS; do
|
||||||
|
mkdir -p "$SCRIPT_DIR/work/chroot-$d"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 1: Kernel ---
|
||||||
|
if [ "$SKIP_KERNEL" = true ]; then
|
||||||
|
printf " ✓ %-60s\n" "Kernel packages (cached)"
|
||||||
|
else
|
||||||
|
if [ ! -d "$KERNEL_SRC/.git" ]; then
|
||||||
|
LINUX_TMP_DIR="${LINUX_DEFAULT_DIR}.tmp"
|
||||||
|
rm -rf "$LINUX_TMP_DIR"
|
||||||
|
|
||||||
|
mkdir -p "$PATCHES_DIR"
|
||||||
|
if [ ! -d "$PATCHES_DIR/.git" ]; then
|
||||||
|
run_stage "Clone ps5-linux-patches" \
|
||||||
|
git clone --depth 1 "$PATCHES_REPO" "$PATCHES_DIR"
|
||||||
|
else
|
||||||
|
run_stage "Update ps5-linux-patches" bash -c '
|
||||||
|
git -C "'"$PATCHES_DIR"'" fetch --depth 1 origin
|
||||||
|
git -C "'"$PATCHES_DIR"'" reset --hard FETCH_HEAD'
|
||||||
|
fi
|
||||||
|
LINUX_BRANCH="v$(grep -m1 "^# Linux/" "$PATCHES_DIR/.config" | grep -oP '\d+\.\d+(\.\d+)?')"
|
||||||
|
|
||||||
|
run_stage "Clone kernel $LINUX_BRANCH" \
|
||||||
|
git clone --branch "$LINUX_BRANCH" --depth 1 "$LINUX_REPO" "$LINUX_TMP_DIR"
|
||||||
|
|
||||||
|
run_stage "Apply patches" bash -c '
|
||||||
|
set -e
|
||||||
|
shopt -s nullglob
|
||||||
|
patches=("'"$PATCHES_DIR"'"/*.patch)
|
||||||
|
[ ${#patches[@]} -eq 0 ] && { echo "No .patch files found in '"$PATCHES_DIR"'"; exit 1; }
|
||||||
|
for p in "${patches[@]}"; do
|
||||||
|
echo "Applying $p"
|
||||||
|
git -C "'"$LINUX_TMP_DIR"'" apply --exclude=Makefile "$p"
|
||||||
|
done'
|
||||||
|
|
||||||
|
run_stage "Copy kernel config" \
|
||||||
|
cp "$PATCHES_DIR/$PATCHES_CONFIG" "$LINUX_TMP_DIR/.config"
|
||||||
|
|
||||||
|
mv "$LINUX_TMP_DIR" "$LINUX_DEFAULT_DIR"
|
||||||
|
else
|
||||||
|
printf " ✓ %-60s\n" "Kernel source (cached)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
KERNEL_SRC="$(cd "$KERNEL_SRC" && pwd)"
|
||||||
|
|
||||||
|
rm -f "$KERNEL_OUT"/*.$PKG_EXT
|
||||||
|
|
||||||
|
run_stage "Build kernel builder image" \
|
||||||
|
docker build -t ps5-kernel-builder -f "$SCRIPT_DIR/docker/kernel-builder/Dockerfile" "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
run_stage "Compile kernel" \
|
||||||
|
docker run --rm --name "$DOCKER_NAME" \
|
||||||
|
-v "$KERNEL_SRC":/src \
|
||||||
|
-v "$KERNEL_OUT":/out \
|
||||||
|
-v "$CCACHE_DIR":/ccache \
|
||||||
|
ps5-kernel-builder
|
||||||
|
|
||||||
|
if [ "$DISTRO" = "all" ]; then
|
||||||
|
run_stage "Package kernel (.deb)" \
|
||||||
|
docker run --rm --name "$DOCKER_NAME" \
|
||||||
|
-v "$KERNEL_SRC":/src \
|
||||||
|
-v "$KERNEL_OUT":/out \
|
||||||
|
-v "$CCACHE_DIR":/ccache \
|
||||||
|
ps5-kernel-builder \
|
||||||
|
bash -c 'make -j$(nproc) bindeb-pkg && cp /*.deb /out/'
|
||||||
|
|
||||||
|
run_stage "Build arch packager image" \
|
||||||
|
docker build -t ps5-kernel-packager-arch \
|
||||||
|
-f "$SCRIPT_DIR/docker/kernel-builder-arch/Dockerfile" "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
run_stage "Package kernel (.pkg.tar.zst)" \
|
||||||
|
docker run --rm --name "$DOCKER_NAME" \
|
||||||
|
-v "$KERNEL_OUT":/out \
|
||||||
|
ps5-kernel-packager-arch
|
||||||
|
|
||||||
|
elif [ "$DISTRO" = "arch" ]; then
|
||||||
|
run_stage "Build arch packager image" \
|
||||||
|
docker build -t ps5-kernel-packager-arch \
|
||||||
|
-f "$SCRIPT_DIR/docker/kernel-builder-arch/Dockerfile" "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
run_stage "Package kernel (.pkg.tar.zst)" \
|
||||||
|
docker run --rm --name "$DOCKER_NAME" \
|
||||||
|
-v "$KERNEL_OUT":/out \
|
||||||
|
ps5-kernel-packager-arch
|
||||||
|
else
|
||||||
|
run_stage "Package kernel (.deb)" \
|
||||||
|
docker run --rm --name "$DOCKER_NAME" \
|
||||||
|
-v "$KERNEL_SRC":/src \
|
||||||
|
-v "$KERNEL_OUT":/out \
|
||||||
|
-v "$CCACHE_DIR":/ccache \
|
||||||
|
ps5-kernel-builder \
|
||||||
|
bash -c 'make -j$(nproc) bindeb-pkg && cp /*.deb /out/'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 2: Build distribution image ---
|
||||||
|
run_stage "Build image builder image" \
|
||||||
|
docker build -t ps5-image-builder -f "$SCRIPT_DIR/docker/image-builder/Dockerfile" "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
if [ "$DISTRO" = "all" ]; then
|
||||||
|
DOCKER_ARGS=(
|
||||||
|
docker run --rm --privileged --name "$DOCKER_NAME"
|
||||||
|
--entrypoint /entrypoint-multi.sh
|
||||||
|
-v "$SCRIPT_DIR":/repo:ro
|
||||||
|
-v "$KERNEL_OUT":/kernel-debs:ro
|
||||||
|
-v "$OUTPUT_DIR":/output
|
||||||
|
-v "$CACHE_DIR":/build/cache
|
||||||
|
-e IMG_SIZE="$IMG_SIZE"
|
||||||
|
-e SKIP_CHROOT="$SKIP_CHROOT"
|
||||||
|
-e "DISTROS=$MULTI_DISTROS"
|
||||||
|
)
|
||||||
|
for d in $MULTI_DISTROS; do
|
||||||
|
DOCKER_ARGS+=(-v "$SCRIPT_DIR/work/chroot-$d:/build/chroot-$d")
|
||||||
|
done
|
||||||
|
DOCKER_ARGS+=(ps5-image-builder)
|
||||||
|
|
||||||
|
run_stage "Build multi-distro image (${IMG_SIZE}MB)" "${DOCKER_ARGS[@]}"
|
||||||
|
|
||||||
|
IMG_PATH="$OUTPUT_DIR/ps5-multi.img"
|
||||||
|
else
|
||||||
|
run_stage "Build $DISTRO image (${IMG_SIZE}MB)" \
|
||||||
|
docker run --rm --privileged --name "$DOCKER_NAME" \
|
||||||
|
-v "$SCRIPT_DIR":/repo:ro \
|
||||||
|
-v "$KERNEL_OUT":/kernel-debs:ro \
|
||||||
|
-v "$OUTPUT_DIR":/output \
|
||||||
|
-v "$CHROOT_DIR":/build/chroot \
|
||||||
|
-v "$CACHE_DIR":/build/cache \
|
||||||
|
-e DISTRO="$DISTRO" \
|
||||||
|
-e IMG_SIZE="$IMG_SIZE" \
|
||||||
|
-e SKIP_CHROOT="$SKIP_CHROOT" \
|
||||||
|
ps5-image-builder
|
||||||
|
|
||||||
|
IMG_PATH="$OUTPUT_DIR/ps5-${DISTRO}.img"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done! Image: $IMG_PATH"
|
||||||
|
echo "Flash: sudo dd if=$IMG_PATH of=/dev/sdX bs=4M status=progress"
|
||||||
13
distros/alpine/grow-rootfs
Normal file
13
distros/alpine/grow-rootfs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Grows the root partition and filesystem to fill the disk.
|
||||||
|
# Runs once on first boot, then disables itself.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ROOT_DEV=$(findmnt -no SOURCE /)
|
||||||
|
DISK=$(lsblk -ndo PKNAME "$ROOT_DEV")
|
||||||
|
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition)
|
||||||
|
|
||||||
|
growpart "/dev/$DISK" "$PART_NUM" || true
|
||||||
|
resize2fs "$ROOT_DEV" || true
|
||||||
|
|
||||||
|
rc-update del grow-rootfs default
|
||||||
13
distros/alpine/grow-rootfs.openrc
Normal file
13
distros/alpine/grow-rootfs.openrc
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
description="Grow root filesystem to fill disk"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need localmount
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
ebegin "Growing root filesystem"
|
||||||
|
/usr/local/sbin/grow-rootfs
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
175
distros/alpine/image.yaml
Normal file
175
distros/alpine/image.yaml
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
image:
|
||||||
|
name: ps5-alpine
|
||||||
|
distribution: alpine
|
||||||
|
release: "3.21"
|
||||||
|
description: Alpine Linux with Weston
|
||||||
|
architecture: x86_64
|
||||||
|
|
||||||
|
source:
|
||||||
|
downloader: alpinelinux-http
|
||||||
|
url: https://dl-cdn.alpinelinux.org/alpine
|
||||||
|
skip_verification: true
|
||||||
|
|
||||||
|
packages:
|
||||||
|
manager: apk
|
||||||
|
update: true
|
||||||
|
cleanup: true
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: repositories
|
||||||
|
url: |-
|
||||||
|
https://dl-cdn.alpinelinux.org/alpine/v3.21/main
|
||||||
|
https://dl-cdn.alpinelinux.org/alpine/v3.21/community
|
||||||
|
|
||||||
|
sets:
|
||||||
|
- packages:
|
||||||
|
# Wayland compositor + basics
|
||||||
|
- weston
|
||||||
|
- weston-shell-desktop
|
||||||
|
- weston-backend-drm
|
||||||
|
- weston-xwayland
|
||||||
|
- weston-terminal
|
||||||
|
- foot
|
||||||
|
- xwayland
|
||||||
|
|
||||||
|
# Wayland support
|
||||||
|
- dbus
|
||||||
|
- eudev
|
||||||
|
- elogind
|
||||||
|
- polkit-elogind
|
||||||
|
- seatd
|
||||||
|
|
||||||
|
# Audio / media
|
||||||
|
- pipewire
|
||||||
|
- wireplumber
|
||||||
|
- pipewire-pulse
|
||||||
|
|
||||||
|
# Display / GPU
|
||||||
|
- mesa-gbm
|
||||||
|
- mesa-egl
|
||||||
|
- mesa-gl
|
||||||
|
- mesa-dri-gallium
|
||||||
|
- mesa-va-gallium
|
||||||
|
- mesa-vulkan-ati
|
||||||
|
- libinput
|
||||||
|
- xkeyboard-config
|
||||||
|
|
||||||
|
# Networking
|
||||||
|
- networkmanager
|
||||||
|
- networkmanager-wifi
|
||||||
|
- wpa_supplicant
|
||||||
|
- linux-firmware
|
||||||
|
- linux-firmware-amdgpu
|
||||||
|
- xf86-video-amdgpu
|
||||||
|
|
||||||
|
# System
|
||||||
|
- openrc
|
||||||
|
- openssh
|
||||||
|
- sudo
|
||||||
|
- shadow
|
||||||
|
- nano
|
||||||
|
- bash
|
||||||
|
- coreutils
|
||||||
|
- util-linux
|
||||||
|
- e2fsprogs
|
||||||
|
- e2fsprogs-extra
|
||||||
|
- cloud-utils-growpart
|
||||||
|
- mkinitfs
|
||||||
|
- kmod
|
||||||
|
- kexec-tools
|
||||||
|
- syslog-ng
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
- font-ubuntu
|
||||||
|
- font-liberation
|
||||||
|
- font-dejavu
|
||||||
|
- fontconfig
|
||||||
|
action: install
|
||||||
|
|
||||||
|
files:
|
||||||
|
- path: /etc/hostname
|
||||||
|
generator: hostname
|
||||||
|
|
||||||
|
- path: /etc/hosts
|
||||||
|
generator: hosts
|
||||||
|
|
||||||
|
- path: /etc/kernel/postinst.d/zz-update-boot
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/zz-update-boot
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/fstab
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/fstab
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/init.d/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs.openrc
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- trigger: post-unpack
|
||||||
|
action: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
# Retry up to 10 times on transient network errors
|
||||||
|
echo "retries = 10" >> /etc/apk/apk.conf 2>/dev/null || true
|
||||||
|
# dbus post-install chokes on an empty machine-id
|
||||||
|
rm -f /etc/machine-id
|
||||||
|
|
||||||
|
|
||||||
|
- trigger: post-packages
|
||||||
|
action: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
fc-cache -f -v
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
rc-update add syslog-ng default
|
||||||
|
rc-update add udev sysinit
|
||||||
|
rc-update add udev-trigger sysinit
|
||||||
|
rc-update add udev-settle sysinit
|
||||||
|
rc-update add sshd default
|
||||||
|
rc-update add dbus default
|
||||||
|
rc-update add elogind default
|
||||||
|
rc-update add seatd default
|
||||||
|
rc-update add networkmanager default
|
||||||
|
|
||||||
|
- trigger: post-files
|
||||||
|
action: |-
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
rc-update add grow-rootfs default
|
||||||
|
|
||||||
|
# Create default user (ps5/ps5) in groups needed for Wayland/input
|
||||||
|
addgroup ps5
|
||||||
|
adduser -D -s /bin/bash -G ps5 ps5
|
||||||
|
echo "ps5:ps5" | chpasswd
|
||||||
|
addgroup ps5 wheel
|
||||||
|
addgroup ps5 video
|
||||||
|
addgroup ps5 input
|
||||||
|
addgroup ps5 seat
|
||||||
|
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel
|
||||||
|
|
||||||
|
# Auto-start weston on tty1 login
|
||||||
|
{
|
||||||
|
echo 'if [ "$(tty)" = "/dev/tty1" ] && [ -z "$WAYLAND_DISPLAY" ]; then'
|
||||||
|
echo ' export XDG_RUNTIME_DIR="/tmp/xdg-runtime-$(id -u)"'
|
||||||
|
echo ' mkdir -p "$XDG_RUNTIME_DIR"'
|
||||||
|
echo ' chmod 700 "$XDG_RUNTIME_DIR"'
|
||||||
|
echo ' exec weston --backend=drm-backend.so'
|
||||||
|
echo 'fi'
|
||||||
|
} >> /home/ps5/.bash_profile
|
||||||
|
chown -R ps5:ps5 /home/ps5
|
||||||
|
|
||||||
|
mkdir -p /boot/efi
|
||||||
|
|
||||||
|
mappings:
|
||||||
|
architecture_map: alpinelinux
|
||||||
67
distros/arch/first-boot-setup
Normal file
67
distros/arch/first-boot-setup
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# First-boot interactive setup: creates user account.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
clear
|
||||||
|
echo "========================================="
|
||||||
|
echo " Welcome! Let's set up your account."
|
||||||
|
echo "========================================="
|
||||||
|
echo
|
||||||
|
|
||||||
|
# --- Username ---
|
||||||
|
while true; do
|
||||||
|
read -rp "Username: " USERNAME
|
||||||
|
if [[ -z "$USERNAME" ]]; then
|
||||||
|
echo "Username cannot be empty."
|
||||||
|
elif ! [[ "$USERNAME" =~ ^[a-z_][a-z0-9_-]*$ ]]; then
|
||||||
|
echo "Invalid username. Use lowercase letters, digits, hyphens, underscores."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- Password ---
|
||||||
|
while true; do
|
||||||
|
read -rsp "Password: " PASSWORD; echo
|
||||||
|
read -rsp "Confirm: " PASSWORD2; echo
|
||||||
|
if [[ -z "$PASSWORD" ]]; then
|
||||||
|
echo "Password cannot be empty."
|
||||||
|
elif [[ "$PASSWORD" != "$PASSWORD2" ]]; then
|
||||||
|
echo "Passwords do not match. Try again."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Creating user $USERNAME ..."
|
||||||
|
|
||||||
|
useradd -m -G wheel,seat -s /bin/bash "$USERNAME"
|
||||||
|
echo "$USERNAME:$PASSWORD" | chpasswd
|
||||||
|
|
||||||
|
# Sudoers
|
||||||
|
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel
|
||||||
|
|
||||||
|
# Sway config
|
||||||
|
mkdir -p "/home/$USERNAME/.config/sway"
|
||||||
|
cp /etc/sway/config "/home/$USERNAME/.config/sway/config"
|
||||||
|
|
||||||
|
# Auto-start sway on tty1 login
|
||||||
|
cat >> "/home/$USERNAME/.bash_profile" << 'EOF'
|
||||||
|
if [ "$(tty)" = "/dev/tty1" ] && [ -z "$WAYLAND_DISPLAY" ]; then
|
||||||
|
exec sway
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chown -R "$USERNAME:$USERNAME" "/home/$USERNAME"
|
||||||
|
|
||||||
|
# Remove the getty override so tty1 returns to normal login prompt.
|
||||||
|
# When this script exits, systemd's Restart=always on getty@tty1
|
||||||
|
# restarts getty with the original agetty config.
|
||||||
|
rm -f /etc/systemd/system/getty@tty1.service.d/first-boot.conf
|
||||||
|
rmdir /etc/systemd/system/getty@tty1.service.d 2>/dev/null || true
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Done!"
|
||||||
|
sleep 1
|
||||||
13
distros/arch/grow-rootfs
Normal file
13
distros/arch/grow-rootfs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Grows the root partition and filesystem to fill the disk.
|
||||||
|
# Runs once on first boot, then disables itself.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ROOT_DEV=$(findmnt -no SOURCE /)
|
||||||
|
DISK="/dev/$(lsblk -ndo PKNAME "$ROOT_DEV")"
|
||||||
|
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition)
|
||||||
|
|
||||||
|
parted -s "$DISK" resizepart "$PART_NUM" 100%
|
||||||
|
resize2fs "$ROOT_DEV"
|
||||||
|
|
||||||
|
systemctl disable grow-rootfs.service
|
||||||
13
distros/arch/grow-rootfs.service
Normal file
13
distros/arch/grow-rootfs.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Grow root filesystem to fill disk
|
||||||
|
DefaultDependencies=no
|
||||||
|
Before=local-fs.target
|
||||||
|
After=local-fs-pre.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/grow-rootfs
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=local-fs.target
|
||||||
189
distros/arch/image.yaml
Normal file
189
distros/arch/image.yaml
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
image:
|
||||||
|
name: ps5-arch
|
||||||
|
distribution: archlinux
|
||||||
|
description: Arch Linux with Sway desktop
|
||||||
|
architecture: x86_64
|
||||||
|
|
||||||
|
source:
|
||||||
|
downloader: archlinux-http
|
||||||
|
url: https://geo.mirror.pkgbuild.com/iso
|
||||||
|
skip_verification: true
|
||||||
|
|
||||||
|
packages:
|
||||||
|
manager: pacman
|
||||||
|
update: true
|
||||||
|
cleanup: true
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: mirrorlist
|
||||||
|
url: |-
|
||||||
|
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
|
||||||
|
|
||||||
|
sets:
|
||||||
|
- packages:
|
||||||
|
# Sway / Wayland desktop
|
||||||
|
- sway
|
||||||
|
- swaylock
|
||||||
|
- swayidle
|
||||||
|
- swaybg
|
||||||
|
- foot
|
||||||
|
- wofi
|
||||||
|
- waybar
|
||||||
|
- mako
|
||||||
|
- grim
|
||||||
|
- slurp
|
||||||
|
- wl-clipboard
|
||||||
|
- xorg-xwayland
|
||||||
|
|
||||||
|
# Audio / media
|
||||||
|
- pipewire
|
||||||
|
- wireplumber
|
||||||
|
- pipewire-pulse
|
||||||
|
|
||||||
|
# Display / GPU (AMD)
|
||||||
|
- mesa
|
||||||
|
- vulkan-radeon
|
||||||
|
- libva-mesa-driver
|
||||||
|
- xf86-video-amdgpu
|
||||||
|
- libinput
|
||||||
|
- xkeyboard-config
|
||||||
|
|
||||||
|
# File manager / apps
|
||||||
|
- thunar
|
||||||
|
- firefox
|
||||||
|
|
||||||
|
# Networking
|
||||||
|
- networkmanager
|
||||||
|
- linux-firmware
|
||||||
|
|
||||||
|
# System
|
||||||
|
- base
|
||||||
|
- openssh
|
||||||
|
- sudo
|
||||||
|
- nano
|
||||||
|
- bash
|
||||||
|
- mkinitcpio
|
||||||
|
- kmod
|
||||||
|
- e2fsprogs
|
||||||
|
- parted
|
||||||
|
- polkit
|
||||||
|
- dbus
|
||||||
|
- seatd
|
||||||
|
- kexec-tools
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
- ttf-dejavu
|
||||||
|
- ttf-liberation
|
||||||
|
- noto-fonts
|
||||||
|
- ttf-font-awesome
|
||||||
|
- fontconfig
|
||||||
|
action: install
|
||||||
|
|
||||||
|
files:
|
||||||
|
- path: /etc/hostname
|
||||||
|
generator: hostname
|
||||||
|
|
||||||
|
- path: /etc/hosts
|
||||||
|
generator: hosts
|
||||||
|
|
||||||
|
- path: /etc/kernel/postinst.d/zz-update-boot
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/zz-update-boot
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/fstab
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/fstab
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/grow-rootfs.service
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs.service
|
||||||
|
|
||||||
|
- path: /opt/pkgs/
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/pkgs
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/first-boot-setup
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/first-boot-setup
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- trigger: post-unpack
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
# Initialise pacman keyring
|
||||||
|
pacman-key --init
|
||||||
|
pacman-key --populate archlinux
|
||||||
|
|
||||||
|
# Fetch up-to-date HTTPS mirrors ranked by score, force-refresh package DBs
|
||||||
|
curl -fsSL "https://archlinux.org/mirrorlist/?country=all&protocol=https&use_mirror_status=on" \
|
||||||
|
| sed 's/^#Server/Server/' > /etc/pacman.d/mirrorlist
|
||||||
|
pacman -Syy --noconfirm
|
||||||
|
|
||||||
|
|
||||||
|
- trigger: post-packages
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
fc-cache -f -v
|
||||||
|
|
||||||
|
# SSH
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
systemctl enable sshd
|
||||||
|
systemctl enable NetworkManager
|
||||||
|
systemctl enable seatd
|
||||||
|
systemctl enable pipewire pipewire-pulse wireplumber || true
|
||||||
|
|
||||||
|
- trigger: post-files
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
systemctl enable grow-rootfs.service
|
||||||
|
|
||||||
|
# Create default user (ps5/ps5)
|
||||||
|
useradd -m -G wheel,seat -s /bin/bash ps5
|
||||||
|
echo "ps5:ps5" | chpasswd
|
||||||
|
echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel
|
||||||
|
|
||||||
|
# Sway config + auto-start on tty1 login
|
||||||
|
mkdir -p /home/ps5/.config/sway
|
||||||
|
cp /etc/sway/config /home/ps5/.config/sway/config
|
||||||
|
{
|
||||||
|
echo 'if [ "$(tty)" = "/dev/tty1" ] && [ -z "$WAYLAND_DISPLAY" ]; then'
|
||||||
|
echo ' export XDG_RUNTIME_DIR="/tmp/xdg-runtime-$(id -u)"'
|
||||||
|
echo ' mkdir -p "$XDG_RUNTIME_DIR"'
|
||||||
|
echo ' chmod 700 "$XDG_RUNTIME_DIR"'
|
||||||
|
echo ' exec sway'
|
||||||
|
echo 'fi'
|
||||||
|
} >> /home/ps5/.bash_profile
|
||||||
|
chown -R ps5:ps5 /home/ps5
|
||||||
|
|
||||||
|
# Ensure vconsole.conf exists (needed by mkinitcpio sd-vconsole hook)
|
||||||
|
[ -f /etc/vconsole.conf ] || echo "KEYMAP=us" > /etc/vconsole.conf
|
||||||
|
|
||||||
|
# Drop autodetect — it detects the build host's hardware, not the target.
|
||||||
|
# This produces a larger but hardware-independent initramfs.
|
||||||
|
sed -i 's/ autodetect//' /etc/mkinitcpio.conf
|
||||||
|
|
||||||
|
# Install custom kernel packages
|
||||||
|
if ls /opt/pkgs/*.pkg.tar.zst 1>/dev/null 2>&1; then
|
||||||
|
pacman -U --noconfirm /opt/pkgs/*.pkg.tar.zst
|
||||||
|
rm -rf /opt/pkgs
|
||||||
|
KVER=$(ls -1t /lib/modules | head -1)
|
||||||
|
# mkinitcpio may warn about missing optional modules; don't fail on those
|
||||||
|
mkinitcpio -k "$KVER" -g "/boot/initrd.img-$KVER" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p /boot/efi
|
||||||
|
/etc/kernel/postinst.d/zz-update-boot
|
||||||
|
|
||||||
|
mappings:
|
||||||
|
architecture_map: archlinux
|
||||||
2
distros/shared/fstab
Normal file
2
distros/shared/fstab
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
LABEL=__DISTRO__ / ext4 rw,relatime 0 1
|
||||||
|
LABEL=boot /boot/efi vfat rw,relatime 0 2
|
||||||
10
distros/shared/zz-update-boot
Executable file
10
distros/shared/zz-update-boot
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Copies the latest kernel + initrd to the EFI partition.
|
||||||
|
# Runs on every kernel install/update.
|
||||||
|
set -e
|
||||||
|
BOOT_PART="/boot/efi"
|
||||||
|
KVER="$1"
|
||||||
|
[ -z "$KVER" ] && KVER="$(ls -1t /lib/modules | head -1)"
|
||||||
|
cp "/boot/vmlinuz-$KVER" "$BOOT_PART/bzImage"
|
||||||
|
cp "/boot/initrd.img-$KVER" "$BOOT_PART/initrd.img"
|
||||||
|
echo ">> Kernel $KVER deployed to $BOOT_PART"
|
||||||
20
distros/shared/zz-update-boot-multi
Normal file
20
distros/shared/zz-update-boot-multi
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Multi-distro boot hook: copies kernel + initrd to the EFI partition
|
||||||
|
# using distro-specific names. Reads /etc/ps5-distro for current distro.
|
||||||
|
set -e
|
||||||
|
BOOT_PART="/boot/efi"
|
||||||
|
KVER="$1"
|
||||||
|
[ -z "$KVER" ] && KVER="$(ls -1t /lib/modules | head -1)"
|
||||||
|
|
||||||
|
DISTRO="unknown"
|
||||||
|
[ -f /etc/ps5-distro ] && DISTRO="$(cat /etc/ps5-distro)"
|
||||||
|
|
||||||
|
cp "/boot/vmlinuz-$KVER" "$BOOT_PART/bzImage"
|
||||||
|
cp "/boot/initrd.img-$KVER" "$BOOT_PART/initrd-${DISTRO}.img"
|
||||||
|
|
||||||
|
# Ubuntu is default boot — also update the generic initrd.img
|
||||||
|
if [ "$DISTRO" = "ubuntu" ]; then
|
||||||
|
cp "/boot/initrd.img-$KVER" "$BOOT_PART/initrd.img"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> Kernel $KVER deployed to $BOOT_PART (distro=$DISTRO)"
|
||||||
13
distros/ubuntu/grow-rootfs
Normal file
13
distros/ubuntu/grow-rootfs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Grows the root partition and filesystem to fill the disk.
|
||||||
|
# Runs once on first boot, then disables itself.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ROOT_DEV=$(findmnt -no SOURCE /)
|
||||||
|
DISK=$(lsblk -ndo PKNAME "$ROOT_DEV")
|
||||||
|
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition)
|
||||||
|
|
||||||
|
growpart "/dev/$DISK" "$PART_NUM" || true
|
||||||
|
resize2fs "$ROOT_DEV" || true
|
||||||
|
|
||||||
|
systemctl disable grow-rootfs.service
|
||||||
13
distros/ubuntu/grow-rootfs.service
Normal file
13
distros/ubuntu/grow-rootfs.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Grow root filesystem to fill disk
|
||||||
|
DefaultDependencies=no
|
||||||
|
Before=local-fs.target
|
||||||
|
After=local-fs-pre.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/grow-rootfs
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=local-fs.target
|
||||||
137
distros/ubuntu/image-minimal.yaml
Normal file
137
distros/ubuntu/image-minimal.yaml
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
image:
|
||||||
|
name: ps5-ubuntu-minimal
|
||||||
|
distribution: ubuntu
|
||||||
|
release: noble
|
||||||
|
description: Ubuntu Noble minimal (for testing)
|
||||||
|
architecture: x86_64
|
||||||
|
|
||||||
|
source:
|
||||||
|
downloader: debootstrap
|
||||||
|
url: http://archive.ubuntu.com/ubuntu
|
||||||
|
variant: minbase
|
||||||
|
keyserver: keyserver.ubuntu.com
|
||||||
|
keys:
|
||||||
|
- 0x790BC7277767219C42C86F933B4FE6ACC0B21F32
|
||||||
|
- 0xf6ecb3762474eda9d21b7022871920d1991bc93c
|
||||||
|
|
||||||
|
packages:
|
||||||
|
manager: apt
|
||||||
|
update: true
|
||||||
|
cleanup: true
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: sources.list
|
||||||
|
url: |-
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }} main restricted universe multiverse
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }}-updates main restricted universe multiverse
|
||||||
|
deb http://security.ubuntu.com/ubuntu {{ image.release }}-security main restricted universe multiverse
|
||||||
|
architectures:
|
||||||
|
- amd64
|
||||||
|
|
||||||
|
sets:
|
||||||
|
- packages:
|
||||||
|
- ubuntu-desktop-minimal
|
||||||
|
- firefox
|
||||||
|
- openssh-server
|
||||||
|
- network-manager
|
||||||
|
- linux-firmware
|
||||||
|
- initramfs-tools
|
||||||
|
- kmod
|
||||||
|
- sudo
|
||||||
|
- nano
|
||||||
|
- cloud-guest-utils
|
||||||
|
- kexec-tools
|
||||||
|
action: install
|
||||||
|
|
||||||
|
files:
|
||||||
|
- path: /etc/hostname
|
||||||
|
generator: hostname
|
||||||
|
|
||||||
|
- path: /etc/hosts
|
||||||
|
generator: hosts
|
||||||
|
|
||||||
|
- path: /etc/machine-id
|
||||||
|
generator: dump
|
||||||
|
|
||||||
|
- path: /etc/NetworkManager/conf.d/dns.conf
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/nm-dns.conf
|
||||||
|
|
||||||
|
- path: /etc/kernel/postinst.d/zz-update-boot
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/zz-update-boot
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/fstab
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/fstab
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/grow-rootfs.service
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs.service
|
||||||
|
|
||||||
|
- path: /opt/debs/
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/debs
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- trigger: post-unpack
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
# Install gnupg + dirmngr so we can fetch the Mozilla PPA key
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y gnupg dirmngr curl
|
||||||
|
|
||||||
|
# Import Mozilla PPA signing key and add repository
|
||||||
|
mkdir -p /root/.gnupg /etc/apt/keyrings
|
||||||
|
chmod 700 /root/.gnupg
|
||||||
|
curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x0AB215679C571D1C8325275B9BDB3D89CE49EC21" | \
|
||||||
|
gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/keyrings/mozillateam.gpg --import
|
||||||
|
chmod 644 /etc/apt/keyrings/mozillateam.gpg
|
||||||
|
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/mozillateam.gpg] http://ppa.launchpad.net/mozillateam/ppa/ubuntu noble main" \
|
||||||
|
> /etc/apt/sources.list.d/mozilla-ppa.list
|
||||||
|
|
||||||
|
# Pin PPA firefox over snap transitional package
|
||||||
|
cat > /etc/apt/preferences.d/mozilla-ppa << 'PINEOF'
|
||||||
|
Package: firefox*
|
||||||
|
Pin: release o=LP-PPA-mozillateam
|
||||||
|
Pin-Priority: 1001
|
||||||
|
PINEOF
|
||||||
|
|
||||||
|
- trigger: post-update
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
passwd -l root
|
||||||
|
|
||||||
|
- trigger: post-packages
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
# SSH — password auth only, root login via sudo
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
systemctl enable ssh
|
||||||
|
|
||||||
|
- trigger: post-files
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
systemctl enable grow-rootfs.service
|
||||||
|
systemctl enable systemd-resolved
|
||||||
|
mkdir -p /boot/efi
|
||||||
|
dpkg -i /opt/debs/*.deb
|
||||||
|
rm -rf /opt/debs
|
||||||
|
KVER=$(ls -1t /lib/modules | head -1)
|
||||||
|
update-initramfs -c -k "$KVER"
|
||||||
|
/etc/kernel/postinst.d/zz-update-boot "$KVER"
|
||||||
|
|
||||||
|
mappings:
|
||||||
|
architecture_map: debian
|
||||||
148
distros/ubuntu/image.yaml
Normal file
148
distros/ubuntu/image.yaml
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
image:
|
||||||
|
name: ps5-ubuntu
|
||||||
|
distribution: ubuntu
|
||||||
|
release: noble
|
||||||
|
description: Ubuntu Noble desktop
|
||||||
|
architecture: x86_64
|
||||||
|
|
||||||
|
source:
|
||||||
|
downloader: debootstrap
|
||||||
|
url: http://archive.ubuntu.com/ubuntu
|
||||||
|
variant: minbase
|
||||||
|
keyserver: keyserver.ubuntu.com
|
||||||
|
keys:
|
||||||
|
- 0x790BC7277767219C42C86F933B4FE6ACC0B21F32
|
||||||
|
- 0xf6ecb3762474eda9d21b7022871920d1991bc93c
|
||||||
|
|
||||||
|
packages:
|
||||||
|
manager: apt
|
||||||
|
update: true
|
||||||
|
cleanup: true
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: sources.list
|
||||||
|
url: |-
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }} main restricted universe multiverse
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }}-updates main restricted universe multiverse
|
||||||
|
deb http://security.ubuntu.com/ubuntu {{ image.release }}-security main restricted universe multiverse
|
||||||
|
architectures:
|
||||||
|
- amd64
|
||||||
|
|
||||||
|
sets:
|
||||||
|
- packages:
|
||||||
|
- ubuntu-desktop
|
||||||
|
- firefox
|
||||||
|
- openssh-server
|
||||||
|
- build-essential
|
||||||
|
- network-manager
|
||||||
|
- linux-firmware
|
||||||
|
- initramfs-tools
|
||||||
|
- kmod
|
||||||
|
- sudo
|
||||||
|
- nano
|
||||||
|
- cloud-guest-utils
|
||||||
|
- kexec-tools
|
||||||
|
- fonts-ubuntu
|
||||||
|
- fonts-liberation
|
||||||
|
- fonts-dejavu
|
||||||
|
- fontconfig
|
||||||
|
action: install
|
||||||
|
|
||||||
|
files:
|
||||||
|
- path: /etc/hostname
|
||||||
|
generator: hostname
|
||||||
|
|
||||||
|
- path: /etc/hosts
|
||||||
|
generator: hosts
|
||||||
|
|
||||||
|
- path: /etc/machine-id
|
||||||
|
generator: dump
|
||||||
|
|
||||||
|
- path: /etc/NetworkManager/conf.d/dns.conf
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/nm-dns.conf
|
||||||
|
|
||||||
|
- path: /etc/kernel/postinst.d/zz-update-boot
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/zz-update-boot
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/fstab
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/fstab
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/grow-rootfs.service
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs.service
|
||||||
|
|
||||||
|
- path: /opt/debs/
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/debs
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- trigger: post-unpack
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
# Clear stale apt lists to avoid InRelease split errors after debootstrap
|
||||||
|
rm -f /var/lib/apt/lists/*InRelease /var/lib/apt/lists/*Release /var/lib/apt/lists/*Packages* || true
|
||||||
|
# Retry up to 10 times on transient 429/network errors
|
||||||
|
echo 'Acquire::Retries "10";' > /etc/apt/apt.conf.d/80retry
|
||||||
|
# Install gnupg + dirmngr so we can fetch the Mozilla PPA key
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y gnupg dirmngr curl
|
||||||
|
|
||||||
|
# Import Mozilla PPA signing key and add repository
|
||||||
|
mkdir -p /root/.gnupg /etc/apt/keyrings
|
||||||
|
chmod 700 /root/.gnupg
|
||||||
|
curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x0AB215679C571D1C8325275B9BDB3D89CE49EC21" | \
|
||||||
|
gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/keyrings/mozillateam.gpg --import
|
||||||
|
chmod 644 /etc/apt/keyrings/mozillateam.gpg
|
||||||
|
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/mozillateam.gpg] http://ppa.launchpad.net/mozillateam/ppa/ubuntu noble main" \
|
||||||
|
> /etc/apt/sources.list.d/mozilla-ppa.list
|
||||||
|
|
||||||
|
# Pin PPA firefox over snap transitional package
|
||||||
|
cat > /etc/apt/preferences.d/mozilla-ppa << 'PINEOF'
|
||||||
|
Package: firefox*
|
||||||
|
Pin: release o=LP-PPA-mozillateam
|
||||||
|
Pin-Priority: 1001
|
||||||
|
PINEOF
|
||||||
|
|
||||||
|
- trigger: post-update
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
passwd -l root
|
||||||
|
|
||||||
|
- trigger: post-packages
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
fc-cache -f -v
|
||||||
|
|
||||||
|
# SSH — password auth only, root login via sudo
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
systemctl enable ssh
|
||||||
|
|
||||||
|
- trigger: post-files
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
systemctl enable grow-rootfs.service
|
||||||
|
systemctl enable systemd-resolved
|
||||||
|
mkdir -p /boot/efi
|
||||||
|
dpkg -i /opt/debs/*.deb
|
||||||
|
rm -rf /opt/debs
|
||||||
|
KVER=$(ls -1t /lib/modules | head -1)
|
||||||
|
update-initramfs -c -k "$KVER"
|
||||||
|
/etc/kernel/postinst.d/zz-update-boot "$KVER"
|
||||||
|
|
||||||
|
mappings:
|
||||||
|
architecture_map: debian
|
||||||
2
distros/ubuntu/nm-dns.conf
Normal file
2
distros/ubuntu/nm-dns.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[main]
|
||||||
|
dns=systemd-resolved
|
||||||
13
distros/ubuntu2604/grow-rootfs
Normal file
13
distros/ubuntu2604/grow-rootfs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Grows the root partition and filesystem to fill the disk.
|
||||||
|
# Runs once on first boot, then disables itself.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ROOT_DEV=$(findmnt -no SOURCE /)
|
||||||
|
DISK=$(lsblk -ndo PKNAME "$ROOT_DEV")
|
||||||
|
PART_NUM=$(cat /sys/class/block/$(basename "$ROOT_DEV")/partition)
|
||||||
|
|
||||||
|
growpart "/dev/$DISK" "$PART_NUM" || true
|
||||||
|
resize2fs "$ROOT_DEV" || true
|
||||||
|
|
||||||
|
systemctl disable grow-rootfs.service
|
||||||
13
distros/ubuntu2604/grow-rootfs.service
Normal file
13
distros/ubuntu2604/grow-rootfs.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Grow root filesystem to fill disk
|
||||||
|
DefaultDependencies=no
|
||||||
|
Before=local-fs.target
|
||||||
|
After=local-fs-pre.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/sbin/grow-rootfs
|
||||||
|
RemainAfterExit=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=local-fs.target
|
||||||
131
distros/ubuntu2604/image.yaml
Normal file
131
distros/ubuntu2604/image.yaml
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
image:
|
||||||
|
name: ps5-ubuntu2604
|
||||||
|
distribution: ubuntu
|
||||||
|
release: resolute
|
||||||
|
description: Ubuntu Resolute desktop
|
||||||
|
architecture: x86_64
|
||||||
|
|
||||||
|
source:
|
||||||
|
downloader: debootstrap
|
||||||
|
url: http://archive.ubuntu.com/ubuntu
|
||||||
|
variant: minbase
|
||||||
|
keyserver: keyserver.ubuntu.com
|
||||||
|
keys:
|
||||||
|
- 0x790BC7277767219C42C86F933B4FE6ACC0B21F32
|
||||||
|
- 0xf6ecb3762474eda9d21b7022871920d1991bc93c
|
||||||
|
|
||||||
|
packages:
|
||||||
|
manager: apt
|
||||||
|
update: true
|
||||||
|
cleanup: true
|
||||||
|
|
||||||
|
repositories:
|
||||||
|
- name: sources.list
|
||||||
|
url: |-
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }} main restricted universe multiverse
|
||||||
|
deb http://archive.ubuntu.com/ubuntu {{ image.release }}-updates main restricted universe multiverse
|
||||||
|
deb http://security.ubuntu.com/ubuntu {{ image.release }}-security main restricted universe multiverse
|
||||||
|
architectures:
|
||||||
|
- amd64
|
||||||
|
|
||||||
|
sets:
|
||||||
|
- packages:
|
||||||
|
- ubuntu-desktop
|
||||||
|
- firefox
|
||||||
|
- openssh-server
|
||||||
|
- build-essential
|
||||||
|
- network-manager
|
||||||
|
- linux-firmware
|
||||||
|
- initramfs-tools
|
||||||
|
- kmod
|
||||||
|
- sudo
|
||||||
|
- nano
|
||||||
|
- cloud-guest-utils
|
||||||
|
- kexec-tools
|
||||||
|
- fonts-ubuntu
|
||||||
|
- fonts-liberation
|
||||||
|
- fonts-dejavu
|
||||||
|
- fontconfig
|
||||||
|
action: install
|
||||||
|
|
||||||
|
files:
|
||||||
|
- path: /etc/hostname
|
||||||
|
generator: hostname
|
||||||
|
|
||||||
|
- path: /etc/hosts
|
||||||
|
generator: hosts
|
||||||
|
|
||||||
|
- path: /etc/machine-id
|
||||||
|
generator: dump
|
||||||
|
|
||||||
|
- path: /etc/NetworkManager/conf.d/dns.conf
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/nm-dns.conf
|
||||||
|
|
||||||
|
- path: /etc/kernel/postinst.d/zz-update-boot
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/zz-update-boot
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/fstab
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/fstab
|
||||||
|
|
||||||
|
- path: /usr/local/sbin/grow-rootfs
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- path: /etc/systemd/system/grow-rootfs.service
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/grow-rootfs.service
|
||||||
|
|
||||||
|
- path: /opt/debs/
|
||||||
|
generator: copy
|
||||||
|
source: /tmp/build-staging/debs
|
||||||
|
|
||||||
|
actions:
|
||||||
|
- trigger: post-unpack
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
# Clear stale apt lists to avoid InRelease split errors after debootstrap
|
||||||
|
rm -f /var/lib/apt/lists/*InRelease /var/lib/apt/lists/*Release /var/lib/apt/lists/*Packages* || true
|
||||||
|
# Retry up to 10 times on transient 429/network errors
|
||||||
|
echo 'Acquire::Retries "10";' > /etc/apt/apt.conf.d/80retry
|
||||||
|
apt-get update
|
||||||
|
|
||||||
|
- trigger: post-update
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
passwd -l root
|
||||||
|
|
||||||
|
- trigger: post-packages
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
fc-cache -f -v
|
||||||
|
|
||||||
|
# SSH — password auth only, root login via sudo
|
||||||
|
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
||||||
|
systemctl enable ssh
|
||||||
|
|
||||||
|
- trigger: post-files
|
||||||
|
action: |-
|
||||||
|
#!/bin/bash
|
||||||
|
set -eux
|
||||||
|
systemctl enable grow-rootfs.service
|
||||||
|
systemctl enable systemd-resolved
|
||||||
|
mkdir -p /boot/efi
|
||||||
|
# Disable kdump postinst hook — it fails in chroot (can't resolve root device)
|
||||||
|
rm -f /etc/kernel/postinst.d/kdump-tools
|
||||||
|
dpkg -i /opt/debs/*.deb
|
||||||
|
rm -rf /opt/debs
|
||||||
|
KVER=$(ls -1t /lib/modules | head -1)
|
||||||
|
update-initramfs -c -k "$KVER"
|
||||||
|
/etc/kernel/postinst.d/zz-update-boot "$KVER"
|
||||||
|
|
||||||
|
mappings:
|
||||||
|
architecture_map: debian
|
||||||
2
distros/ubuntu2604/nm-dns.conf
Normal file
2
distros/ubuntu2604/nm-dns.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[main]
|
||||||
|
dns=systemd-resolved
|
||||||
35
docker/image-builder/Dockerfile
Normal file
35
docker/image-builder/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# System dependencies for distrobuilder and image creation
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates wget git make gcc libc6-dev build-essential \
|
||||||
|
debootstrap rsync gpg dirmngr squashfs-tools \
|
||||||
|
parted dosfstools e2fsprogs kmod \
|
||||||
|
initramfs-tools fdisk gdisk udev kpartx \
|
||||||
|
xz-utils bzip2 zstd \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Go (Ubuntu 24.04 only has 1.22, distrobuilder needs newer)
|
||||||
|
RUN wget -q https://go.dev/dl/go1.25.6.linux-amd64.tar.gz -O /tmp/go.tar.gz && \
|
||||||
|
tar -C /usr/local -xzf /tmp/go.tar.gz && \
|
||||||
|
rm /tmp/go.tar.gz
|
||||||
|
ENV PATH="/usr/local/go/bin:/root/go/bin:${PATH}"
|
||||||
|
|
||||||
|
# Build distrobuilder from source per upstream instructions
|
||||||
|
RUN mkdir -p /root/go/src/github.com/lxc/ && \
|
||||||
|
cd /root/go/src/github.com/lxc/ && \
|
||||||
|
git clone --depth 1 --branch v3.3.1 https://github.com/lxc/distrobuilder && \
|
||||||
|
cd distrobuilder && \
|
||||||
|
make && \
|
||||||
|
cp /root/go/bin/distrobuilder /usr/local/bin/ && \
|
||||||
|
rm -rf /root/go/src /root/go/pkg
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY docker/image-builder/entrypoint.sh /entrypoint.sh
|
||||||
|
COPY docker/image-builder/entrypoint-multi.sh /entrypoint-multi.sh
|
||||||
|
RUN chmod +x /entrypoint.sh /entrypoint-multi.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
292
docker/image-builder/entrypoint-multi.sh
Normal file
292
docker/image-builder/entrypoint-multi.sh
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Multi-distro image builder: builds multiple distros into a single
|
||||||
|
# GPT image (shared FAT32 boot + one ext4 rootfs partition per distro).
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
IMG_SIZE="${IMG_SIZE:-32000}"
|
||||||
|
SKIP_CHROOT="${SKIP_CHROOT:-false}"
|
||||||
|
DISTROS="${DISTROS:-ubuntu ubuntu2604 arch alpine}"
|
||||||
|
STAGING="/tmp/build-staging"
|
||||||
|
EFI_LABEL="boot"
|
||||||
|
IMG="/output/ps5-multi.img"
|
||||||
|
NUM_DISTROS=$(echo $DISTROS | wc -w)
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 1: Build each distro's rootfs via distrobuilder
|
||||||
|
# ======================================================================
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
CHROOT="/build/chroot-${DISTRO}"
|
||||||
|
ROOT_LABEL="$DISTRO"
|
||||||
|
|
||||||
|
if [ "$SKIP_CHROOT" = "true" ] && [ -d "$CHROOT/bin" ]; then
|
||||||
|
echo "=== Skipping $DISTRO chroot build, reusing existing rootfs ==="
|
||||||
|
else
|
||||||
|
echo "=== Building $DISTRO rootfs ==="
|
||||||
|
|
||||||
|
# --- Stage files for distrobuilder's copy generators ---
|
||||||
|
rm -rf "$STAGING"
|
||||||
|
mkdir -p "$STAGING/debs" "$STAGING/pkgs"
|
||||||
|
|
||||||
|
# Use the multi-boot hook instead of the single-distro one
|
||||||
|
cp /repo/distros/shared/zz-update-boot-multi "$STAGING/zz-update-boot"
|
||||||
|
|
||||||
|
# Generate per-distro fstab with partition labels
|
||||||
|
printf 'LABEL=%-14s / ext4 rw,relatime 0 1\nLABEL=%-14s /boot/efi vfat rw,relatime 0 2\n' \
|
||||||
|
"$ROOT_LABEL" "$EFI_LABEL" > "$STAGING/fstab"
|
||||||
|
|
||||||
|
cp /repo/distros/${DISTRO}/grow-rootfs "$STAGING/"
|
||||||
|
cp /repo/distros/${DISTRO}/nm-dns.conf "$STAGING/" 2>/dev/null || true
|
||||||
|
|
||||||
|
case "$DISTRO" in
|
||||||
|
ubuntu*)
|
||||||
|
cp /repo/distros/${DISTRO}/grow-rootfs.service "$STAGING/"
|
||||||
|
cp /kernel-debs/*.deb "$STAGING/debs/"
|
||||||
|
;;
|
||||||
|
alpine)
|
||||||
|
cp /repo/distros/alpine/grow-rootfs.openrc "$STAGING/"
|
||||||
|
;;
|
||||||
|
arch)
|
||||||
|
cp /repo/distros/arch/grow-rootfs.service "$STAGING/"
|
||||||
|
cp /repo/distros/arch/first-boot-setup "$STAGING/"
|
||||||
|
cp /kernel-debs/*.pkg.tar.zst "$STAGING/pkgs/"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Build rootfs ---
|
||||||
|
rm -rf "$CHROOT"/* "$CHROOT"/.[!.]* 2>/dev/null || true
|
||||||
|
mkdir -p "$CHROOT"
|
||||||
|
|
||||||
|
YAML="/repo/distros/${DISTRO}/image.yaml"
|
||||||
|
distrobuilder build-dir "$YAML" "$CHROOT" --with-post-files --cache-dir /build/cache --cleanup=false
|
||||||
|
|
||||||
|
# --- Post-distrobuilder fixups ---
|
||||||
|
case "$DISTRO" in
|
||||||
|
ubuntu*)
|
||||||
|
rm -f "$CHROOT/etc/resolv.conf"
|
||||||
|
ln -sf /run/systemd/resolve/stub-resolv.conf "$CHROOT/etc/resolv.conf"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Write distro marker
|
||||||
|
echo "$DISTRO" > "$CHROOT/etc/ps5-distro"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Alpine kernel gap: no kernel installed via image.yaml ---
|
||||||
|
# This runs even with --skip-chroot because Alpine's rootfs never includes a kernel;
|
||||||
|
# we must always extract it from the .deb artifacts and generate an initrd.
|
||||||
|
if [ "$DISTRO" = "alpine" ]; then
|
||||||
|
echo "=== Alpine: installing kernel from .deb artifacts ==="
|
||||||
|
|
||||||
|
# Extract modules + vmlinuz from the linux-image .deb
|
||||||
|
ALPINE_STAGING="/tmp/alpine-kernel-staging"
|
||||||
|
rm -rf "$ALPINE_STAGING"
|
||||||
|
mkdir -p "$ALPINE_STAGING"
|
||||||
|
for deb in /kernel-debs/linux-image-*.deb; do
|
||||||
|
[ -f "$deb" ] || continue
|
||||||
|
dpkg-deb -x "$deb" "$ALPINE_STAGING"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Identify kernel version from the extracted .deb before copying
|
||||||
|
KVER=$(ls -1 "$ALPINE_STAGING/lib/modules" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -n "$KVER" ]; then
|
||||||
|
# Resolve the real modules path inside the chroot.
|
||||||
|
# Alpine may use usr-merge (/lib -> usr/lib), so we must follow
|
||||||
|
# symlinks to find the actual directory on disk.
|
||||||
|
if [ -L "$CHROOT/lib" ]; then
|
||||||
|
MODDIR="$CHROOT/usr/lib/modules"
|
||||||
|
else
|
||||||
|
MODDIR="$CHROOT/lib/modules"
|
||||||
|
fi
|
||||||
|
mkdir -p "$MODDIR"
|
||||||
|
# Remove any stale modules from a previous build
|
||||||
|
rm -rf "$MODDIR/$KVER"
|
||||||
|
cp -a "$ALPINE_STAGING/lib/modules/$KVER" "$MODDIR/"
|
||||||
|
mkdir -p "$CHROOT/boot"
|
||||||
|
cp "$ALPINE_STAGING/boot/vmlinuz-$KVER" "$CHROOT/boot/vmlinuz-$KVER"
|
||||||
|
echo ">> Alpine: modules copied to $MODDIR/$KVER"
|
||||||
|
ls -la "$MODDIR/"
|
||||||
|
fi
|
||||||
|
rm -rf "$ALPINE_STAGING"
|
||||||
|
|
||||||
|
if [ -n "$KVER" ]; then
|
||||||
|
echo "=== Alpine: generating initrd ==="
|
||||||
|
chroot "$CHROOT" depmod -a "$KVER" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Bind-mount essentials and run mkinitfs inside the alpine chroot
|
||||||
|
mount --bind /dev "$CHROOT/dev"
|
||||||
|
mount --bind /proc "$CHROOT/proc"
|
||||||
|
mount --bind /sys "$CHROOT/sys"
|
||||||
|
chroot "$CHROOT" mkinitfs -k "$KVER" -o "/boot/initrd.img-$KVER" "$KVER" || true
|
||||||
|
umount "$CHROOT/sys" "$CHROOT/proc" "$CHROOT/dev"
|
||||||
|
|
||||||
|
# Populate /boot/efi/ for boot partition assembly
|
||||||
|
mkdir -p "$CHROOT/boot/efi"
|
||||||
|
cp "$CHROOT/boot/vmlinuz-$KVER" "$CHROOT/boot/efi/bzImage"
|
||||||
|
# mkinitfs may output as initramfs-<flavor> — find whatever was generated
|
||||||
|
if [ -f "$CHROOT/boot/initrd.img-$KVER" ]; then
|
||||||
|
cp "$CHROOT/boot/initrd.img-$KVER" "$CHROOT/boot/efi/initrd.img"
|
||||||
|
else
|
||||||
|
# mkinitfs default output: /boot/initramfs-vanilla or similar
|
||||||
|
INITRD=$(ls -1t "$CHROOT"/boot/initramfs-* "$CHROOT"/boot/initrd* 2>/dev/null | head -1)
|
||||||
|
if [ -n "$INITRD" ]; then
|
||||||
|
cp "$INITRD" "$CHROOT/boot/efi/initrd.img"
|
||||||
|
else
|
||||||
|
echo "WARNING: No initrd found for alpine after mkinitfs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ">> Alpine: kernel $KVER staged to boot/efi/"
|
||||||
|
else
|
||||||
|
echo "WARNING: No kernel modules found in .deb for alpine, skipping initrd generation"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 2: Create GPT image with dynamic partition layout
|
||||||
|
# ======================================================================
|
||||||
|
echo "=== Creating ${IMG_SIZE}MB GPT image ==="
|
||||||
|
TMPIMG="/build/ps5-multi.img"
|
||||||
|
dd if=/dev/zero of="$TMPIMG" bs=1M count=$IMG_SIZE conv=fsync status=progress
|
||||||
|
|
||||||
|
BOOT_END=500 # MiB
|
||||||
|
USABLE=$((IMG_SIZE - BOOT_END))
|
||||||
|
PER_DISTRO=$((USABLE / NUM_DISTROS))
|
||||||
|
|
||||||
|
parted -s "$TMPIMG" mklabel gpt
|
||||||
|
parted -s "$TMPIMG" mkpart primary fat32 1MiB ${BOOT_END}MiB
|
||||||
|
parted -s "$TMPIMG" set 1 esp on
|
||||||
|
|
||||||
|
PART_START=$BOOT_END
|
||||||
|
PART_I=1
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
if [ $PART_I -lt $NUM_DISTROS ]; then
|
||||||
|
PART_END=$((PART_START + PER_DISTRO))
|
||||||
|
parted -s "$TMPIMG" mkpart primary ext4 ${PART_START}MiB ${PART_END}MiB
|
||||||
|
else
|
||||||
|
parted -s "$TMPIMG" mkpart primary ext4 ${PART_START}MiB 100%
|
||||||
|
fi
|
||||||
|
PART_START=$((PART_START + PER_DISTRO))
|
||||||
|
PART_I=$((PART_I + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 3: Setup loop device + format partitions
|
||||||
|
# ======================================================================
|
||||||
|
LOOP_PATH=$(losetup -f)
|
||||||
|
if [ ! -e "$LOOP_PATH" ]; then
|
||||||
|
LOOP_NUM=${LOOP_PATH#/dev/loop}
|
||||||
|
mknod "$LOOP_PATH" b 7 "$LOOP_NUM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOOPDEV=$(losetup -f --show "$TMPIMG")
|
||||||
|
kpartx -av "$LOOPDEV"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
LOOP_BASE=$(basename "$LOOPDEV")
|
||||||
|
PART_BOOT="/dev/mapper/${LOOP_BASE}p1"
|
||||||
|
|
||||||
|
echo "=== Formatting partitions ==="
|
||||||
|
mkfs.vfat -n "$EFI_LABEL" -F32 "$PART_BOOT"
|
||||||
|
|
||||||
|
PART_NUM=2
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
PART="/dev/mapper/${LOOP_BASE}p${PART_NUM}"
|
||||||
|
mkfs.ext4 -L "$DISTRO" -m 1 "$PART"
|
||||||
|
PART_NUM=$((PART_NUM + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 4: Copy each rootfs to its partition
|
||||||
|
# ======================================================================
|
||||||
|
mkdir -p /tmp/mnt_boot /tmp/mnt_root
|
||||||
|
|
||||||
|
PART_NUM=2
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
echo "=== Copying $DISTRO rootfs ==="
|
||||||
|
PART="/dev/mapper/${LOOP_BASE}p${PART_NUM}"
|
||||||
|
mount "$PART" /tmp/mnt_root
|
||||||
|
cp -a "/build/chroot-${DISTRO}"/* /tmp/mnt_root/
|
||||||
|
sync
|
||||||
|
umount /tmp/mnt_root
|
||||||
|
PART_NUM=$((PART_NUM + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 5: Assemble boot partition
|
||||||
|
# ======================================================================
|
||||||
|
echo "=== Assembling boot partition ==="
|
||||||
|
mount "$PART_BOOT" /tmp/mnt_boot
|
||||||
|
|
||||||
|
# Collect boot files from each distro's chroot.
|
||||||
|
# Prefer /boot/efi/ (populated by fresh builds), fallback to /boot/ (skip-chroot reruns).
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
CHROOT="/build/chroot-${DISTRO}"
|
||||||
|
EFIDIR="$CHROOT/boot/efi"
|
||||||
|
BOOTDIR="$CHROOT/boot"
|
||||||
|
# Find kernel version: look for versioned directories (not 'kernel'), following symlinks
|
||||||
|
MODBASE="$CHROOT/lib/modules"
|
||||||
|
[ -L "$CHROOT/lib" ] && MODBASE="$CHROOT/usr/lib/modules"
|
||||||
|
KVER=$(find "$MODBASE" -maxdepth 1 -mindepth 1 -type d -not -name 'kernel' -printf '%f\n' 2>/dev/null | sort -V | tail -1)
|
||||||
|
|
||||||
|
# Copy bzImage (shared kernel — same for all, just copy once)
|
||||||
|
if [ ! -f /tmp/mnt_boot/bzImage ]; then
|
||||||
|
if [ -f "$EFIDIR/bzImage" ]; then
|
||||||
|
cp "$EFIDIR/bzImage" /tmp/mnt_boot/
|
||||||
|
elif [ -n "$KVER" ] && [ -f "$BOOTDIR/vmlinuz-$KVER" ]; then
|
||||||
|
cp "$BOOTDIR/vmlinuz-$KVER" /tmp/mnt_boot/bzImage
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy distro-specific initrd
|
||||||
|
if [ -f "$EFIDIR/initrd.img" ]; then
|
||||||
|
cp "$EFIDIR/initrd.img" "/tmp/mnt_boot/initrd-${DISTRO}.img"
|
||||||
|
elif [ -n "$KVER" ] && [ -f "$BOOTDIR/initrd.img-$KVER" ]; then
|
||||||
|
cp "$BOOTDIR/initrd.img-$KVER" "/tmp/mnt_boot/initrd-${DISTRO}.img"
|
||||||
|
elif [ -f "$BOOTDIR/initramfs-vanilla" ]; then
|
||||||
|
# Alpine mkinitfs names its output initramfs-vanilla
|
||||||
|
cp "$BOOTDIR/initramfs-vanilla" "/tmp/mnt_boot/initrd-${DISTRO}.img"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up /boot/efi contents from the rootfs (they're on the boot partition now)
|
||||||
|
rm -rf "$CHROOT/boot/efi"/*
|
||||||
|
done
|
||||||
|
|
||||||
|
# Ubuntu is default boot — copy its initrd as the generic initrd.img
|
||||||
|
if [ -f /tmp/mnt_boot/initrd-ubuntu.img ]; then
|
||||||
|
cp /tmp/mnt_boot/initrd-ubuntu.img /tmp/mnt_boot/initrd.img
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate per-distro cmdline files
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
sed "s|__DISTRO__|${DISTRO}|" /repo/boot/cmdline.txt > "/tmp/mnt_boot/cmdline-${DISTRO}.txt"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Default cmdline points to ubuntu
|
||||||
|
sed "s|__DISTRO__|ubuntu|" /repo/boot/cmdline.txt > /tmp/mnt_boot/cmdline.txt
|
||||||
|
|
||||||
|
# Copy kexec scripts
|
||||||
|
for DISTRO in $DISTROS; do
|
||||||
|
cp "/repo/boot/kexec-${DISTRO}.sh" /tmp/mnt_boot/
|
||||||
|
chmod +x "/tmp/mnt_boot/kexec-${DISTRO}.sh"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Copy vram.txt
|
||||||
|
cp /repo/boot/vram.txt /tmp/mnt_boot/
|
||||||
|
sync
|
||||||
|
|
||||||
|
# ======================================================================
|
||||||
|
# Step 6: Cleanup
|
||||||
|
# ======================================================================
|
||||||
|
umount /tmp/mnt_boot
|
||||||
|
rmdir /tmp/mnt_boot /tmp/mnt_root
|
||||||
|
kpartx -dv "$LOOPDEV"
|
||||||
|
losetup -d "$LOOPDEV"
|
||||||
|
|
||||||
|
mv "$TMPIMG" "$IMG"
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "Done! $IMG (${IMG_SIZE}MB) — $(echo $DISTROS | tr ' ' ' + ')"
|
||||||
|
echo "Flash: sudo dd if=$IMG of=/dev/sdX bs=4M status=progress"
|
||||||
|
echo "========================================"
|
||||||
175
docker/image-builder/entrypoint.sh
Executable file
175
docker/image-builder/entrypoint.sh
Executable file
@@ -0,0 +1,175 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
DISTRO="${DISTRO:-ubuntu}"
|
||||||
|
IMG_SIZE="${IMG_SIZE:-12000}"
|
||||||
|
SKIP_CHROOT="${SKIP_CHROOT:-false}"
|
||||||
|
STAGING="/tmp/build-staging"
|
||||||
|
ROOT_LABEL="${DISTRO}"
|
||||||
|
EFI_LABEL="boot"
|
||||||
|
CHROOT="/build/chroot"
|
||||||
|
IMG="/output/ps5-${DISTRO}.img"
|
||||||
|
|
||||||
|
if [ "$SKIP_CHROOT" = "true" ] && [ -d "$CHROOT/bin" ]; then
|
||||||
|
echo "=== Reusing cached $DISTRO rootfs ==="
|
||||||
|
else
|
||||||
|
echo "=== Building $DISTRO rootfs ==="
|
||||||
|
# --- Stage files for distrobuilder's copy generators ---
|
||||||
|
rm -rf "$STAGING"
|
||||||
|
mkdir -p "$STAGING/debs"
|
||||||
|
cp /repo/distros/shared/zz-update-boot "$STAGING/"
|
||||||
|
# Generate per-distro fstab with partition labels
|
||||||
|
printf 'LABEL=%-14s / ext4 rw,relatime 0 1\nLABEL=%-14s /boot/efi vfat rw,relatime 0 2\n' \
|
||||||
|
"$ROOT_LABEL" "$EFI_LABEL" > "$STAGING/fstab"
|
||||||
|
cp /repo/distros/${DISTRO}/grow-rootfs "$STAGING/"
|
||||||
|
cp /repo/distros/${DISTRO}/nm-dns.conf "$STAGING/" 2>/dev/null || true
|
||||||
|
|
||||||
|
case "$DISTRO" in
|
||||||
|
ubuntu*)
|
||||||
|
cp /repo/distros/${DISTRO}/grow-rootfs.service "$STAGING/"
|
||||||
|
cp /kernel-debs/*.deb "$STAGING/debs/"
|
||||||
|
;;
|
||||||
|
alpine)
|
||||||
|
cp /repo/distros/alpine/grow-rootfs.openrc "$STAGING/"
|
||||||
|
;;
|
||||||
|
arch)
|
||||||
|
cp /repo/distros/arch/grow-rootfs.service "$STAGING/"
|
||||||
|
cp /repo/distros/arch/first-boot-setup "$STAGING/"
|
||||||
|
mkdir -p "$STAGING/pkgs"
|
||||||
|
cp /kernel-debs/*.pkg.tar.zst "$STAGING/pkgs/"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Build rootfs ---
|
||||||
|
rm -rf "$CHROOT"/* "$CHROOT"/.[!.]* 2>/dev/null || true
|
||||||
|
|
||||||
|
YAML="/repo/distros/${DISTRO}/image.yaml"
|
||||||
|
distrobuilder build-dir "$YAML" "$CHROOT" --with-post-files --cache-dir /build/cache --cleanup=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Post-distrobuilder fixups ---
|
||||||
|
case "$DISTRO" in
|
||||||
|
ubuntu*)
|
||||||
|
rm -f "$CHROOT/etc/resolv.conf"
|
||||||
|
ln -sf /run/systemd/resolve/stub-resolv.conf "$CHROOT/etc/resolv.conf"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# --- Alpine kernel gap: no kernel installed via image.yaml ---
|
||||||
|
# Extract kernel from .deb, copy modules + bzImage, then chroot to run mkinitfs
|
||||||
|
if [ "$DISTRO" = "alpine" ]; then
|
||||||
|
echo "=== Alpine: installing kernel from .deb artifacts ==="
|
||||||
|
|
||||||
|
ALPINE_STAGING="/tmp/alpine-kernel-staging"
|
||||||
|
rm -rf "$ALPINE_STAGING"
|
||||||
|
mkdir -p "$ALPINE_STAGING"
|
||||||
|
for deb in /kernel-debs/linux-image-*.deb; do
|
||||||
|
[ -f "$deb" ] || continue
|
||||||
|
dpkg-deb -x "$deb" "$ALPINE_STAGING"
|
||||||
|
done
|
||||||
|
|
||||||
|
KVER=$(ls -1 "$ALPINE_STAGING/lib/modules" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -n "$KVER" ]; then
|
||||||
|
# Resolve the real modules path (Alpine may use usr-merge: /lib -> usr/lib)
|
||||||
|
if [ -L "$CHROOT/lib" ]; then
|
||||||
|
MODDIR="$CHROOT/usr/lib/modules"
|
||||||
|
else
|
||||||
|
MODDIR="$CHROOT/lib/modules"
|
||||||
|
fi
|
||||||
|
mkdir -p "$MODDIR"
|
||||||
|
rm -rf "$MODDIR/$KVER"
|
||||||
|
cp -a "$ALPINE_STAGING/lib/modules/$KVER" "$MODDIR/"
|
||||||
|
mkdir -p "$CHROOT/boot"
|
||||||
|
cp "$ALPINE_STAGING/boot/vmlinuz-$KVER" "$CHROOT/boot/vmlinuz-$KVER"
|
||||||
|
echo ">> Alpine: modules copied to $MODDIR/$KVER"
|
||||||
|
fi
|
||||||
|
rm -rf "$ALPINE_STAGING"
|
||||||
|
|
||||||
|
if [ -n "$KVER" ]; then
|
||||||
|
chroot "$CHROOT" depmod -a "$KVER" 2>/dev/null || true
|
||||||
|
|
||||||
|
mount --bind /dev "$CHROOT/dev"
|
||||||
|
mount --bind /proc "$CHROOT/proc"
|
||||||
|
mount --bind /sys "$CHROOT/sys"
|
||||||
|
chroot "$CHROOT" mkinitfs -k "$KVER" -o "/boot/initrd.img-$KVER" "$KVER" || true
|
||||||
|
umount "$CHROOT/sys" "$CHROOT/proc" "$CHROOT/dev"
|
||||||
|
|
||||||
|
# Populate /boot/efi/ for boot partition assembly
|
||||||
|
mkdir -p "$CHROOT/boot/efi"
|
||||||
|
cp "$CHROOT/boot/vmlinuz-$KVER" "$CHROOT/boot/efi/bzImage"
|
||||||
|
if [ -f "$CHROOT/boot/initrd.img-$KVER" ]; then
|
||||||
|
cp "$CHROOT/boot/initrd.img-$KVER" "$CHROOT/boot/efi/initrd.img"
|
||||||
|
else
|
||||||
|
INITRD=$(ls -1t "$CHROOT"/boot/initramfs-* "$CHROOT"/boot/initrd* 2>/dev/null | head -1)
|
||||||
|
if [ -n "$INITRD" ]; then
|
||||||
|
cp "$INITRD" "$CHROOT/boot/efi/initrd.img"
|
||||||
|
else
|
||||||
|
echo "WARNING: No initrd found for alpine after mkinitfs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ">> Alpine: kernel $KVER staged to boot/efi/"
|
||||||
|
else
|
||||||
|
echo "WARNING: No kernel modules found in .deb for alpine"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Create GPT disk image ---
|
||||||
|
echo "=== Creating ${IMG_SIZE}MB disk image ==="
|
||||||
|
TMPIMG="/build/ps5-${DISTRO}.img"
|
||||||
|
dd if=/dev/zero of="$TMPIMG" bs=1M count=$IMG_SIZE conv=fsync status=progress
|
||||||
|
|
||||||
|
parted -s "$TMPIMG" mklabel gpt
|
||||||
|
parted -s "$TMPIMG" mkpart primary ext4 500MiB 100%
|
||||||
|
parted -s "$TMPIMG" mkpart primary fat32 1MiB 500MiB
|
||||||
|
parted -s "$TMPIMG" set 2 esp on
|
||||||
|
|
||||||
|
# Ensure the free loop device node exists (udev doesn't run inside containers,
|
||||||
|
# so when the kernel allocates a new loop number it may lack a /dev node)
|
||||||
|
LOOP_PATH=$(losetup -f)
|
||||||
|
if [ ! -e "$LOOP_PATH" ]; then
|
||||||
|
LOOP_NUM=${LOOP_PATH#/dev/loop}
|
||||||
|
mknod "$LOOP_PATH" b 7 "$LOOP_NUM"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOOPDEV=$(losetup -f --show "$TMPIMG")
|
||||||
|
# Use kpartx to create partition device mappings (more reliable in containers)
|
||||||
|
kpartx -av "$LOOPDEV"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# kpartx creates /dev/mapper/loopXp1, /dev/mapper/loopXp2
|
||||||
|
LOOP_BASE=$(basename "$LOOPDEV")
|
||||||
|
PART1="/dev/mapper/${LOOP_BASE}p1"
|
||||||
|
PART2="/dev/mapper/${LOOP_BASE}p2"
|
||||||
|
|
||||||
|
echo "=== Formatting partitions ==="
|
||||||
|
mkfs.ext4 -L "$ROOT_LABEL" -m 1 "$PART1"
|
||||||
|
mkfs.vfat -n "$EFI_LABEL" -F32 "$PART2"
|
||||||
|
|
||||||
|
mkdir -p /tmp/usb_root /tmp/usb_efi
|
||||||
|
mount "$PART1" /tmp/usb_root
|
||||||
|
mount "$PART2" /tmp/usb_efi
|
||||||
|
|
||||||
|
echo "=== Copying rootfs to image ==="
|
||||||
|
cp -a "$CHROOT"/* /tmp/usb_root/
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "=== Assembling boot partition ==="
|
||||||
|
mv /tmp/usb_root/boot/efi/* /tmp/usb_efi/ 2>/dev/null || true
|
||||||
|
sed "s|__DISTRO__|$ROOT_LABEL|" /repo/boot/cmdline.txt > /tmp/usb_efi/cmdline.txt
|
||||||
|
cp /repo/boot/vram.txt /tmp/usb_efi/
|
||||||
|
sync
|
||||||
|
|
||||||
|
umount /tmp/usb_root /tmp/usb_efi
|
||||||
|
rmdir /tmp/usb_root /tmp/usb_efi
|
||||||
|
kpartx -dv "$LOOPDEV"
|
||||||
|
losetup -d "$LOOPDEV"
|
||||||
|
|
||||||
|
# Move finished image to output volume
|
||||||
|
mv "$TMPIMG" "$IMG"
|
||||||
|
sync
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo "Done! $IMG (${IMG_SIZE}MB)"
|
||||||
|
echo "Flash: sudo dd if=$IMG of=/dev/sdX bs=4M status=progress"
|
||||||
|
echo "========================================"
|
||||||
16
docker/kernel-builder-arch/Dockerfile
Normal file
16
docker/kernel-builder-arch/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM archlinux:latest
|
||||||
|
|
||||||
|
# Only packaging tools needed — kernel is already compiled
|
||||||
|
RUN pacman -Syu --noconfirm && pacman -S --noconfirm \
|
||||||
|
zstd libarchive && \
|
||||||
|
pacman -Scc --noconfirm
|
||||||
|
|
||||||
|
WORKDIR /out/staging
|
||||||
|
|
||||||
|
# Pre-built artifacts are bind-mounted at /out/staging
|
||||||
|
# Output packages go to /out
|
||||||
|
|
||||||
|
COPY docker/kernel-builder-arch/build.sh /build.sh
|
||||||
|
RUN chmod +x /build.sh
|
||||||
|
|
||||||
|
CMD ["/build.sh"]
|
||||||
58
docker/kernel-builder-arch/build.sh
Normal file
58
docker/kernel-builder-arch/build.sh
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Packages pre-built kernel artifacts as a pacman .pkg.tar.zst.
|
||||||
|
# Runs inside Docker as root; packages manually (makepkg refuses root).
|
||||||
|
# Expects build artifacts staged in /out/staging by the ubuntu build step.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ ! -f /out/staging/boot/bzImage ]; then
|
||||||
|
echo "Error: no staged artifacts in /out/staging — run the kernel build step first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine version from staged modules directory
|
||||||
|
KVER=$(ls /out/staging/lib/modules/)
|
||||||
|
PKGNAME="linux-custom"
|
||||||
|
VERSION=${KVER%%-*}
|
||||||
|
|
||||||
|
echo "==> Packaging kernel $KVER as pacman package"
|
||||||
|
|
||||||
|
STAGING=$(mktemp -d)
|
||||||
|
|
||||||
|
# Copy staged boot artifacts
|
||||||
|
mkdir -p "$STAGING/boot"
|
||||||
|
cp /out/staging/boot/bzImage "$STAGING/boot/vmlinuz-$KVER"
|
||||||
|
cp /out/staging/System.map "$STAGING/boot/System.map-$KVER"
|
||||||
|
cp /out/staging/.config "$STAGING/boot/config-$KVER"
|
||||||
|
|
||||||
|
# Copy pre-installed modules (Arch uses /lib -> /usr/lib)
|
||||||
|
mkdir -p "$STAGING/usr/lib/modules"
|
||||||
|
cp -a "/out/staging/lib/modules/$KVER" "$STAGING/usr/lib/modules/"
|
||||||
|
|
||||||
|
# Create .PKGINFO
|
||||||
|
BUILDDATE=$(date -u +%s)
|
||||||
|
INSTALLED_SIZE=$(du -sb "$STAGING" | awk '{print $1}')
|
||||||
|
cat > "$STAGING/.PKGINFO" << EOF
|
||||||
|
pkgname = $PKGNAME
|
||||||
|
pkgbase = $PKGNAME
|
||||||
|
pkgver = ${VERSION}-1
|
||||||
|
pkgdesc = Custom Linux kernel $KVER for PS5
|
||||||
|
url = https://kernel.org
|
||||||
|
builddate = $BUILDDATE
|
||||||
|
packager = bootstrap_docker
|
||||||
|
size = $INSTALLED_SIZE
|
||||||
|
arch = x86_64
|
||||||
|
license = GPL-2.0-only
|
||||||
|
provides = linux=$VERSION
|
||||||
|
provides = linux-custom=$VERSION
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create .MTREE (required by newer pacman)
|
||||||
|
cd "$STAGING"
|
||||||
|
LANG=C bsdtar -czf .MTREE --format=mtree \
|
||||||
|
--options='!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link' \
|
||||||
|
.PKGINFO *
|
||||||
|
|
||||||
|
# Build the package
|
||||||
|
LANG=C bsdtar -cf - .PKGINFO .MTREE * | zstd -c -T0 > "/out/${PKGNAME}-${VERSION}-1-x86_64.pkg.tar.zst"
|
||||||
|
|
||||||
|
echo "==> Done: /out/${PKGNAME}-${VERSION}-1-x86_64.pkg.tar.zst"
|
||||||
23
docker/kernel-builder/Dockerfile
Normal file
23
docker/kernel-builder/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential bc bison flex kmod cpio gawk \
|
||||||
|
libdw-dev libelf-dev libssl-dev \
|
||||||
|
python3 rsync dpkg-dev debhelper zstd \
|
||||||
|
perl git xz-utils ccache \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV PATH="/usr/lib/ccache:${PATH}"
|
||||||
|
ENV CCACHE_DIR=/ccache
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
# Kernel source is bind-mounted at /src
|
||||||
|
# Build artifacts are staged to /out/staging
|
||||||
|
|
||||||
|
COPY docker/kernel-builder/build.sh /build.sh
|
||||||
|
RUN chmod +x /build.sh
|
||||||
|
|
||||||
|
CMD ["/build.sh"]
|
||||||
28
docker/kernel-builder/build.sh
Normal file
28
docker/kernel-builder/build.sh
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Compiles the kernel and stages all artifacts into /out/staging.
|
||||||
|
# Runs inside Docker; kernel source is bind-mounted at /src.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Clean host-built tool artifacts that may reference wrong include paths
|
||||||
|
make -C tools/objtool clean 2>/dev/null || true
|
||||||
|
|
||||||
|
make olddefconfig
|
||||||
|
make -j"$(nproc)" bzImage modules
|
||||||
|
|
||||||
|
# Stage all artifacts so downstream packagers don't need to run make
|
||||||
|
echo "=== Staging build artifacts ==="
|
||||||
|
rm -rf /out/staging
|
||||||
|
mkdir -p /out/staging/boot
|
||||||
|
|
||||||
|
cp arch/x86/boot/bzImage /out/staging/boot/
|
||||||
|
cp System.map /out/staging/
|
||||||
|
cp .config /out/staging/
|
||||||
|
|
||||||
|
make modules_install INSTALL_MOD_PATH=/out/staging INSTALL_MOD_STRIP=1
|
||||||
|
|
||||||
|
# Remove dangling symlinks back into the source tree
|
||||||
|
KVER=$(make -s kernelrelease)
|
||||||
|
rm -f "/out/staging/lib/modules/$KVER/build" \
|
||||||
|
"/out/staging/lib/modules/$KVER/source"
|
||||||
|
|
||||||
|
echo "=== Build artifacts staged in /out/staging ==="
|
||||||
5834
patches/config
Normal file
5834
patches/config
Normal file
File diff suppressed because it is too large
Load Diff
2935
patches/linux.patch
Normal file
2935
patches/linux.patch
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user