refactor: clean up installer for pre-built tarball releases

- Removed npm dependency installation from installer (tarballs include deps)
- Cleaned up output with better icons and formatting
- Simplified installation flow since tarballs are pre-built
- Reduced installer from 590 to 522 lines
- Fixed messaging to be cleaner and more professional

The installer now expects properly built tarballs from create-release.sh
that include all dependencies and pre-built CSS.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
courtmanr@gmail.com
2025-05-31 15:29:05 +01:00
parent 946ecc9b05
commit 788ef90cc9
+96 -178
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# Pulse for Proxmox VE - Simplified Installer
# Pulse for Proxmox VE - Installer
# https://github.com/rcourtman/Pulse
NODE_MAJOR_VERSION=20
@@ -24,7 +24,7 @@ while [[ "$#" -gt 0 ]]; do
SPECIFIED_VERSION_TAG="$2"
shift 2
else
echo "Error: --version requires a tag name [e.g., v3.2.3]" >&2
echo "Error: --version requires a tag name (e.g., v3.16.0)" >&2
exit 1
fi
;;
@@ -34,55 +34,35 @@ done
# Color output functions
print_info() {
echo -e "\033[1;34m[INFO]\033[0m $1"
echo -e "\033[0;36m➜\033[0m $1"
}
print_success() {
echo -e "\033[1;32m[SUCCESS]\033[0m $1"
echo -e "\033[0;32m\033[0m $1"
}
print_warning() {
echo -e "\033[1;33m[WARNING]\033[0m $1"
echo -e "\033[0;33m\033[0m $1"
}
print_error() {
echo -e "\033[1;31m[ERROR]\033[0m $1" >&2
echo -e "\033[0;31m\033[0m $1" >&2
}
# Check if running as root
check_root() {
if [ "$(id -u)" -ne 0 ]; then
print_error "This script must be run as root. Please use sudo."
print_error "This script must be run as root"
exit 1
fi
}
# Print welcome banner
print_welcome() {
echo -e "\033[1;34m"
echo ' ____ _ '
echo '| _ \ _ _| |___ ___ '
echo '| |_) | | | | / __|/ _ \'
echo '| __/| |_| | \__ \ __/'
echo '|_| \__,_|_|___/\___|'
echo ' '
echo -e "\033[0m"
echo ""
echo -e "\033[1mWelcome to the Pulse for Proxmox VE Installer\033[0m"
echo "This script will install Pulse, a lightweight monitoring application."
echo "See README.md for more details: https://github.com/rcourtman/Pulse"
echo -e "\033[1;34mPulse for Proxmox VE Installer\033[0m"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
print_info "Required Dependencies:"
print_info "- Core Tools: curl, sudo, gpg, tar (via apt)"
print_info "- Runtime: Node.js & npm (via NodeSource)"
echo ""
read -p "Do you want to proceed? [Y/n]: " confirm_proceed
if [[ "$confirm_proceed" =~ ^[Nn]$ ]]; then
print_error "Operation aborted by user."
exit 1
fi
print_info "Proceeding..."
}
# Get latest release tag from GitHub
@@ -93,7 +73,6 @@ get_latest_release_tag() {
latest_tag=$(curl -s "$api_url" | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
if [ -z "$latest_tag" ] || [ "$latest_tag" = "null" ]; then
print_error "Could not determine latest release tag"
return 1
fi
@@ -105,18 +84,14 @@ check_release_exists() {
local tag=$1
local api_url="https://api.github.com/repos/rcourtman/Pulse/releases/tags/$tag"
if curl -s --fail "$api_url" > /dev/null 2>&1; then
return 0
else
return 1
fi
curl -s --fail "$api_url" > /dev/null 2>&1
}
# Download and extract tarball
download_and_extract_tarball() {
local version=$1
local temp_dir="/tmp/pulse-install-$$"
local tarball_url="${REPO_BASE_URL}/archive/refs/tags/${version}.tar.gz"
local tarball_url="${REPO_BASE_URL}/releases/download/${version}/pulse-${version}.tar.gz"
print_info "Downloading Pulse ${version}..."
@@ -124,9 +99,9 @@ download_and_extract_tarball() {
mkdir -p "$temp_dir"
cd "$temp_dir" || exit 1
# Download tarball
if ! curl -L -o "pulse.tar.gz" "$tarball_url"; then
print_error "Failed to download tarball from $tarball_url"
# Download tarball with progress
if ! curl -L --progress-bar -o "pulse.tar.gz" "$tarball_url"; then
print_error "Failed to download release"
rm -rf "$temp_dir"
return 1
fi
@@ -134,38 +109,36 @@ download_and_extract_tarball() {
# Extract tarball
print_info "Extracting files..."
if ! tar -xzf "pulse.tar.gz"; then
print_error "Failed to extract tarball"
print_error "Failed to extract files"
rm -rf "$temp_dir"
return 1
fi
# Find extracted directory (format: Pulse-{version without v})
local extracted_dir=$(find . -maxdepth 1 -type d -name "Pulse-*" | head -1)
# Find extracted directory
local extracted_dir=$(find . -maxdepth 1 -type d -name "pulse-*" | head -1)
if [ -z "$extracted_dir" ]; then
print_error "Could not find extracted directory"
print_error "Invalid release archive"
rm -rf "$temp_dir"
return 1
fi
# Backup existing data if updating
if [ -d "$PULSE_DIR/data" ]; then
print_info "Backing up existing data..."
print_info "Backing up user data..."
cp -r "$PULSE_DIR/data" "$temp_dir/data-backup"
fi
# Remove old installation
if [ -d "$PULSE_DIR" ]; then
print_info "Removing old installation..."
rm -rf "$PULSE_DIR"
fi
# Move extracted files to installation directory
print_info "Installing files to $PULSE_DIR..."
# Move to installation directory
mv "$extracted_dir" "$PULSE_DIR"
# Restore data if it existed
if [ -d "$temp_dir/data-backup" ]; then
print_info "Restoring data..."
print_info "Restoring user data..."
cp -r "$temp_dir/data-backup"/* "$PULSE_DIR/data/" 2>/dev/null || true
fi
@@ -173,7 +146,7 @@ download_and_extract_tarball() {
cd /
rm -rf "$temp_dir"
print_success "Files extracted successfully"
print_success "Installation files ready"
return 0
}
@@ -181,8 +154,6 @@ download_and_extract_tarball() {
get_current_version() {
if [ -f "$PULSE_DIR/package.json" ]; then
grep '"version"' "$PULSE_DIR/package.json" | head -1 | sed -E 's/.*"version": "([^"]+)".*/\1/'
else
echo ""
fi
}
@@ -202,48 +173,58 @@ check_installation() {
elif [ "$pulse_exists" = true ] || [ "$service_exists" = true ]; then
local current_version=$(get_current_version)
if [ -n "$current_version" ]; then
print_info "Pulse v$current_version is currently installed"
print_info "Pulse v$current_version is installed"
else
print_info "Pulse is currently installed"
print_info "Pulse is installed"
fi
if [ -n "$MODE_UPDATE" ]; then
INSTALL_MODE="update"
else
echo ""
echo "What would you like to do?"
echo "Choose an action:"
echo " 1) Update to latest version"
echo " 2) Remove Pulse"
echo " 3) Cancel"
read -p "Enter your choice [1-3]: " choice
echo ""
read -p "Your choice [1-3]: " choice
case $choice in
1) INSTALL_MODE="update" ;;
2) INSTALL_MODE="remove" ;;
3) print_info "Operation cancelled"; exit 0 ;;
3) print_info "Cancelled"; exit 0 ;;
*) print_error "Invalid choice"; exit 1 ;;
esac
fi
else
INSTALL_MODE="install"
print_info "Pulse is not installed"
fi
}
# Install system dependencies
install_dependencies() {
print_info "Installing system dependencies..."
print_info "Checking system dependencies..."
apt-get update || { print_error "Failed to update package lists"; exit 1; }
# Update package lists quietly
apt-get update -qq || { print_error "Failed to update package lists"; exit 1; }
# Check and install missing dependencies
local deps="curl sudo gpg tar"
local missing_deps=""
for dep in $deps; do
if ! command -v $dep &> /dev/null; then
print_info "Installing $dep..."
apt-get install -y $dep || { print_error "Failed to install $dep"; exit 1; }
missing_deps="$missing_deps $dep"
fi
done
print_success "System dependencies installed"
if [ -n "$missing_deps" ]; then
print_info "Installing:$missing_deps"
apt-get install -y -qq $missing_deps || { print_error "Failed to install dependencies"; exit 1; }
fi
print_success "System dependencies ready"
}
# Setup Node.js
@@ -251,7 +232,7 @@ setup_node() {
if command -v node &> /dev/null; then
local current_version=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$current_version" -ge "$NODE_MAJOR_VERSION" ]; then
print_info "Node.js v$(node -v) is already installed"
print_success "Node.js $(node -v) is installed"
return 0
fi
fi
@@ -263,8 +244,8 @@ setup_node() {
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
apt-get update || { print_error "Failed to update package lists"; exit 1; }
apt-get install -y nodejs || { print_error "Failed to install Node.js"; exit 1; }
apt-get update -qq || { print_error "Failed to update package lists"; exit 1; }
apt-get install -y -qq nodejs || { print_error "Failed to install Node.js"; exit 1; }
print_success "Node.js $(node -v) installed"
}
@@ -272,84 +253,32 @@ setup_node() {
# Create pulse user
create_pulse_user() {
if id "$PULSE_USER" &>/dev/null; then
print_info "User $PULSE_USER already exists"
print_success "User '$PULSE_USER' exists"
else
print_info "Creating user $PULSE_USER..."
print_info "Creating user '$PULSE_USER'..."
useradd -r -s /bin/false -d /nonexistent -U "$PULSE_USER" || {
print_error "Failed to create user $PULSE_USER"
print_error "Failed to create user"
exit 1
}
print_success "User $PULSE_USER created"
print_success "User created"
fi
}
# Install npm dependencies
install_npm_deps() {
print_info "Installing NPM dependencies..."
cd "$PULSE_DIR" || exit 1
# Check if node_modules exists (from tarball)
if [ -d "node_modules" ] && [ -f "package-lock.json" ]; then
print_info "Dependencies already included in release"
# Verify dependencies are working
if npm list --depth=0 &>/dev/null; then
print_success "NPM dependencies verified"
# Check if CSS is already built
if [ -f "src/public/index.css" ]; then
print_info "CSS assets already built in release"
return 0
fi
else
print_warning "Dependencies need to be reinstalled"
rm -rf node_modules
fi
fi
# For tarball installations, we need to install ALL dependencies first to build CSS
print_info "Installing all dependencies (including dev) for CSS build..."
npm ci || npm install || {
print_error "Failed to install NPM dependencies"
exit 1
}
# Build CSS if needed
if [ ! -f "src/public/output.css" ]; then
print_info "Building CSS assets..."
npm run build:css || {
print_error "Failed to build CSS assets"
exit 1
}
print_success "CSS assets built"
else
print_info "CSS assets already exist"
fi
# Now remove dev dependencies to save space
print_info "Removing development dependencies..."
npm prune --omit=dev || print_warning "Failed to prune dev dependencies"
print_success "NPM dependencies installed and optimized"
}
# Set file permissions
set_permissions() {
print_info "Setting file permissions..."
print_info "Setting permissions..."
chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR"
chmod -R 755 "$PULSE_DIR"
# Ensure data directory exists and has correct permissions
# Ensure data directory exists
mkdir -p "$PULSE_DIR/data"
chown "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/data"
chmod 755 "$PULSE_DIR/data"
print_success "Permissions set"
}
# Configure environment
configure_environment() {
print_info "Configuring environment..."
local env_file="$PULSE_DIR/.env"
if [ ! -f "$env_file" ]; then
cat > "$env_file" << EOF
@@ -359,13 +288,11 @@ EOF
chown "$PULSE_USER:$PULSE_USER" "$env_file"
chmod 600 "$env_file"
fi
print_success "Environment configured"
}
# Setup systemd service
setup_systemd_service() {
print_info "Setting up systemd service..."
print_info "Configuring service..."
cat > "/etc/systemd/system/$SERVICE_NAME" << EOF
[Unit]
@@ -390,19 +317,25 @@ WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl enable "$SERVICE_NAME" &>/dev/null
systemctl start "$SERVICE_NAME"
# Wait a moment and check if service started
sleep 2
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_success "Service started successfully"
print_success "Service started"
else
print_error "Service failed to start. Check: journalctl -u $SERVICE_NAME -n 50"
print_error "Service failed to start"
print_info "Check logs: journalctl -u $SERVICE_NAME -n 50"
exit 1
fi
}
# Perform installation
perform_install() {
print_info "Installing Pulse..."
echo ""
install_dependencies
setup_node
create_pulse_user
@@ -417,36 +350,28 @@ perform_install() {
else
TARGET_TAG=$(get_latest_release_tag)
if [ -z "$TARGET_TAG" ]; then
print_error "Could not determine version to install"
print_error "Could not determine latest version"
exit 1
fi
fi
print_info "Installing Pulse $TARGET_TAG..."
download_and_extract_tarball "$TARGET_TAG" || exit 1
install_npm_deps
set_permissions
configure_environment
setup_systemd_service
echo ""
print_success "Installation complete!"
}
# Perform update
perform_update() {
# Stop service
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_info "Stopping $SERVICE_NAME..."
print_info "Stopping service..."
systemctl stop "$SERVICE_NAME"
fi
# Backup user data
local backup_dir="/tmp/pulse-backup-$$"
if [ -d "$PULSE_DIR/data" ]; then
print_info "Backing up user data..."
mkdir -p "$backup_dir"
cp -r "$PULSE_DIR/data" "$backup_dir/"
fi
# Determine target version
if [ -n "$SPECIFIED_VERSION_TAG" ]; then
TARGET_TAG="$SPECIFIED_VERSION_TAG"
@@ -457,37 +382,31 @@ perform_update() {
else
TARGET_TAG=$(get_latest_release_tag)
if [ -z "$TARGET_TAG" ]; then
print_error "Could not determine version to update to"
print_error "Could not determine latest version"
exit 1
fi
fi
local current_version=$(get_current_version)
if [ "$current_version" = "${TARGET_TAG#v}" ]; then
print_info "Already running version $TARGET_TAG"
print_info "Already running latest version"
systemctl start "$SERVICE_NAME"
return 0
fi
print_info "Updating from v$current_version to $TARGET_TAG..."
print_info "Updating to $TARGET_TAG..."
echo ""
download_and_extract_tarball "$TARGET_TAG" || exit 1
# Restore user data
if [ -d "$backup_dir/data" ]; then
print_info "Restoring user data..."
cp -r "$backup_dir/data"/* "$PULSE_DIR/data/" 2>/dev/null || true
rm -rf "$backup_dir"
fi
install_npm_deps
set_permissions
# Restart service
systemctl start "$SERVICE_NAME"
sleep 2
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_success "Update completed successfully"
echo ""
print_success "Update complete!"
else
print_error "Service failed to start after update"
exit 1
@@ -496,49 +415,45 @@ perform_update() {
# Remove Pulse
perform_remove() {
print_warning "This will remove Pulse completely from your system"
print_warning "This will completely remove Pulse"
read -p "Are you sure? [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
print_info "Removal cancelled"
print_info "Cancelled"
exit 0
fi
echo ""
# Stop and disable service
if systemctl is-active --quiet "$SERVICE_NAME"; then
print_info "Stopping service..."
systemctl stop "$SERVICE_NAME"
fi
systemctl disable "$SERVICE_NAME" 2>/dev/null || true
systemctl disable "$SERVICE_NAME" &>/dev/null || true
rm -f "/etc/systemd/system/$SERVICE_NAME"
systemctl daemon-reload
# Remove directory
if [ -d "$PULSE_DIR" ]; then
rm -rf "$PULSE_DIR"
fi
# Remove old directory if exists
if [ -d "$OLD_PULSE_DIR" ]; then
rm -rf "$OLD_PULSE_DIR"
fi
# Remove directories
[ -d "$PULSE_DIR" ] && rm -rf "$PULSE_DIR"
[ -d "$OLD_PULSE_DIR" ] && rm -rf "$OLD_PULSE_DIR"
print_success "Pulse has been removed"
}
# Migrate from old installation
perform_migration() {
print_info "Migrating from $OLD_PULSE_DIR to $PULSE_DIR..."
print_info "Migrating old installation..."
# Stop old service if running
# Stop old service
if systemctl is-active --quiet "pulse-proxmox.service"; then
systemctl stop "pulse-proxmox.service"
fi
systemctl disable "pulse-proxmox.service" 2>/dev/null || true
systemctl disable "pulse-proxmox.service" &>/dev/null || true
# Backup old data if exists
# Backup old data
local backup_dir="/tmp/pulse-migration-$$"
if [ -d "$OLD_PULSE_DIR/data" ]; then
print_info "Backing up old data..."
mkdir -p "$backup_dir"
cp -r "$OLD_PULSE_DIR/data" "$backup_dir/"
fi
@@ -553,21 +468,24 @@ perform_migration() {
# Restore old data
if [ -d "$backup_dir/data" ]; then
print_info "Restoring old data..."
print_info "Restoring data..."
cp -r "$backup_dir/data"/* "$PULSE_DIR/data/" 2>/dev/null || true
chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/data"
rm -rf "$backup_dir"
fi
}
# Final instructions
# Show final instructions
show_final_instructions() {
local ip=$(hostname -I | awk '{print $1}')
echo ""
print_success "Pulse installation completed!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e "Access Pulse: \033[1;36mhttp://${ip}:3000\033[0m"
echo ""
print_info "Access Pulse at: http://$(hostname -I | awk '{print $1}'):3000"
print_info "Service status: systemctl status $SERVICE_NAME"
print_info "View logs: journalctl -u $SERVICE_NAME -f"
echo "Useful commands:"
echo " systemctl status $SERVICE_NAME"
echo " journalctl -u $SERVICE_NAME -f"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
}
@@ -594,7 +512,7 @@ main() {
show_final_instructions
;;
*)
print_error "Invalid operation mode"
print_error "Invalid operation"
exit 1
;;
esac