aboutsummaryrefslogtreecommitdiff
path: root/common/install.sh
diff options
context:
space:
mode:
authorsrdusr <trevorgray@srdusr.com>2025-09-30 11:50:05 +0200
committersrdusr <trevorgray@srdusr.com>2025-09-30 11:50:05 +0200
commita6243585ba861c80d19f5f135170204e22bfc346 (patch)
tree0710801dd067d27e70988b7477a5aa06157b4d91 /common/install.sh
parent8051e7846625a06b30baa15868631de286f7ee5d (diff)
downloaddotfiles-a6243585ba861c80d19f5f135170204e22bfc346.tar.gz
dotfiles-a6243585ba861c80d19f5f135170204e22bfc346.zip
Various changes, fixed .zshenv initializing properly, updated install.sh/packages.yml (testing shell-git invocation/make tmp and re-exec with bash)
Diffstat (limited to 'common/install.sh')
-rwxr-xr-xcommon/install.sh200
1 files changed, 185 insertions, 15 deletions
diff --git a/common/install.sh b/common/install.sh
index 6ef2787..4f531bf 100755
--- a/common/install.sh
+++ b/common/install.sh
@@ -4,10 +4,18 @@
# Created On: Tue 06 Sep 2025 16:20:52 PM CAT
# Project: Dotfiles installation script
-# TODO: allow optional change user/password, also optional change root password, first check if they are the same (auto)
-
# Dependencies: git, curl
+# POSIX-compatible shim: if not running under bash (e.g., invoked via `sh -c "$(curl ...)"`),
+# re-exec the remainder of this script with bash.
+if [ -z "${BASH_VERSION:-}" ]; then
+ tmp="$(mktemp)" || exit 1
+ # Read the rest of the script into a temp file, then exec bash on it
+ cat > "$tmp"
+ exec bash "$tmp" "$@"
+ exit 1
+fi
+
set -euo pipefail # Exit on error, undefined vars, pipe failures
#======================================
@@ -140,6 +148,7 @@ declare -A INSTALLATION_STEPS=(
["install_dependencies"]="Install dependencies"
["install_dotfiles"]="Install dotfiles repository"
["setup_user_dirs"]="Setup user directories"
+ ["setup_passwords"]="Setup user and root passwords (optional)"
["install_essentials"]="Install essential tools"
["install_packages"]="Install system packages"
["setup_shell"]="Setup shell environment"
@@ -159,6 +168,7 @@ STEP_ORDER=(
"install_dotfiles"
"deploy_config"
"setup_user_dirs"
+ "setup_passwords"
"install_essentials"
"install_packages"
"setup_shell"
@@ -525,7 +535,18 @@ detect_package_manager() {
# Find packages.yml in standard locations
local original_dir="$PWD"
cd "$HOME" 2>/dev/null || true
- local packages_files=("$PACKAGES_FILE" "common/$PACKAGES_FILE" ".cfg/common/$PACKAGES_FILE")
+ # Search common locations for packages.yml, including repo-local and script directory
+ local __script_dir
+ __script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+ local packages_files=(
+ "$PACKAGES_FILE" \
+ "common/$PACKAGES_FILE" \
+ ".cfg/common/$PACKAGES_FILE" \
+ "$__script_dir/../packages.yml" \
+ "$__script_dir/packages.yml" \
+ "$DOTFILES_DIR/common/$PACKAGES_FILE" \
+ "$DOTFILES_DIR/packages.yml"
+ )
local found_packages_file=""
for pf in "${packages_files[@]}"; do
if [[ -f "$pf" ]]; then
@@ -535,17 +556,54 @@ detect_package_manager() {
done
cd "$original_dir" 2>/dev/null || true
+ # Optionally merge a profile overlay packages.yml over the base file
+ local merged_packages_file=""
if command_exists yq && [[ -n "$found_packages_file" ]]; then
+ local overlay_candidates=(
+ "$HOME/.cfg/profile/$INSTALL_MODE/packages.yml"
+ "$HOME/profile/$INSTALL_MODE/packages.yml"
+ "$__script_dir/../profile/$INSTALL_MODE/packages.yml"
+ "$__script_dir/profile/$INSTALL_MODE/packages.yml"
+ "$DOTFILES_DIR/profile/$INSTALL_MODE/packages.yml"
+ )
+ local overlay_file=""
+ for opf in "${overlay_candidates[@]}"; do
+ [[ -f "$opf" ]] && { overlay_file="$opf"; break; }
+ done
+ if [[ -n "$overlay_file" ]]; then
+ merged_packages_file="$(mktemp)"
+ if yq eval-all 'select(fileIndex==0) * select(fileIndex==1)' "$found_packages_file" "$overlay_file" >"$merged_packages_file" 2>/dev/null; then
+ print_info "Using merged packages.yml (base + profile overlay: $INSTALL_MODE)"
+ else
+ print_warning "Failed to merge profile packages overlay; using base packages.yml"
+ rm -f "$merged_packages_file" 2>/dev/null || true
+ merged_packages_file=""
+ fi
+ fi
+ fi
+
+ # If we have a resolved file, set PACKAGES_FILE to its absolute path for downstream steps
+ if [[ -n "$found_packages_file" ]]; then
+ if [[ -n "$merged_packages_file" ]]; then
+ PACKAGES_FILE="$merged_packages_file"
+ else
+ # Canonical absolute path
+ PACKAGES_FILE="$(cd "$(dirname "$found_packages_file")" && pwd -P)/$(basename "$found_packages_file")"
+ fi
+ export PACKAGES_FILE
+ fi
+
+ if command_exists yq && [[ -n "${merged_packages_file:-$found_packages_file}" ]]; then
# Prefer distro block, fallback to manager block
# Initialize to avoid set -u (nounset) issues before assignment
local pm_update="" pm_install=""
if [[ -n "$DISTRO" ]]; then
- pm_update=$(yq eval ".package_managers.${DISTRO}.update" "$found_packages_file" 2>/dev/null | grep -v "^null$" || echo "")
- pm_install=$(yq eval ".package_managers.${DISTRO}.install" "$found_packages_file" 2>/dev/null | grep -v "^null$" || echo "")
+ pm_update=$(yq eval ".package_managers.${DISTRO}.update" "${merged_packages_file:-$found_packages_file}" 2>/dev/null | grep -v "^null$" || echo "")
+ pm_install=$(yq eval ".package_managers.${DISTRO}.install" "${merged_packages_file:-$found_packages_file}" 2>/dev/null | grep -v "^null$" || echo "")
fi
if [[ -z "$pm_update" || -z "$pm_install" ]]; then
- pm_update=$(yq eval ".package_managers.${PACKAGE_MANAGER}.update" "$found_packages_file" 2>/dev/null | grep -v "^null$" || echo "")
- pm_install=$(yq eval ".package_managers.${PACKAGE_MANAGER}.install" "$found_packages_file" 2>/dev/null | grep -v "^null$" || echo "")
+ pm_update=$(yq eval ".package_managers.${PACKAGE_MANAGER}.update" "${merged_packages_file:-$found_packages_file}" 2>/dev/null | grep -v "^null$" || echo "")
+ pm_install=$(yq eval ".package_managers.${PACKAGE_MANAGER}.install" "${merged_packages_file:-$found_packages_file}" 2>/dev/null | grep -v "^null$" || echo "")
fi
if [[ -n "$pm_update" && -n "$pm_install" ]]; then
PACKAGE_UPDATE_CMD="$pm_update"
@@ -2110,6 +2168,119 @@ setup_user_dirs() {
mark_step_completed "setup_user_dirs"
}
+setup_passwords() {
+ print_section "Setting Up Passwords (Optional)"
+ save_state "setup_passwords" "started"
+
+ if [[ "$ASK_MODE" != true && "$FORCE_MODE" != true ]]; then
+ print_info "Skipping password setup (non-interactive). Use --ask to be prompted."
+ mark_step_completed "setup_passwords"
+ return 0
+ fi
+
+ # Change current user password
+ if prompt_user "Change password for user '$USER'?" "N"; then
+ print_color "$YELLOW" "Enter new password for $USER: "
+ read -rs __pw_user; echo
+ print_color "$YELLOW" "Confirm new password for $USER: "
+ read -rs __pw_user2; echo
+ if [[ "$__pw_user" == "$__pw_user2" && -n "$__pw_user" ]]; then
+ if execute_with_privilege "bash -lc 'echo \"$USER:$__pw_user\" | chpasswd'"; then
+ print_success "Password updated for $USER"
+ else
+ print_error "Failed to update password for $USER"
+ fi
+ else
+ print_warning "Passwords did not match; skipping $USER"
+ fi
+ unset __pw_user __pw_user2
+ else
+ print_skip "User password change (skipped)"
+ fi
+
+ # Change root password
+ if prompt_user "Change password for 'root'?" "N"; then
+ print_color "$YELLOW" "Enter new password for root: "
+ read -rs __pw_root; echo
+ print_color "$YELLOW" "Confirm new password for root: "
+ read -rs __pw_root2; echo
+ if [[ "$__pw_root" == "$__pw_root2" && -n "$__pw_root" ]]; then
+ if execute_with_privilege "bash -lc 'echo \"root:$__pw_root\" | chpasswd'"; then
+ print_success "Password updated for root"
+ else
+ print_error "Failed to update password for root"
+ fi
+ else
+ print_warning "Passwords did not match; skipping root"
+ fi
+ unset __pw_root __pw_root2
+ else
+ print_skip "Root password change (skipped)"
+ fi
+
+ mark_step_completed "setup_passwords"
+}
+
+# Safely sync a system file with backup. Usage: sync_system_file_with_backup /etc/target /path/to/source
+sync_system_file_with_backup() {
+ local target="$1" src="$2"
+ if [[ -z "$target" || -z "$src" ]]; then
+ print_error "sync_system_file_with_backup: missing arguments"
+ return 1
+ fi
+ if [[ ! -f "$src" ]]; then
+ print_error "Source file not found: $src"
+ return 1
+ fi
+ local backup="${target}.bak.$(date +%Y%m%d-%H%M%S)"
+ run_privileged "mkdir -p '$(dirname "$target")'" || return 1
+ if run_privileged "test -f '$target'"; then
+ run_privileged "cp -a '$target' '$backup'" || return 1
+ print_info "Backed up $target to $backup"
+ fi
+ run_privileged "install -m 644 '$src' '$target'" && print_success "Updated $target"
+}
+
+# Pre-install essentials (git, curl) early if missing
+preinstall_essentials() {
+ local need_any=false
+ command_exists git || need_any=true
+ command_exists curl || need_any=true
+ if [[ "$need_any" != true ]]; then
+ return 0
+ fi
+ detect_os
+ detect_package_manager || return 1
+ update_package_database || true
+ local ok=true
+ command_exists git || install_single_package git dependency || ok=false
+ command_exists curl || install_single_package curl dependency || ok=false
+ [[ "$ok" == true ]]
+}
+
+## Privileged file helpers (Utility)
+# Ensure a line exists in a file (exact match). Creates file and parent dir if needed. Uses privilege.
+ensure_line_in_file_privileged() {
+ local file="$1"
+ local line="$2"
+
+ # Create parent dir if needed
+ local dir
+ dir="$(dirname "$file")"
+ run_privileged "mkdir -p '$dir'" || return 1
+
+ # Create file if missing
+ run_privileged "touch '$file'" || return 1
+
+ # Check exact line presence
+ if run_privileged "grep -Fqx -- '$(printf %s "$line" | sed "s/'/'\\''/g")' '$file'"; then
+ return 0
+ fi
+
+ # Append safely
+ run_privileged "printf '%s\n' '$(printf %s "$line" | sed "s/'/'\\''/g")' >> '$file'"
+}
+
install_essentials() {
print_section "Installing Essential Tools"
save_state "install_essentials" "started"
@@ -2351,8 +2522,6 @@ setup_shell() {
mark_step_completed "setup_shell"
}
-## install_zsh_plugins deprecated; handled via packages.yml
-
setup_ssh() {
print_section "Setting Up SSH"
save_state "setup_ssh" "started"
@@ -3718,13 +3887,14 @@ trap handle_interrupt INT
# Execute main if script is run directly
if [[ "${BASH_SOURCE[0]-}" == "$0" ]]; then
- # Check basic requirements
- for req in git curl; do
- if ! command_exists "$req"; then
- print_error "$req is required but not installed"
+ # Ensure basic requirements (attempt auto-install if possible)
+ if ! command_exists git || ! command_exists curl; then
+ print_warning "git/curl missing; attempting to install prerequisites"
+ preinstall_essentials || {
+ print_error "Required tools git/curl are not installed and could not be auto-installed"
exit 1
- fi
- done
+ }
+ fi
main "$@"
fi