#!/usr/bin/env bash # install_for_jruby - Provision the JRuby-based radio automation stack. # Detects/reuses existing Java and JRuby; installs gems one at a time. # Must be run as root from within the target directory (e.g. /srv/radio/). # Idempotent: safe to re-run. set -euo pipefail INSTALL_DIR="$(pwd)" SERVICE_NAME="$(basename "$INSTALL_DIR")" GEM_HOME_LOCAL="$INSTALL_DIR/.gems" JRBURY_VERSION="10.1.1.0" MAVEN_BASE="https://repo1.maven.org/maven2/org/jruby/jruby-dist" JRUBY_HOME_PINNED="/opt/jruby-${JRBURY_VERSION}" echo "=== Radio Automation Installer (JRuby) ===" echo "Install dir: $INSTALL_DIR" echo "Service name: $SERVICE_NAME" echo # --- Prompt for config values (pre-fill from existing config.json) --- EXISTING_CONFIG="$INSTALL_DIR/config.json" declare -A CFG CFG[ICE_HOST]="localhost" CFG[ICE_PORT]="7777" CFG[ICE_MOUNT]="/audio.mp3" CFG[ICE_USER]="source" CFG[ICE_PASS]="" CFG[GPODDER_ENABLE]="false" CFG[GPODDER_HOST]="https://gpodder.net" CFG[GPODDER_USER]="" CFG[GPODDER_PASS]="" if [[ -f "$EXISTING_CONFIG" ]]; then echo "Existing config.json found; using as defaults." CFG[ICE_HOST]=$(jq -r '.icecast.host // "localhost"' "$EXISTING_CONFIG") CFG[ICE_PORT]=$(jq -r '.icecast.port // 7777' "$EXISTING_CONFIG") CFG[ICE_MOUNT]=$(jq -r '.icecast.mount // "/audio.mp3"' "$EXISTING_CONFIG") CFG[ICE_USER]=$(jq -r '.icecast.username // "source"' "$EXISTING_CONFIG") CFG[ICE_PASS]=$(jq -r '.icecast.password // ""' "$EXISTING_CONFIG") CFG[GPODDER_ENABLE]=$(jq -r '.gpodder.enable // false' "$EXISTING_CONFIG") CFG[GPODDER_HOST]=$(jq -r '.gpodder.host // "https://gpodder.net"' "$EXISTING_CONFIG") CFG[GPODDER_USER]=$(jq -r '.gpodder.username // ""' "$EXISTING_CONFIG") CFG[GPODDER_PASS]=$(jq -r '.gpodder.password // ""' "$EXISTING_CONFIG") fi read -rp "Icecast host [$CFG[ICE_HOST]]: " v; CFG[ICE_HOST]=${v:-$CFG[ICE_HOST]} read -rp "Icecast port [$CFG[ICE_PORT]]: " v; CFG[ICE_PORT]=${v:-$CFG[ICE_PORT]} read -rp "Icecast mount [$CFG[ICE_MOUNT]]: " v; CFG[ICE_MOUNT]=${v:-$CFG[ICE_MOUNT]} read -rp "Icecast source username [$CFG[ICE_USER]]: " v; CFG[ICE_USER]=${v:-$CFG[ICE_USER]} read -rsp "Icecast source password [$CFG[ICE_PASS]]: "; echo; CFG[ICE_PASS]=${v:-$CFG[ICE_PASS]} read -rp "Enable gPodder sync? (true/false) [$CFG[GPODDER_ENABLE]]: " v; CFG[GPODDER_ENABLE]=${v:-$CFG[GPODDER_ENABLE]} read -rp "gPodder host [$CFG[GPODDER_HOST]]: " v; CFG[GPODDER_HOST]=${v:-$CFG[GPODDER_HOST]} read -rp "gPodder username [$CFG[GPODDER_USER]]: " v; CFG[GPODDER_USER]=${v:-$CFG[GPODDER_USER]} read -rsp "gPodder password [$CFG[GPODDER_PASS]]: "; echo; CFG[GPODDER_PASS]=${v:-$CFG[GPODDER_PASS]} echo echo "Summary:" echo " Icecast: ${CFG[ICE_HOST]}:${CFG[ICE_PORT]}${CFG[ICE_MOUNT]}" echo " gPodder sync: ${CFG[GPODDER_ENABLE]}" read -rp "Proceed? [y/N] " confirm [[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; } # --- Base packages --- echo "Installing base packages..." apt-get update -qq apt-get install -y -qq liquidsoap icecast2 jq curl unzip ca-certificates >/dev/null # --- Detect or install Java (>= 21) --- JAVA_OK=false if command -v java &>/dev/null; then JAVA_VER=$(java -version 2>&1 | head -1 | sed 's/.*"\([0-9]*\)\..*/\1/') if (( JAVA_VER >= 21 )); then echo "Found Java $JAVA_VER; reusing." JAVA_OK=true fi fi if [[ "$JAVA_OK" != "true" ]]; then echo "Installing OpenJDK 21 headless..." apt-get install -y -qq openjdk-21-jdk-headless >/dev/null fi # --- Detect or install JRuby --- JRUBY_BIN="" if [[ -x "$JRUBY_HOME_PINNED/bin/jruby" ]]; then JRUBY_BIN="$JRUBY_HOME_PINNED/bin/jruby" echo "Found pinned JRuby at $JRUBY_HOME_PINNED" elif command -v jruby &>/dev/null; then JRUBY_BIN="$(command -v jruby)" echo "Found JRuby on PATH: $JRUBY_BIN" else echo "Downloading JRuby ${JRBURY_VERSION} from Maven Central..." TARBALL="jruby-dist-${JRBURY_VERSION}-bin.tar.gz" URL="${MAVEN_BASE}/${JRBURY_VERSION}/${TARBALL}" cd /tmp curl -fsSL -o "$TARBALL" "$URL" tar xzf "$TARBALL" mv "jruby-${JRBURY_VERSION}" "$JRUBY_HOME_PINNED" rm -f "$TARBALL" JRUBY_BIN="$JRUBY_HOME_PINNED/bin/jruby" echo "Installed JRuby to $JRUBY_HOME_PINNED" fi # --- Register via update-alternatives --- for cmd in jruby bundle gem rake irb; do ALT_SRC="$($JRUBY_BIN -e "puts Gem.bindir" 2>/dev/null || echo "$(${JRUBY_BIN%/jruby}${cmd}" --version &>/dev/null && which ${cmd} 2>/dev/null || echo "$JRUBY_HOME_PINNED/bin/${cmd}")")" # Simpler: just link the known bin paths LINK_TARGET="$JRUBY_HOME_PINNED/bin/${cmd}" if [[ -x "$LINK_TARGET" ]]; then ln -sf "$LINK_TARGET" "/usr/local/bin/${cmd}" update-alternatives --install "/usr/local/bin/${cmd}" "${cmd}" "$LINK_TARGET" 100 || true fi done echo "JRuby integrated via /usr/local/bin symlinks." # --- Install gems one at a time (avoid OOM on low-RAM hosts) --- export GEM_HOME="$GEM_HOME_LOCAL" export GEM_PATH="$GEM_HOME_LOCAL" export JRUBY_OPTS="-J-Xmx1g -J-Xss512k" echo "Installing bundler..." "$JRUBY_BIN" -S gem install bundler --no-document 2>&1 | tail -1 echo "Installing nokogiri..." "$JRUBY_BIN" -S gem install nokogiri --no-document 2>&1 | tail -1 echo "Installing sqlite3..." "$JRUBY_BIN" -S gem install sqlite3 --no-document 2>&1 | tail -1 echo "Installing json..." "$JRUBY_BIN" -S gem install json --no-document 2>&1 | tail -1 # --- Create directory structure --- for d in music podcasts jingles announcements playlists state logs; do mkdir -p "$INSTALL_DIR/$d" done # --- Generate config.json --- cat > "$INSTALL_DIR/config.json" </dev/null; then useradd --system --home-dir "$INSTALL_DIR" --shell /usr/sbin/nologin liquidsoap fi chown -R liquidsoap:liquidsoap "$INSTALL_DIR" # --- Write systemd service --- cat > /etc/systemd/system/"$SERVICE_NAME".service </dev/null | grep -v "fetch_podcasts.rb"; echo "0 * * * * $FETCH_CMD") | crontab - (crontab -l 2>/dev/null | grep -v "update_playlists.rb"; echo "30 * * * * $UPDATE_CMD") | crontab - # --- Reload and enable --- systemctl daemon-reload systemctl enable "$SERVICE_NAME" echo echo "=== Installation complete ===" echo "Next steps:" echo " 1. Drop MP3/M4A files into $INSTALL_DIR/music/" echo " 2. Edit $INSTALL_DIR/schedule.txt with your show schedule" echo " 3. Review $INSTALL_DIR/station.liq" echo " 4. Start services:" echo " sudo systemctl start icecast2" echo " sudo systemctl start $SERVICE_NAME"