179 lines
5.0 KiB
Bash
Executable File
179 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# volumen one-shot installer for Linux + systemd.
|
|
#
|
|
# Run from a cloned repository as root:
|
|
# sudo ./install.sh
|
|
#
|
|
# The Ruby toolchain is installed automatically from the distro repositories on
|
|
# Fedora and openEuler; on other distributions the script prints guidance and
|
|
# expects Ruby >= 3.3 with a C toolchain to be present already.
|
|
#
|
|
# FreeBSD uses rc.d instead of systemd — see docs/deployment.md.
|
|
|
|
set -euo pipefail
|
|
|
|
VOLUMEN_USER="volumen"
|
|
CONFIG_DIR="/etc/volumen"
|
|
CONFIG_FILE="${CONFIG_DIR}/config.toml"
|
|
DATA_DIR="/var/lib/volumen"
|
|
CONTENT_DIR="${DATA_DIR}/posts"
|
|
USERS_FILE="${DATA_DIR}/users.toml"
|
|
SERVICE_FILE="/etc/systemd/system/volumen.service"
|
|
MIN_RUBY="3.3"
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
APP_DIR="${SCRIPT_DIR}"
|
|
|
|
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33mWARN:\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
require_root() {
|
|
[ "$(id -u)" -eq 0 ] || die "Run as root: sudo ./install.sh"
|
|
}
|
|
|
|
detect_distro() {
|
|
[ -r /etc/os-release ] || { printf 'unknown'; return; }
|
|
# shellcheck disable=SC1091
|
|
. /etc/os-release
|
|
printf '%s' "$(printf '%s' "${ID:-unknown}" | tr '[:upper:]' '[:lower:]')"
|
|
}
|
|
|
|
install_toolchain() {
|
|
case "$1" in
|
|
fedora)
|
|
log "Installing Ruby toolchain via dnf (Fedora)…"
|
|
dnf install -y ruby ruby-devel gcc gcc-c++ make redhat-rpm-config openssl-devel
|
|
;;
|
|
openeuler)
|
|
log "Installing Ruby toolchain via dnf (openEuler)…"
|
|
dnf install -y ruby ruby-devel gcc gcc-c++ make openssl-devel
|
|
;;
|
|
*)
|
|
warn "Automatic toolchain install is wired only for Fedora and openEuler."
|
|
cat >&2 <<'EOF'
|
|
Install Ruby >= 3.3 and a C toolchain manually, then re-run this script:
|
|
Debian/Ubuntu: apt install ruby-full build-essential libssl-dev
|
|
Arch: pacman -S ruby base-devel openssl
|
|
openSUSE: zypper install ruby ruby-devel gcc make libopenssl-devel
|
|
EOF
|
|
;;
|
|
esac
|
|
}
|
|
|
|
check_ruby() {
|
|
command -v ruby >/dev/null 2>&1 || die "Ruby not found; install Ruby >= ${MIN_RUBY} and re-run."
|
|
local version
|
|
version="$(ruby -e 'print RUBY_VERSION')"
|
|
if ! MIN="$MIN_RUBY" ruby -e 'exit(Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(ENV["MIN"]))'; then
|
|
die "Ruby ${version} is too old; volumen needs >= ${MIN_RUBY}."
|
|
fi
|
|
log "Ruby ${version} OK."
|
|
}
|
|
|
|
ensure_bundler() {
|
|
if ! command -v bundle >/dev/null 2>&1; then
|
|
log "Installing bundler…"
|
|
gem install --no-document bundler
|
|
fi
|
|
}
|
|
|
|
create_user() {
|
|
if id "$VOLUMEN_USER" >/dev/null 2>&1; then
|
|
log "User ${VOLUMEN_USER} already exists."
|
|
else
|
|
log "Creating system user ${VOLUMEN_USER}…"
|
|
useradd --system --home "$DATA_DIR" --shell /usr/sbin/nologin "$VOLUMEN_USER"
|
|
fi
|
|
}
|
|
|
|
create_dirs() {
|
|
log "Creating ${CONFIG_DIR} and ${CONTENT_DIR}…"
|
|
mkdir -p "$CONFIG_DIR" "$CONTENT_DIR"
|
|
chown -R "$VOLUMEN_USER:$VOLUMEN_USER" "$DATA_DIR"
|
|
}
|
|
|
|
install_gems() {
|
|
log "Installing gem dependencies…"
|
|
( cd "$APP_DIR" && bundle install )
|
|
}
|
|
|
|
generate_config() {
|
|
if [ -f "$CONFIG_FILE" ]; then
|
|
log "Config ${CONFIG_FILE} already exists; leaving it untouched."
|
|
return
|
|
fi
|
|
log "Generating ${CONFIG_FILE}…"
|
|
( cd "$APP_DIR" && bundle exec ruby -Ilib -rvolumen/config \
|
|
-e 'Volumen::Config.generate_default(ARGV[0])' "$CONFIG_FILE" )
|
|
sed -i 's/^host = .*/host = "127.0.0.1"/' "$CONFIG_FILE"
|
|
sed -i "s#^content_dir = .*#content_dir = \"${CONTENT_DIR}\"#" "$CONFIG_FILE"
|
|
sed -i "s#^users_file = .*#users_file = \"${USERS_FILE}\"#" "$CONFIG_FILE"
|
|
chown "$VOLUMEN_USER:$VOLUMEN_USER" "$CONFIG_FILE"
|
|
chmod 600 "$CONFIG_FILE"
|
|
}
|
|
|
|
install_service() {
|
|
log "Installing systemd unit ${SERVICE_FILE}…"
|
|
cat > "$SERVICE_FILE" <<EOF
|
|
[Unit]
|
|
Description=volumen blog engine
|
|
After=network.target
|
|
|
|
[Service]
|
|
User=${VOLUMEN_USER}
|
|
Group=${VOLUMEN_USER}
|
|
WorkingDirectory=${APP_DIR}
|
|
ExecStart=$(command -v bundle) exec exe/volumen serve --config ${CONFIG_FILE} --content ${CONTENT_DIR}
|
|
Restart=on-failure
|
|
RestartSec=2
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=${DATA_DIR}
|
|
PrivateTmp=true
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
systemctl daemon-reload
|
|
systemctl enable volumen
|
|
}
|
|
|
|
final_notes() {
|
|
cat <<EOF
|
|
|
|
volumen is installed and the service is enabled (not started yet).
|
|
|
|
Next steps:
|
|
1. Create the first admin password (you will log in as user "admin"):
|
|
cd ${APP_DIR}
|
|
sudo -u ${VOLUMEN_USER} $(command -v bundle) exec exe/volumen hash-password
|
|
Paste the printed hash into ${CONFIG_FILE} under [admin] password_hash.
|
|
2. (Recommended) set a stable admin.session_key in the config:
|
|
openssl rand -hex 64
|
|
3. Start the service:
|
|
sudo systemctl start volumen
|
|
4. Put nginx in front of 127.0.0.1:9090 — see docs/deployment.md.
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
require_root
|
|
local distro
|
|
distro="$(detect_distro)"
|
|
log "Detected distribution: ${distro}"
|
|
install_toolchain "$distro"
|
|
check_ruby
|
|
ensure_bundler
|
|
create_user
|
|
create_dirs
|
|
install_gems
|
|
generate_config
|
|
install_service
|
|
final_notes
|
|
}
|
|
|
|
main "$@"
|