Merge pull request #315 from community-scripts/MickLesk-patch-2

Bump tools.func - includes refactor: deb822 | new functions: setup_mariadb_db, setup_postgresql_db | fixes: setup_uv output, setup_rust (update-handling), setup_java (output bugfix)
This commit is contained in:
Michel Roegl-Brunner
2025-11-13 13:26:56 +01:00
committed by GitHub

View File

@@ -72,15 +72,23 @@ stop_all_services() {
local service_patterns=("$@")
for pattern in "${service_patterns[@]}"; do
# Find all matching services
systemctl list-units --type=service --all 2>/dev/null |
grep -oE "${pattern}[^ ]*\.service" |
sort -u |
while read -r service; do
# Find all matching services (use || true to avoid pipeline failures)
local services
services=$(systemctl list-units --type=service --all 2>/dev/null |
grep -oE "${pattern}[^ ]*\.service" 2>/dev/null |
sort -u 2>/dev/null || true)
# Only process if we found any services
if [[ -n "$services" ]]; then
while IFS= read -r service; do
[[ -z "$service" ]] && continue
$STD systemctl stop "$service" 2>/dev/null || true
$STD systemctl disable "$service" 2>/dev/null || true
done <<<"$services"
fi
done
done
return 0
}
# ------------------------------------------------------------------------------
@@ -1198,65 +1206,49 @@ ensure_apt_working() {
}
# ------------------------------------------------------------------------------
# Standardized deb822 repository setup
# Validates all parameters and fails safely if any are empty
# Standardized deb822 repository setup (with optional Architectures)
# Always runs apt update after repo creation to ensure package availability
# ------------------------------------------------------------------------------
setup_deb822_repo() {
local name="$1"
local gpg_url="$2"
local repo_url="$3"
local suite="$4"
local component="${5:-main}"
local architectures="${6:-$(dpkg --print-architecture)}"
local component="${5-main}"
local architectures="${6-}" # optional
# Validate required parameters
if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then
msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)"
msg_error "setup_deb822_repo: missing required parameters (name=$name repo=$repo_url suite=$suite)"
return 1
fi
# Cleanup old configs for this app
# Cleanup
cleanup_old_repo_files "$name"
# Cleanup any orphaned .sources files from other apps
cleanup_orphaned_sources
# Ensure keyring directory exists
mkdir -p /etc/apt/keyrings || {
msg_error "Failed to create /etc/apt/keyrings directory"
msg_error "Failed to create /etc/apt/keyrings"
return 1
}
# Download GPG key (with --yes to avoid interactive prompts)
curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || {
msg_error "Failed to download or import GPG key for ${name} from $gpg_url"
# Import GPG
curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" || {
msg_error "Failed to import GPG key for ${name}"
return 1
}
# Create deb822 sources file
cat <<EOF >/etc/apt/sources.list.d/${name}.sources
Types: deb
URIs: $repo_url
Suites: $suite
Components: $component
Architectures: $architectures
Signed-By: /etc/apt/keyrings/${name}.gpg
EOF
# Write deb822
{
echo "Types: deb"
echo "URIs: $repo_url"
echo "Suites: $suite"
echo "Components: $component"
[[ -n "$architectures" ]] && echo "Architectures: $architectures"
echo "Signed-By: /etc/apt/keyrings/${name}.gpg"
} >/etc/apt/sources.list.d/${name}.sources
# Use cached apt update
local apt_cache_file="/var/cache/apt-update-timestamp"
local current_time=$(date +%s)
local last_update=0
if [[ -f "$apt_cache_file" ]]; then
last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0)
fi
# For repo changes, always update but respect short-term cache (30s)
if ((current_time - last_update > 30)); then
$STD apt update
echo "$current_time" >"$apt_cache_file"
fi
}
# ------------------------------------------------------------------------------
@@ -1415,7 +1407,7 @@ verify_gpg_fingerprint() {
}
# ==============================================================================
# EXISTING FUNCTIONS
# INSTALL FUNCTIONS
# ==============================================================================
# ------------------------------------------------------------------------------
@@ -1517,7 +1509,7 @@ check_for_gh_release() {
return 0
fi
msg_error "No update available: ${app} is not installed!"
msg_ok "No update available: ${app} is already on pinned version (${current})"
return 1
fi
@@ -2795,8 +2787,9 @@ function setup_java() {
fi
# Validate INSTALLED_VERSION is not empty if matched
local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0")
if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then
local JDK_COUNT=0
JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk")
if [[ -z "$INSTALLED_VERSION" && "${JDK_COUNT:-0}" -gt 0 ]]; then
msg_warn "Found Temurin JDK but cannot determine version"
INSTALLED_VERSION="0"
fi
@@ -3060,6 +3053,85 @@ setup_mariadb() {
msg_ok "Setup MariaDB $MARIADB_VERSION"
}
# ------------------------------------------------------------------------------
# Creates MariaDB database with user, charset and optional extra grants/modes
#
# Description:
# - Generates password if empty
# - Creates database with utf8mb4_unicode_ci
# - Creates local user with password
# - Grants full access to this DB
# - Optional: apply extra GRANT statements (comma-separated)
# - Optional: apply custom GLOBAL sql_mode
# - Saves credentials to file
# - Exports variables for use in calling script
#
# Usage:
# MARIADB_DB_NAME="myapp_db" MARIADB_DB_USER="myapp_user" setup_mariadb_db
# MARIADB_DB_NAME="domain_monitor" MARIADB_DB_USER="domainmonitor" setup_mariadb_db
# MARIADB_DB_NAME="myapp" MARIADB_DB_USER="myapp" MARIADB_DB_EXTRA_GRANTS="GRANT SELECT ON \`mysql\`.\`time_zone_name\`" setup_mariadb_db
# MARIADB_DB_NAME="ghostfolio" MARIADB_DB_USER="ghostfolio" MARIADB_DB_SQL_MODE="" setup_mariadb_db
#
# Variables:
# MARIADB_DB_NAME - Database name (required)
# MARIADB_DB_USER - Database user (required)
# MARIADB_DB_PASS - User password (optional, auto-generated if empty)
# MARIADB_DB_EXTRA_GRANTS - Comma-separated GRANT statements (optional)
# Example: "GRANT SELECT ON \`mysql\`.\`time_zone_name\`"
# MARIADB_DB_SQL_MODE - Optional global sql_mode override (e.g. "", "STRICT_TRANS_TABLES")
# MARIADB_DB_CREDS_FILE - Credentials file path (optional, default: ~/${APPLICATION}.creds)
#
# Exports:
# MARIADB_DB_NAME, MARIADB_DB_USER, MARIADB_DB_PASS
# ------------------------------------------------------------------------------
function setup_mariadb_db() {
if [[ -z "${MARIADB_DB_NAME:-}" || -z "${MARIADB_DB_USER:-}" ]]; then
msg_error "MARIADB_DB_NAME and MARIADB_DB_USER must be set before calling setup_mariadb_db"
return 1
fi
if [[ -z "${MARIADB_DB_PASS:-}" ]]; then
MARIADB_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
fi
msg_info "Setting up MariaDB Database"
$STD mariadb -u root -e "CREATE DATABASE \`$MARIADB_DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
$STD mariadb -u root -e "CREATE USER '$MARIADB_DB_USER'@'localhost' IDENTIFIED BY '$MARIADB_DB_PASS';"
$STD mariadb -u root -e "GRANT ALL ON \`$MARIADB_DB_NAME\`.* TO '$MARIADB_DB_USER'@'localhost';"
# Optional extra grants
if [[ -n "${MARIADB_DB_EXTRA_GRANTS:-}" ]]; then
IFS=',' read -ra G_LIST <<<"${MARIADB_DB_EXTRA_GRANTS:-}"
for g in "${G_LIST[@]}"; do
g=$(echo "$g" | xargs)
$STD mariadb -u root -e "$g TO '$MARIADB_DB_USER'@'localhost';"
done
fi
# Optional sql_mode override
if [[ -n "${MARIADB_DB_SQL_MODE:-}" ]]; then
$STD mariadb -u root -e "SET GLOBAL sql_mode='${MARIADB_DB_SQL_MODE:-}';"
fi
$STD mariadb -u root -e "FLUSH PRIVILEGES;"
local CREDS_FILE="${MARIADB_DB_CREDS_FILE:-${HOME}/${APPLICATION}.creds}"
{
echo "MariaDB Credentials"
echo "Database: $MARIADB_DB_NAME"
echo "User: $MARIADB_DB_USER"
echo "Password: $MARIADB_DB_PASS"
} >>"$CREDS_FILE"
msg_ok "Set up MariaDB Database"
export MARIADB_DB_NAME
export MARIADB_DB_USER
export MARIADB_DB_PASS
}
# ------------------------------------------------------------------------------
# Installs or updates MongoDB to specified major version.
#
@@ -3819,6 +3891,103 @@ function setup_postgresql() {
fi
}
# ------------------------------------------------------------------------------
# Creates PostgreSQL database with user and optional extensions
#
# Description:
# - Creates PostgreSQL role with login and password
# - Creates database with UTF8 encoding and template0
# - Installs optional extensions (postgis, pgvector, etc.)
# - Configures ALTER ROLE settings for Django/Rails compatibility
# - Saves credentials to file
# - Exports variables for use in calling script
#
# Usage:
# PG_DB_NAME="myapp_db" PG_DB_USER="myapp_user" setup_postgresql_db
# PG_DB_NAME="immich" PG_DB_USER="immich" PG_DB_EXTENSIONS="pgvector" setup_postgresql_db
# PG_DB_NAME="ghostfolio" PG_DB_USER="ghostfolio" PG_DB_GRANT_SUPERUSER="true" setup_postgresql_db
# PG_DB_NAME="adventurelog" PG_DB_USER="adventurelog" PG_DB_EXTENSIONS="postgis" setup_postgresql_db
#
# Variables:
# PG_DB_NAME - Database name (required)
# PG_DB_USER - Database user (required)
# PG_DB_PASS - Database password (optional, auto-generated if empty)
# PG_DB_EXTENSIONS - Comma-separated list of extensions (optional, e.g. "postgis,pgvector")
# PG_DB_GRANT_SUPERUSER - Grant SUPERUSER privilege (optional, "true" to enable, security risk!)
# PG_DB_SCHEMA_PERMS - Grant schema-level permissions (optional, "true" to enable)
# PG_DB_SKIP_ALTER_ROLE - Skip ALTER ROLE settings (optional, "true" to skip)
# PG_DB_CREDS_FILE - Credentials file path (optional, default: ~/${APPLICATION}.creds)
#
# Exports:
# PG_DB_NAME, PG_DB_USER, PG_DB_PASS - For use in calling script
# ------------------------------------------------------------------------------
function setup_postgresql_db() {
# Validation
if [[ -z "${PG_DB_NAME:-}" || -z "${PG_DB_USER:-}" ]]; then
msg_error "PG_DB_NAME and PG_DB_USER must be set before calling setup_postgresql_db"
return 1
fi
# Generate password if not provided
if [[ -z "${PG_DB_PASS:-}" ]]; then
PG_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
fi
msg_info "Setting up PostgreSQL Database"
$STD sudo -u postgres psql -c "CREATE ROLE $PG_DB_USER WITH LOGIN PASSWORD '$PG_DB_PASS';"
$STD sudo -u postgres psql -c "CREATE DATABASE $PG_DB_NAME WITH OWNER $PG_DB_USER ENCODING 'UTF8' TEMPLATE template0;"
# Install extensions (comma-separated)
if [[ -n "${PG_DB_EXTENSIONS:-}" ]]; then
IFS=',' read -ra EXT_LIST <<<"${PG_DB_EXTENSIONS:-}"
for ext in "${EXT_LIST[@]}"; do
ext=$(echo "$ext" | xargs) # Trim whitespace
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS $ext;"
done
fi
# ALTER ROLE settings for Django/Rails compatibility (unless skipped)
if [[ "${PG_DB_SKIP_ALTER_ROLE:-}" != "true" ]]; then
$STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET client_encoding TO 'utf8';"
$STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET default_transaction_isolation TO 'read committed';"
$STD sudo -u postgres psql -c "ALTER ROLE $PG_DB_USER SET timezone TO 'UTC';"
fi
# Schema permissions (if requested)
if [[ "${PG_DB_SCHEMA_PERMS:-}" == "true" ]]; then
$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DB_NAME TO $PG_DB_USER;"
$STD sudo -u postgres psql -c "ALTER USER $PG_DB_USER CREATEDB;"
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT ALL ON SCHEMA public TO $PG_DB_USER;"
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $PG_DB_USER;"
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $PG_DB_USER;"
$STD sudo -u postgres psql -d "$PG_DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $PG_DB_USER;"
fi
# Superuser grant (if requested - WARNING!)
if [[ "${PG_DB_GRANT_SUPERUSER:-}" == "true" ]]; then
msg_warn "Granting SUPERUSER privilege (security risk!)"
$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $PG_DB_NAME to $PG_DB_USER;"
$STD sudo -u postgres psql -c "ALTER USER $PG_DB_USER WITH SUPERUSER;"
fi
# Save credentials
local CREDS_FILE="${PG_DB_CREDS_FILE:-${HOME}/${APPLICATION}.creds}"
{
echo "PostgreSQL Credentials"
echo "Database: $PG_DB_NAME"
echo "User: $PG_DB_USER"
echo "Password: $PG_DB_PASS"
} >>"$CREDS_FILE"
msg_ok "Set up PostgreSQL Database"
# Export for use in calling script
export PG_DB_NAME
export PG_DB_USER
export PG_DB_PASS
}
# ------------------------------------------------------------------------------
# Installs rbenv and ruby-build, installs Ruby and optionally Rails.
#
@@ -4172,12 +4341,28 @@ function setup_rust() {
}
export PATH="$CARGO_BIN:$PATH"
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile"
# Verify installation
if ! command -v rustc >/dev/null 2>&1; then
msg_error "Rust binary not found after installation"
return 1
fi
local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}')
if [[ -z "$RUST_VERSION" ]]; then
msg_error "Failed to determine Rust version"
return 1
fi
cache_installed_version "rust" "$RUST_VERSION"
msg_ok "Setup Rust $RUST_VERSION"
else
# Scenario 2: Rustup already installed - update/maintain
msg_info "Update Rust ($RUST_TOOLCHAIN)"
# Ensure default toolchain is set
$STD rustup default "$RUST_TOOLCHAIN" 2>/dev/null || {
# If default fails, install the toolchain first
$STD rustup install "$RUST_TOOLCHAIN" || {
msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN"
return 1
@@ -4186,17 +4371,33 @@ function setup_rust() {
msg_error "Failed to set default Rust toolchain"
return 1
}
}
# Update to latest patch version
$STD rustup update "$RUST_TOOLCHAIN" || true
# Ensure PATH is updated for current shell session
export PATH="$CARGO_BIN:$PATH"
local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}')
if [[ -z "$RUST_VERSION" ]]; then
msg_error "Failed to determine Rust version after update"
return 1
fi
cache_installed_version "rust" "$RUST_VERSION"
msg_ok "Update Rust $RUST_VERSION"
fi
# Install global crates
if [[ -n "$RUST_CRATES" ]]; then
msg_info "Processing Rust crates: $RUST_CRATES"
IFS=',' read -ra CRATES <<<"$RUST_CRATES"
for crate in "${CRATES[@]}"; do
local NAME VER INSTALLED_VER
crate=$(echo "$crate" | xargs) # trim whitespace
[[ -z "$crate" ]] && continue # skip empty entries
local NAME VER INSTALLED_VER CRATE_LIST
if [[ "$crate" == *"@"* ]]; then
NAME="${crate%@*}"
VER="${crate##*@}"
@@ -4205,18 +4406,50 @@ function setup_rust() {
VER=""
fi
INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v')
# Get list of installed crates once
CRATE_LIST=$(cargo install --list 2>/dev/null || echo "")
# Check if already installed
if echo "$CRATE_LIST" | grep -q "^${NAME} "; then
INSTALLED_VER=$(echo "$CRATE_LIST" | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:')
if [[ -n "$INSTALLED_VER" ]]; then
if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then
$STD cargo install "$NAME" --version "$VER" --force
msg_info "Upgrading $NAME from v$INSTALLED_VER to v$VER"
$STD cargo install "$NAME" --version "$VER" --force || {
msg_error "Failed to install $NAME@$VER"
return 1
}
msg_ok "Upgraded $NAME to v$VER"
elif [[ -z "$VER" ]]; then
$STD cargo install "$NAME" --force
msg_info "Upgrading $NAME to latest"
$STD cargo install "$NAME" --force || {
msg_error "Failed to upgrade $NAME"
return 1
}
local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:')
msg_ok "Upgraded $NAME to v$NEW_VER"
else
msg_ok "$NAME v$INSTALLED_VER already installed"
fi
else
$STD cargo install "$NAME" ${VER:+--version "$VER"}
msg_info "Installing $NAME${VER:+@$VER}"
if [[ -n "$VER" ]]; then
$STD cargo install "$NAME" --version "$VER" || {
msg_error "Failed to install $NAME@$VER"
return 1
}
msg_ok "Installed $NAME v$VER"
else
$STD cargo install "$NAME" || {
msg_error "Failed to install $NAME"
return 1
}
local NEW_VER=$(cargo install --list 2>/dev/null | grep "^${NAME} " | head -1 | awk '{print $2}' | tr -d 'v:')
msg_ok "Installed $NAME v$NEW_VER"
fi
fi
done
msg_ok "Processed Rust crates"
fi
}
@@ -4353,7 +4586,9 @@ function setup_uv() {
# Optional: Generate shell completions
$STD uv generate-shell-completion bash >/etc/bash_completion.d/uv 2>/dev/null || true
if [[ -d /usr/share/zsh/site-functions ]]; then
$STD uv generate-shell-completion zsh >/usr/share/zsh/site-functions/_uv 2>/dev/null || true
fi
# Optional: Install specific Python version if requested
if [[ -n "${PYTHON_VERSION:-}" ]]; then