Files
netris-nestri/packages/scripts/entrypoint_nestri.sh
Kristian Ollikainen 49cc5e1ab9 feat: Runner image variants, CI improvements (#306)
## Description

Next step would be having full DE environment variant I guess? I'll see
later if it's doable in this PR or if I'll do separate one for keeping
things small and manageable for once 😅

- Added easily doable variants for runners, with simple CI build matrix.
- Added playsite in CI builds finally.
- Some CI formatting and naming fixes.
- Removed PR full runner builds as they kept failing due to lack of disk
space on GH runner.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* New dedicated runner images for Steam, Heroic, and Minecraft plus a
common runtime and builder images.
* **Chores**
* CI/workflow reorganization to build and publish more runner variants
and base images.
* Installer and package tweaks (package manager flags, CUDA enablement)
and updated build tooling.
* Unified startup to use a constructed launch command; removed two
default environment exports.
  * Added container ignore patterns.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
2025-11-20 15:40:50 +02:00

281 lines
9.1 KiB
Bash

#!/bin/bash
# Common helpers as requirement
if [[ -f /etc/nestri/common.sh ]]; then
source /etc/nestri/common.sh
else
echo "Error: Common script not found at /etc/nestri/common.sh" >&2
exit 1
fi
# Parses resolution string
parse_resolution() {
local resolution="$1"
if [[ -z "$resolution" ]]; then
log "Error: No resolution provided"
return 1
fi
IFS='x' read -r width height <<< "$resolution"
if ! [[ "$width" =~ ^[0-9]+$ ]] || ! [[ "$height" =~ ^[0-9]+$ ]]; then
log "Error: Invalid resolution format. Expected: WIDTHxHEIGHT (e.g., 1920x1080), got: $resolution"
return 1
fi
export WIDTH="$width"
export HEIGHT="$height"
return 0
}
# Configuration
MAX_RETRIES=3
RETRY_COUNT=0
WAYLAND_READY_DELAY=3
ENTCMD_PREFIX=""
PRELOAD_SHIM_64=/usr/lib64/libvimputti_shim.so
PRELOAD_SHIM_32=/usr/lib32/libvimputti_shim.so
# Kills process if running
kill_if_running() {
local pid="$1"
local name="$2"
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
log "Killing existing $name process (PID: $pid)..."
kill "$pid"
wait "$pid" 2>/dev/null || true
fi
}
# Starts nestri-server
start_nestri_server() {
kill_if_running "${NESTRI_PID:-}" "nestri-server"
WAYLAND_SOCKET="${XDG_RUNTIME_DIR}/wayland-1"
# Make sure to remove old socket if exists (along with .lock file)
if [[ -e "$WAYLAND_SOCKET" ]]; then
log "Removing stale Wayland socket $WAYLAND_SOCKET"
rm -f "$WAYLAND_SOCKET" 2>/dev/null || {
log "Error: Failed to remove stale Wayland socket $WAYLAND_SOCKET"
exit 1
}
# Ignore error if .lock file doesn't exist
rm -f "${WAYLAND_SOCKET}.lock" 2>/dev/null || true
fi
# Also if gstreamer cache exists, remove it to avoid previous errors from persisting
local gst_cache="${NESTRI_HOME}/.cache/gstreamer-1.0/"
if [[ -d "$gst_cache" ]]; then
log "Removing gstreamer cache at $gst_cache"
rm -rf "${gst_cache}" 2>/dev/null || {
log "Warning: Failed to remove gstreamer cache at $gst_cache"
}
fi
# Start nestri-server
log "Starting nestri-server.."
# Try with realtime scheduling first (chrt -f 80), if fails, launch normally
if $ENTCMD_PREFIX chrt -f 80 true 2>/dev/null; then
$ENTCMD_PREFIX chrt -f 80 nestri-server $NESTRI_PARAMS &
NESTRI_PID=$!
log "Started nestri-server with realtime scheduling"
else
$ENTCMD_PREFIX nestri-server $NESTRI_PARAMS &
NESTRI_PID=$!
log "Started nestri-server"
fi
log "Waiting for Wayland display $WAYLAND_SOCKET.."
for ((i=1; i<=15; i++)); do
if [[ -e "$WAYLAND_SOCKET" ]]; then
log "Wayland display $WAYLAND_SOCKET ready"
sleep "${WAYLAND_READY_DELAY:-3}"
start_compositor
return
fi
sleep 1
done
log "Error: Wayland display $WAYLAND_SOCKET not available"
increment_retry "nestri-server"
restart_chain
}
# Starts compositor with optional application
start_compositor() {
kill_if_running "${COMPOSITOR_PID:-}" "compositor"
kill_if_running "${APP_PID:-}" "application"
# Set default compositor if unset
if [[ -z "${NESTRI_LAUNCH_COMPOSITOR+x}" ]]; then
NESTRI_LAUNCH_COMPOSITOR="gamescope --backend wayland --force-grab-cursor -g -f --rt -W ${WIDTH} -H ${HEIGHT} -r ${FRAMERATE:-60}"
fi
# If PRELOAD_SHIM_arch's are set and exist, set LD_PRELOAD for 32/64-bit apps
local do_ld_preload=false
if [[ -f "$PRELOAD_SHIM_64" ]] || [[ -f "$PRELOAD_SHIM_32" ]]; then
do_ld_preload=true
log "Using LD_PRELOAD shim(s)"
fi
# Configure launch cmd with dbus if set
local launch_cmd=""
if [[ -n "${NESTRI_LAUNCH_CMD+x}" ]]; then
if $do_ld_preload; then
launch_cmd="LD_PRELOAD='/usr/\$LIB/libvimputti_shim.so' dbus-launch $NESTRI_LAUNCH_CMD"
else
launch_cmd="dbus-launch $NESTRI_LAUNCH_CMD"
fi
fi
# Launch compositor if configured
if [[ -n "${NESTRI_LAUNCH_COMPOSITOR}" ]]; then
local compositor_cmd="$NESTRI_LAUNCH_COMPOSITOR"
local is_gamescope=false
# Check if this is a gamescope command
if [[ "$compositor_cmd" == *"gamescope"* ]]; then
is_gamescope=true
if [[ -n "$launch_cmd" ]] && [[ "$compositor_cmd" != *" -- "* ]]; then
# If steam in launch command, enable gamescope integration via -e and enable mangohud
if [[ "$launch_cmd" == *"steam"* ]]; then
compositor_cmd+=" --mangoapp -e"
fi
compositor_cmd+=" -- bash -c $(printf %q "$launch_cmd")"
fi
fi
# Get appropriate socket based on compositor type
if $is_gamescope; then
COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/gamescope-0"
else
COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/wayland-0"
fi
# Clean up old socket if exists
if [[ -e "$COMPOSITOR_SOCKET" ]]; then
log "Removing stale compositor socket $COMPOSITOR_SOCKET"
rm -f "$COMPOSITOR_SOCKET" 2>/dev/null || {
log "Error: Failed to remove stale compositor socket $COMPOSITOR_SOCKET"
exit 1
}
# Ignore error if .lock file doesn't exist
rm -f "${COMPOSITOR_SOCKET}.lock" 2>/dev/null || true
fi
log "Starting compositor: $compositor_cmd"
WAYLAND_DISPLAY=wayland-1 /bin/bash -c "$compositor_cmd" &
COMPOSITOR_PID=$!
# If ld_preload is true, export LD_PRELOAD
if $do_ld_preload; then
export LD_PRELOAD='/usr/$LIB/libvimputti_shim.so'
fi
log "Waiting for compositor socket $COMPOSITOR_SOCKET.."
for ((i=1; i<=15; i++)); do
if [[ -e "$COMPOSITOR_SOCKET" ]]; then
log "Compositor socket ready $COMPOSITOR_SOCKET"
# Patch resolution with wlr-randr for non-gamescope compositors
if ! $is_gamescope; then
local OUTPUT_NAME
OUTPUT_NAME=$(WAYLAND_DISPLAY=wayland-0 wlr-randr --json | jq -r '.[] | select(.enabled == true) | .name' | head -n 1)
if [ -z "$OUTPUT_NAME" ]; then
log "Warning: No enabled outputs detected. Skipping wlr-randr resolution patch"
return
fi
WAYLAND_DISPLAY=wayland-0 wlr-randr --output "$OUTPUT_NAME" --custom-mode "$WIDTH"x"$HEIGHT"
log "Patched resolution with wlr-randr"
if [[ -n "$launch_cmd" ]]; then
log "Starting application: $launch_cmd"
WAYLAND_DISPLAY="$COMPOSITOR_SOCKET" bash -c "$launch_cmd" &
APP_PID=$!
fi
else
log "Gamescope detected, skipping wlr-randr resolution patch"
fi
return
fi
sleep 1
done
log "Warning: Compositor socket not found after 15 seconds ($COMPOSITOR_SOCKET)"
else
# Launch standalone application if no compositor
if [[ -n "$launch_cmd" ]]; then
log "Starting standalone application: $launch_cmd"
WAYLAND_DISPLAY=wayland-1 bash -c "$launch_cmd" &
APP_PID=$!
else
log "No compositor or application configured"
fi
fi
}
# Increments retry counter
increment_retry() {
local component="$1"
((RETRY_COUNT++))
if [[ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]]; then
log "Error: Max retries reached for $component"
exit 1
fi
}
# Restarts the chain
restart_chain() {
log "Restarting nestri-server and compositor..."
start_nestri_server
}
# Cleans up processes
cleanup() {
local exit_code=$?
log "Terminating processes..."
kill_if_running "${NESTRI_PID:-}" "nestri-server"
kill_if_running "${COMPOSITOR_PID:-}" "compositor"
kill_if_running "${APP_PID:-}" "application"
exit $exit_code
}
# Monitor processes for unexpected exits
main_loop() {
trap cleanup SIGINT SIGTERM EXIT
while true; do
sleep 1
# Check nestri-server
if [[ -n "${NESTRI_PID:-}" ]] && ! kill -0 "${NESTRI_PID}" 2>/dev/null; then
log "nestri-server died"
increment_retry "nestri-server"
restart_chain
# Check compositor
elif [[ -n "${COMPOSITOR_PID:-}" ]] && ! kill -0 "${COMPOSITOR_PID}" 2>/dev/null; then
log "compositor died"
increment_retry "compositor"
start_compositor
# Check application
elif [[ -n "${APP_PID:-}" ]] && ! kill -0 "${APP_PID}" 2>/dev/null; then
log "application died"
increment_retry "application"
start_compositor
fi
done
}
main() {
parse_resolution "${RESOLUTION:-1920x1080}" || exit 1
get_container_info || {
log "Warning: Failed to detect container information."
}
if [[ "$container_runtime" != "apptainer" ]]; then
ENTCMD_PREFIX="sudo -E -u ${NESTRI_USER}"
fi
restart_chain
main_loop
}
main