mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-11 00:05:36 +02:00
## 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>
281 lines
9.1 KiB
Bash
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
|