feat(runner): Fixes and improvements (#259)

## Description

- Improves latency for runner
- Fixes bugs in entrypoint bash scripts
- Package updates, gstreamer 1.26 and workaround for it

Modified runner workflow to hopefully pull latest cachyos base image on
nightlies. This will cause a full build but for nightlies should be
fine?

Also removed the duplicate key-down workaround as we've enabled ordered
datachannels now. Increased retransmit to 2 from 0 to see if it'll help
with some network issues.

Marked as draft as I need to do bug testing still, I'll do it after
fever calms down 😅



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

## Summary by CodeRabbit

- **New Features**
- Enhanced deployment workflows with optimized container image
management.
- Improved audio and video processing for lower latency and better
synchronization.
  - Consolidated debugging options to ease command-line monitoring.

- **Refactor**
- Streamlined internal script flow and process handling for smoother
performance.
- Updated dependency management and communication protocols to boost
overall stability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
Kristian Ollikainen
2025-04-13 23:13:09 +03:00
committed by GitHub
parent f408ec56cb
commit 9a6826b069
15 changed files with 1257 additions and 672 deletions

View File

@@ -1,72 +1,176 @@
#!/bin/bash
set -euo pipefail
# Wait for dbus socket to be ready
echo "Waiting for DBus system bus socket..."
DBUS_SOCKET="/run/dbus/system_bus_socket"
for _ in {1..10}; do # Wait up to 10 seconds
if [ -e "$DBUS_SOCKET" ]; then
echo "DBus system bus socket is ready."
break
fi
sleep 1
done
if [ ! -e "$DBUS_SOCKET" ]; then
echo "Error: DBus system bus socket did not appear. Exiting."
exit 1
fi
# Configuration
CACHE_DIR="/home/nestri/.cache/nvidia"
NVIDIA_INSTALLER_DIR="/tmp"
TIMEOUT_SECONDS=10
# Wait for PipeWire to be ready
echo "Waiting for PipeWire socket..."
PIPEWIRE_SOCKET="/run/user/${UID}/pipewire-0"
for _ in {1..10}; do # Wait up to 10 seconds
if [ -e "$PIPEWIRE_SOCKET" ]; then
echo "PipeWire socket is ready."
break
fi
sleep 1
done
if [ ! -e "$PIPEWIRE_SOCKET" ]; then
echo "Error: PipeWire socket did not appear. Exiting."
exit 1
fi
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
echo "Detecting GPU vendor..."
source /etc/nestri/gpu_helpers.sh
# Waits for a given socket to be ready
wait_for_socket() {
local socket_path="$1"
local name="$2"
log "Waiting for $name socket at $socket_path..."
for ((i=1; i<=TIMEOUT_SECONDS; i++)); do
if [[ -e "$socket_path" ]]; then
log "$name socket is ready."
return 0
fi
sleep 1
done
log "Error: $name socket did not appear after ${TIMEOUT_SECONDS}s."
return 1
}
get_gpu_info
# Ensures cache directory exists
setup_cache() {
log "Setting up NVIDIA driver cache directory at $CACHE_DIR..."
mkdir -p "$CACHE_DIR" || {
log "Warning: Failed to create cache directory, continuing without cache."
return 1
}
chown nestri:nestri "$CACHE_DIR" 2>/dev/null || {
log "Warning: Failed to set cache directory ownership, continuing..."
}
}
# Check for NVIDIA so we can apply a workaround
if [[ -n "${vendor_devices[nvidia]:-}" ]]; then
echo "NVIDIA GPU detected, applying driver fix..."
# Determine NVIDIA driver version from host
if [ -f "/proc/driver/nvidia/version" ]; then
NVIDIA_DRIVER_VERSION=$(head -n1 /proc/driver/nvidia/version | awk '{for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+\.[0-9\.]+/) {print $i; exit}}')
elif command -v nvidia-smi &> /dev/null; then
NVIDIA_DRIVER_VERSION=$(nvidia-smi --version | grep -i 'driver version' | cut -d: -f2 | tr -d ' ')
else
echo "Failed to determine NVIDIA driver version. Exiting."
exit 1
# Grabs NVIDIA driver installer
get_nvidia_installer() {
local driver_version="$1"
local arch="$2"
local filename="NVIDIA-Linux-${arch}-${driver_version}.run"
local cached_file="${CACHE_DIR}/${filename}"
local tmp_file="${NVIDIA_INSTALLER_DIR}/${filename}"
# Check cache
if [[ -f "$cached_file" ]]; then
log "Found cached NVIDIA installer at $cached_file."
cp "$cached_file" "$tmp_file" || {
log "Warning: Failed to copy cached installer, proceeding with download."
rm -f "$cached_file" 2>/dev/null
}
fi
NVIDIA_DRIVER_ARCH=$(uname -m)
filename="NVIDIA-Linux-${NVIDIA_DRIVER_ARCH}-${NVIDIA_DRIVER_VERSION}.run"
cd /tmp/
if [ ! -f "${filename}" ]; then
# Attempt multiple download sources
if ! wget "https://international.download.nvidia.com/XFree86/Linux-${NVIDIA_DRIVER_ARCH}/${NVIDIA_DRIVER_VERSION}/${filename}"; then
if ! wget "https://international.download.nvidia.com/tesla/${NVIDIA_DRIVER_VERSION}/${filename}"; then
echo "Failed to download NVIDIA driver from both XFree86 and Tesla repositories"
exit 1
# Download if not in tmp
if [[ ! -f "$tmp_file" ]]; then
log "Downloading NVIDIA driver installer ($filename)..."
local urls=(
"https://international.download.nvidia.com/XFree86/Linux-${arch}/${driver_version}/${filename}"
"https://international.download.nvidia.com/tesla/${driver_version}/${filename}"
)
local success=0
for url in "${urls[@]}"; do
if wget -q --show-progress "$url" -O "$tmp_file"; then
success=1
break
fi
log "Failed to download from $url, trying next source..."
done
if [[ "$success" -eq 0 ]]; then
log "Error: Failed to download NVIDIA driver from all sources."
return 1
fi
chmod +x "${filename}"
# Install driver components without kernel modules
sudo ./"${filename}" --silent --no-kernel-module --install-compat32-libs --no-nouveau-check --no-nvidia-modprobe --no-systemd --no-rpms --no-backup --no-check-for-alternate-installs
# Cache the downloaded file
cp "$tmp_file" "$cached_file" 2>/dev/null && \
chown nestri:nestri "$cached_file" 2>/dev/null || \
log "Warning: Failed to cache NVIDIA driver, continuing..."
fi
fi
echo "Switching to nestri user for application startup..."
exec sudo -E -u nestri /etc/nestri/entrypoint_nestri.sh
chmod +x "$tmp_file" || {
log "Error: Failed to make NVIDIA installer executable."
return 1
}
return 0
}
# Installs the NVIDIA driver
install_nvidia_driver() {
local filename="$1"
log "Installing NVIDIA driver components from $filename..."
sudo ./"$filename" \
--silent \
--no-kernel-module \
--install-compat32-libs \
--no-nouveau-check \
--no-nvidia-modprobe \
--no-systemd \
--no-rpms \
--no-backup \
--no-check-for-alternate-installs || {
log "Error: NVIDIA driver installation failed."
return 1
}
log "NVIDIA driver installation completed."
return 0
}
main() {
# Wait for required sockets
wait_for_socket "/run/dbus/system_bus_socket" "DBus" || exit 1
wait_for_socket "/run/user/${UID}/pipewire-0" "PipeWire" || exit 1
# Load GPU helpers and detect GPU
log "Detecting GPU vendor..."
if [[ ! -f /etc/nestri/gpu_helpers.sh ]]; then
log "Error: GPU helpers script not found at /etc/nestri/gpu_helpers.sh."
exit 1
fi
source /etc/nestri/gpu_helpers.sh
get_gpu_info || {
log "Error: Failed to detect GPU information."
exit 1
}
# Handle NVIDIA GPU
if [[ -n "${vendor_devices[nvidia]:-}" ]]; then
log "NVIDIA GPU detected, applying driver fix..."
# Determine NVIDIA driver version
local nvidia_driver_version=""
if [[ -f "/proc/driver/nvidia/version" ]]; then
nvidia_driver_version=$(awk '/NVIDIA/ {for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+\.[0-9\.]+/) {print $i; exit}}' /proc/driver/nvidia/version | head -n1)
elif command -v nvidia-smi >/dev/null 2>&1; then
nvidia_driver_version=$(nvidia-smi --version | grep -i 'driver version' | cut -d: -f2 | tr -d ' ')
fi
if [[ -z "$nvidia_driver_version" ]]; then
log "Error: Failed to determine NVIDIA driver version."
exit 1
fi
log "Detected NVIDIA driver version: $nvidia_driver_version"
# Set up cache and get installer
setup_cache
local arch=$(uname -m)
local filename="NVIDIA-Linux-${arch}-${nvidia_driver_version}.run"
cd "$NVIDIA_INSTALLER_DIR" || {
log "Error: Failed to change to $NVIDIA_INSTALLER_DIR."
exit 1
}
get_nvidia_installer "$nvidia_driver_version" "$arch" || exit 1
# Install driver
install_nvidia_driver "$filename" || exit 1
else
log "No NVIDIA GPU detected, skipping driver fix."
fi
# Switch to nestri user
log "Switching to nestri user for application startup..."
if [[ ! -x /etc/nestri/entrypoint_nestri.sh ]]; then
log "Error: Entry point script /etc/nestri/entrypoint_nestri.sh not found or not executable."
exit 1
fi
exec sudo -E -u nestri /etc/nestri/entrypoint_nestri.sh
}
# Trap signals for clean exit
trap 'log "Received termination signal, exiting..."; exit 1' SIGINT SIGTERM
main

View File

@@ -1,222 +1,233 @@
#!/bin/bash
set -euo pipefail
# Make user directory owned by the default user
chown -f "$(id -nu):$(id -ng)" ~ || \
sudo chown -f "$(id -nu):$(id -ng)" ~ || \
chown -R -f -h --no-preserve-root "$(id -nu):$(id -ng)" ~ || \
sudo chown -R -f -h --no-preserve-root "$(id -nu):$(id -ng)" ~ || \
echo 'Failed to change user directory permissions, there may be permission issues'
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# Source environment variables from envs.sh
if [ -f /etc/nestri/envs.sh ]; then
echo "Sourcing environment variables from envs.sh..."
source /etc/nestri/envs.sh
else
echo "envs.sh not found! Ensure it exists at /etc/nestri/envs.sh."
exit 1
fi
# Ensures user directory ownership
chown_user_directory() {
local user_group="$(id -nu):$(id -ng)"
chown -f "$user_group" ~ 2>/dev/null ||
sudo chown -f "$user_group" ~ 2>/dev/null ||
chown -R -f -h --no-preserve-root "$user_group" ~ 2>/dev/null ||
sudo chown -R -f -h --no-preserve-root "$user_group" ~ 2>/dev/null ||
log "Warning: Failed to change user directory permissions, there may be permission issues, continuing..."
}
# 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
}
# Loads environment variables
load_envs() {
if [[ -f /etc/nestri/envs.sh ]]; then
log "Sourcing environment variables from envs.sh..."
source /etc/nestri/envs.sh
else
log "Error: envs.sh not found at /etc/nestri/envs.sh"
exit 1
fi
}
# Configuration
MAX_RETRIES=3
RETRY_COUNT=0
# Helper function to restart the chain
restart_chain() {
echo "Restarting nestri-server, compositor..."
# Start nestri-server
start_nestri_server
RETRY_COUNT=0
# 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
}
# Function to start nestri-server
# Starts nestri-server
start_nestri_server() {
if [[ -n "${NESTRI_PID:-}" ]] && kill -0 "${NESTRI_PID}" 2 >/dev/null; then
echo "Killing existing nestri-server process..."
kill "${NESTRI_PID}"
fi
kill_if_running "${NESTRI_PID:-}" "nestri-server"
echo "Starting nestri-server..."
nestri-server $(echo $NESTRI_PARAMS) &
log "Starting nestri-server..."
nestri-server $NESTRI_PARAMS &
NESTRI_PID=$!
# Wait for Wayland display (wayland-1) to be ready
echo "Waiting for Wayland display 'wayland-1' to be ready..."
log "Waiting for Wayland display 'wayland-1'..."
WAYLAND_SOCKET="${XDG_RUNTIME_DIR}/wayland-1"
for _ in {1..15}; do # Wait up to 15 seconds
if [ -e "$WAYLAND_SOCKET" ]; then
echo "Wayland display 'wayland-1' is ready."
sleep 5 # necessary sleep - reduces chance that non-ready socket is used
for ((i=1; i<=15; i++)); do
if [[ -e "$WAYLAND_SOCKET" ]]; then
log "Wayland display 'wayland-1' ready."
sleep 3
start_compositor
return
fi
sleep 1
done
echo "Error: Wayland display 'wayland-1' did not appear. Incrementing retry count..."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for nestri-server. Exiting."
exit 1
fi
log "Error: Wayland display 'wayland-1' not available."
increment_retry "nestri-server"
restart_chain
}
# Function to start compositor (labwc)
# Starts compositor (labwc)
start_compositor() {
if [[ -n "${COMPOSITOR_PID:-}" ]] && kill -0 "${COMPOSITOR_PID}" 2 >/dev/null; then
echo "Killing existing compositor process..."
kill "${COMPOSITOR_PID}"
fi
kill_if_running "${COMPOSITOR_PID:-}" "compositor"
echo "Pre-configuring compositor..."
log "Pre-configuring compositor..."
mkdir -p "${HOME}/.config/labwc/"
echo '<?xml version="1.0" encoding="UTF-8"?><labwc_config><keyboard><default/></keyboard><mouse><default/><context name="Root"><mousebind button="Left" action="Press"/><mousebind button="Right" action="Press"/><mousebind button="Middle" action="Press"/></context></mouse></labwc_config>' > ~/.config/labwc/rc.xml
cat > ~/.config/labwc/rc.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<labwc_config>
<keyboard><default/></keyboard>
<mouse><default/>
<context name="Root">
<mousebind button="Left" action="Press"/>
<mousebind button="Right" action="Press"/>
<mousebind button="Middle" action="Press"/>
</context>
</mouse>
</labwc_config>
EOF
echo '<?xml version="1.0" encoding="UTF-8"?><openbox_menu></openbox_menu>' > ~/.config/labwc/menu.xml
echo "Starting compositor..."
log "Starting compositor..."
rm -rf /tmp/.X11-unix && mkdir -p /tmp/.X11-unix && chown nestri:nestri /tmp/.X11-unix
WAYLAND_DISPLAY=wayland-1 WLR_BACKENDS=wayland labwc &
COMPOSITOR_PID=$!
# Wait for compositor to initialize
echo "Waiting for compositor to initialize..."
log "Waiting for compositor to initialize..."
COMPOSITOR_SOCKET="${XDG_RUNTIME_DIR}/wayland-0"
for _ in {1..15}; do
if [ -e "$COMPOSITOR_SOCKET" ]; then
echo "compositor is initialized, wayland-0 output ready."
sleep 3 # necessary sleep - reduces chance that non-ready socket is used
for ((i=1; i<=15; i++)); do
if [[ -e "$COMPOSITOR_SOCKET" ]]; then
log "Compositor initialized, wayland-0 ready."
sleep 2
start_wlr_randr
return
fi
sleep 1
done
echo "Error: compositor did not initialize correctly. Incrementing retry count..."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for compositor. Exiting."
exit 1
fi
log "Error: Compositor did not initialize."
increment_retry "compositor"
start_compositor
}
# Function to run wlr-randr
# Configures resolution with wlr-randr
start_wlr_randr() {
echo "Configuring resolution with wlr-randr..."
log "Configuring resolution with wlr-randr..."
OUTPUT_NAME=$(WAYLAND_DISPLAY=wayland-0 wlr-randr --json | jq -r '.[] | select(.enabled == true) | .name' | head -n 1)
if [ -z "$OUTPUT_NAME" ]; then
echo "Error: No enabled outputs detected, exiting."
if [[ -z "$OUTPUT_NAME" ]]; then
log "Error: No enabled outputs detected."
exit 1
fi
# Retry logic for wlr-randr
local WLR_RETRIES=0
while ! WAYLAND_DISPLAY=wayland-0 wlr-randr --output "$OUTPUT_NAME" --custom-mode "$RESOLUTION"; do
echo "Error: Failed to configure wlr-randr. Retrying..."
log "Error: Failed to configure wlr-randr. Retrying..."
((WLR_RETRIES++))
if [ "$WLR_RETRIES" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for wlr-randr, exiting."
if [[ "$WLR_RETRIES" -ge "$MAX_RETRIES" ]]; then
log "Error: Max retries reached for wlr-randr."
exit 1
fi
sleep 2
done
echo "wlr-randr configuration successful."
sleep 2 # necessary sleep - makes sure resolution is changed before next step(s)
log "wlr-randr configuration successful."
sleep 2
}
# Function to start Steam
# Starts Steam
start_steam() {
if [[ -n "${STEAM_PID:-}" ]] && kill -0 "${STEAM_PID}" 2 >/dev/null; then
echo "Killing existing Steam process..."
kill "${STEAM_PID}"
fi
kill_if_running "${STEAM_PID:-}" "Steam"
echo "Starting Steam with -tenfoot..."
WAYLAND_DISPLAY=wayland-0 steam-native -tenfoot &
log "Starting Steam with -tenfoot..."
steam-native -tenfoot &
STEAM_PID=$!
# Verify Steam started successfully
sleep 2
if ! kill -0 "$STEAM_PID" 2>/dev/null; then
echo "Error: Steam failed to start."
log "Error: Steam failed to start."
return 1
fi
echo "Steam started successfully."
log "Steam started successfully."
return 0
}
# Main loop to monitor processes
# 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..."
RETRY_COUNT=0
start_nestri_server
}
# Cleans up processes
cleanup() {
log "Terminating processes..."
kill_if_running "${NESTRI_PID:-}" "nestri-server"
kill_if_running "${COMPOSITOR_PID:-}" "compositor"
kill_if_running "${STEAM_PID:-}" "Steam"
exit 0
}
# Monitor processes for unexpected exits
main_loop() {
trap 'echo "Terminating...";
if [[ -n "${NESTRI_PID:-}" ]] && kill -0 "${NESTRI_PID}" 2>/dev/null; then
kill "${NESTRI_PID}"
fi
if [[ -n "${COMPOSITOR_PID:-}" ]] && kill -0 "${COMPOSITOR_PID}" 2>/dev/null; then
kill "${COMPOSITOR_PID}"
fi
if [[ -n "${STEAM_PID:-}" ]] && kill -0 "${STEAM_PID}" 2>/dev/null; then
kill "${STEAM_PID}"
fi
exit 0' SIGINT SIGTERM
trap cleanup SIGINT SIGTERM
while true; do
# Wait for any child process to exit
wait -n
# Check which process exited
if ! kill -0 ${NESTRI_PID:-} 2 >/dev/null; then
echo "nestri-server crashed. Restarting chain..."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for nestri-server. Exiting."
exit 1
fi
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
start_steam || {
echo "Failed to restart Steam after chain restart."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached. Exiting."
exit 1
fi
}
elif ! kill -0 ${COMPOSITOR_PID:-} 2 >/dev/null; then
echo "compositor crashed. Restarting compositor..."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for compositor. Exiting."
exit 1
fi
start_steam || increment_retry "Steam"
# Check compositor
elif [[ -n "${COMPOSITOR_PID:-}" ]] && ! kill -0 "${COMPOSITOR_PID}" 2>/dev/null; then
log "compositor died."
increment_retry "compositor"
start_compositor
start_steam || {
echo "Failed to restart Steam after compositor restart."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached. Exiting."
exit 1
fi
}
elif ! kill -0 ${STEAM_PID:-} 2 >/dev/null; then
echo "Steam crashed. Restarting Steam..."
((RETRY_COUNT++))
if [ "$RETRY_COUNT" -ge "$MAX_RETRIES" ]; then
echo "Max retries reached for Steam. Exiting."
exit 1
fi
start_steam
start_steam || increment_retry "Steam"
# Check Steam
elif [[ -n "${STEAM_PID:-}" ]] && ! kill -0 "${STEAM_PID}" 2>/dev/null; then
log "Steam died."
increment_retry "Steam"
start_steam || increment_retry "Steam"
fi
done
}
# Initialize retry counter
RETRY_COUNT=0
main() {
chown_user_directory
load_envs
#parse_resolution "${RESOLUTION:-1920x1080}" || exit 1 # Not used currently
restart_chain
start_steam || increment_retry "Steam"
main_loop
}
# Start the initial chain
restart_chain
# Start Steam after initial setup
start_steam
# Enter monitoring loop
main_loop
main

View File

@@ -7,5 +7,8 @@ export XDG_SESSION_TYPE=wayland
export DISPLAY=:0
export $(dbus-launch)
# Causes some setups to break
export PROTON_NO_FSYNC=1
# Our preferred prefix
export WINEPREFIX=/home/${USER}/.nestripfx/

View File

@@ -27,6 +27,7 @@ autorestart=true
autostart=true
startretries=3
priority=3
nice=-10
[program:pipewire-pulse]
user=nestri
@@ -35,6 +36,7 @@ autorestart=true
autostart=true
startretries=3
priority=4
nice=-10
[program:wireplumber]
user=nestri
@@ -43,6 +45,7 @@ autorestart=true
autostart=true
startretries=3
priority=5
nice=-10
[program:entrypoint]
user=root