Add arch_install.sh
This commit is contained in:
parent
aa1152f321
commit
238ff2d05c
|
|
@ -0,0 +1,309 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Arch Linux installer for:
|
||||
# - R + RStudio Server (via AUR rstudio-server-bin)
|
||||
# - JupyterHub + JupyterLab (native install, not TLJH)
|
||||
#
|
||||
# What it does:
|
||||
# - installs system dependencies
|
||||
# - installs and enables UFW
|
||||
# - creates an admin UNIX account
|
||||
# - installs R and RStudio Server
|
||||
# - installs JupyterHub into /opt/jupyterhub
|
||||
# - configures JupyterHub as a systemd service
|
||||
#
|
||||
# Run as root:
|
||||
# sudo bash install_jhub_rstudio_arch.sh
|
||||
#
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
# -----------------------------
|
||||
# Configuration: EDIT THESE
|
||||
# -----------------------------
|
||||
ADMIN_USER="your_admin_user"
|
||||
ADMIN_PASSWORD="your_admin_password"
|
||||
|
||||
JUPYTERHUB_PORT="8000"
|
||||
RSTUDIO_PORT="8787"
|
||||
|
||||
JUPYTERHUB_VENV="/opt/jupyterhub"
|
||||
JUPYTERHUB_CONFIG_DIR="/etc/jupyterhub"
|
||||
JUPYTERHUB_CONFIG_FILE="${JUPYTERHUB_CONFIG_DIR}/jupyterhub_config.py"
|
||||
JUPYTERHUB_SERVICE_FILE="/etc/systemd/system/jupyterhub.service"
|
||||
|
||||
USE_UFW="yes"
|
||||
INSTALL_PARU="yes"
|
||||
# -----------------------------
|
||||
|
||||
log() {
|
||||
printf "\n\033[1;36m==> %s\033[0m\n" "$*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf "\n\033[1;33m[WARN]\033[0m %s\n" "$*"
|
||||
}
|
||||
|
||||
err() {
|
||||
printf "\n\033[1;31m[ERROR]\033[0m %s\n" "$*" >&2
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
err "Run this script as root (sudo)."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
detect_real_user() {
|
||||
if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then
|
||||
REAL_USER="${SUDO_USER}"
|
||||
else
|
||||
REAL_USER="$(logname 2>/dev/null || true)"
|
||||
if [[ -z "${REAL_USER}" || "${REAL_USER}" == "root" ]]; then
|
||||
err "Could not determine the non-root user that should build AUR packages."
|
||||
err "Run with: sudo bash $0 from your normal user session."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
REAL_HOME="$(getent passwd "${REAL_USER}" | cut -d: -f6)"
|
||||
if [[ -z "${REAL_HOME}" || ! -d "${REAL_HOME}" ]]; then
|
||||
err "Could not determine home directory for ${REAL_USER}."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_base_packages() {
|
||||
log "Updating pacman and installing base packages"
|
||||
|
||||
pacman -Syu --noconfirm
|
||||
|
||||
pacman -S --needed --noconfirm \
|
||||
base-devel \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
sudo \
|
||||
ufw \
|
||||
nginx \
|
||||
python \
|
||||
python-pip \
|
||||
python-virtualenv \
|
||||
nodejs \
|
||||
npm \
|
||||
sqlite \
|
||||
pam \
|
||||
shadow \
|
||||
which \
|
||||
r \
|
||||
gcc-fortran
|
||||
}
|
||||
|
||||
configure_firewall() {
|
||||
if [[ "${USE_UFW}" != "yes" ]]; then
|
||||
warn "Skipping UFW configuration"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Configuring UFW firewall"
|
||||
systemctl enable --now ufw
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw allow "${JUPYTERHUB_PORT}"/tcp
|
||||
ufw allow "${RSTUDIO_PORT}"/tcp
|
||||
ufw --force enable
|
||||
}
|
||||
|
||||
create_admin_user() {
|
||||
log "Creating admin system user if needed"
|
||||
|
||||
if ! id "${ADMIN_USER}" >/dev/null 2>&1; then
|
||||
useradd -m -s /bin/bash "${ADMIN_USER}"
|
||||
echo "${ADMIN_USER}:${ADMIN_PASSWORD}" | chpasswd
|
||||
usermod -aG wheel "${ADMIN_USER}"
|
||||
log "Created user ${ADMIN_USER}"
|
||||
else
|
||||
warn "User ${ADMIN_USER} already exists; updating password"
|
||||
echo "${ADMIN_USER}:${ADMIN_PASSWORD}" | chpasswd
|
||||
usermod -aG wheel "${ADMIN_USER}" || true
|
||||
fi
|
||||
}
|
||||
|
||||
install_paru() {
|
||||
if command -v paru >/dev/null 2>&1; then
|
||||
log "paru already installed"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "${INSTALL_PARU}" != "yes" ]]; then
|
||||
err "paru is not installed and INSTALL_PARU=no"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Installing paru AUR helper as ${REAL_USER}"
|
||||
|
||||
local build_dir="${REAL_HOME}/paru-build"
|
||||
rm -rf "${build_dir}"
|
||||
|
||||
sudo -u "${REAL_USER}" bash -lc "
|
||||
set -Eeuo pipefail
|
||||
cd '${REAL_HOME}'
|
||||
git clone https://aur.archlinux.org/paru.git '${build_dir}'
|
||||
cd '${build_dir}'
|
||||
makepkg -si --noconfirm
|
||||
"
|
||||
|
||||
rm -rf "${build_dir}"
|
||||
}
|
||||
|
||||
install_rstudio_server() {
|
||||
log "Installing RStudio Server from AUR (rstudio-server-bin)"
|
||||
|
||||
sudo -u "${REAL_USER}" bash -lc "
|
||||
set -Eeuo pipefail
|
||||
paru -S --needed --noconfirm rstudio-server-bin
|
||||
"
|
||||
|
||||
mkdir -p /etc/rstudio
|
||||
if ! grep -q '^rsession-which-r=' /etc/rstudio/rserver.conf 2>/dev/null; then
|
||||
echo 'rsession-which-r=/usr/bin/R' >> /etc/rstudio/rserver.conf
|
||||
else
|
||||
sed -i 's|^rsession-which-r=.*|rsession-which-r=/usr/bin/R|' /etc/rstudio/rserver.conf
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/rstudio-server
|
||||
chown -R root:root /var/lib/rstudio-server
|
||||
|
||||
# Some installs need an explicit PAM service file present
|
||||
if [[ ! -e /etc/pam.d/rstudio ]]; then
|
||||
cat >/etc/pam.d/rstudio <<'EOF'
|
||||
auth include system-login
|
||||
account include system-login
|
||||
password include system-login
|
||||
session include system-login
|
||||
EOF
|
||||
fi
|
||||
|
||||
systemctl enable --now rstudio-server
|
||||
}
|
||||
|
||||
install_jupyterhub() {
|
||||
log "Installing JupyterHub in virtual environment"
|
||||
|
||||
mkdir -p "${JUPYTERHUB_VENV}"
|
||||
python -m venv "${JUPYTERHUB_VENV}"
|
||||
|
||||
"${JUPYTERHUB_VENV}/bin/pip" install --upgrade pip wheel setuptools
|
||||
|
||||
"${JUPYTERHUB_VENV}/bin/pip" install \
|
||||
jupyterhub \
|
||||
jupyterlab \
|
||||
notebook
|
||||
|
||||
npm install -g configurable-http-proxy
|
||||
}
|
||||
|
||||
configure_jupyterhub() {
|
||||
log "Configuring JupyterHub"
|
||||
|
||||
mkdir -p "${JUPYTERHUB_CONFIG_DIR}"
|
||||
|
||||
cat >"${JUPYTERHUB_CONFIG_FILE}" <<EOF
|
||||
c = get_config()
|
||||
|
||||
c.JupyterHub.bind_url = 'http://0.0.0.0:${JUPYTERHUB_PORT}'
|
||||
c.JupyterHub.hub_ip = '127.0.0.1'
|
||||
c.JupyterHub.spawner_class = 'jupyterhub.spawner.LocalProcessSpawner'
|
||||
|
||||
c.Authenticator.admin_users = {'${ADMIN_USER}'}
|
||||
c.JupyterHub.admin_access = True
|
||||
|
||||
c.Spawner.default_url = '/lab'
|
||||
|
||||
# Use system users + PAM authentication
|
||||
c.JupyterHub.authenticator_class = 'jupyterhub.auth.PAMAuthenticator'
|
||||
|
||||
# Keep runtime state in standard locations
|
||||
c.JupyterHub.cookie_secret_file = '${JUPYTERHUB_CONFIG_DIR}/jupyterhub_cookie_secret'
|
||||
c.JupyterHub.db_url = 'sqlite:///${JUPYTERHUB_CONFIG_DIR}/jupyterhub.sqlite'
|
||||
c.ConfigurableHTTPProxy.command = 'configurable-http-proxy'
|
||||
EOF
|
||||
|
||||
if [[ ! -f "${JUPYTERHUB_CONFIG_DIR}/jupyterhub_cookie_secret" ]]; then
|
||||
umask 077
|
||||
head -c 32 /dev/urandom > "${JUPYTERHUB_CONFIG_DIR}/jupyterhub_cookie_secret"
|
||||
fi
|
||||
|
||||
chmod 600 "${JUPYTERHUB_CONFIG_DIR}/jupyterhub_cookie_secret"
|
||||
|
||||
cat >"${JUPYTERHUB_SERVICE_FILE}" <<EOF
|
||||
[Unit]
|
||||
Description=JupyterHub
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=${JUPYTERHUB_VENV}/bin/jupyterhub -f ${JUPYTERHUB_CONFIG_FILE}
|
||||
WorkingDirectory=${JUPYTERHUB_CONFIG_DIR}
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now jupyterhub
|
||||
}
|
||||
|
||||
verify_services() {
|
||||
log "Verifying services"
|
||||
|
||||
systemctl --no-pager --full status rstudio-server || true
|
||||
systemctl --no-pager --full status jupyterhub || true
|
||||
systemctl --no-pager --full status ufw || true
|
||||
|
||||
ss -tulpn | grep -E ":(${JUPYTERHUB_PORT}|${RSTUDIO_PORT}|80|443)\b" || true
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
local server_ip
|
||||
server_ip="$(hostname -I | awk '{print $1}')"
|
||||
|
||||
echo
|
||||
echo "=========================================================="
|
||||
echo "✅ Installation Summary"
|
||||
echo "=========================================================="
|
||||
echo "JupyterHub is installed and running at:"
|
||||
echo " http://${server_ip}:${JUPYTERHUB_PORT}/"
|
||||
echo
|
||||
echo "RStudio Server is installed and running at:"
|
||||
echo " http://${server_ip}:${RSTUDIO_PORT}/"
|
||||
echo
|
||||
echo "Login user:"
|
||||
echo " ${ADMIN_USER}"
|
||||
echo
|
||||
echo "Important notes:"
|
||||
echo " - JupyterHub on this script is a native Arch install, not TLJH."
|
||||
echo " - RStudio Server was installed from the AUR package rstudio-server-bin."
|
||||
echo " - The same UNIX account (${ADMIN_USER}) works for both services."
|
||||
echo "=========================================================="
|
||||
}
|
||||
|
||||
main() {
|
||||
require_root
|
||||
detect_real_user
|
||||
install_base_packages
|
||||
configure_firewall
|
||||
create_admin_user
|
||||
install_paru
|
||||
install_rstudio_server
|
||||
install_jupyterhub
|
||||
configure_jupyterhub
|
||||
verify_services
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue