1
0
Fork 0
bootstrapper/bootstrap.sh

317 lines
8.4 KiB
Bash
Raw Normal View History

2022-08-23 21:04:44 +02:00
#!/bin/sh
2024-06-26 18:52:20 +02:00
set -eu
2024-01-01 22:29:15 +01:00
DOTS_REPO="https://git.snaile.de/luca/dotfiles"
DOTS_BRANCH="main"
STOW_DIR=".local/share/stow"
DOTS_PACKAGE="dots"
2024-06-26 18:52:20 +02:00
CONFIG_FILE="./config.yml"
2024-07-16 13:52:00 +02:00
LOG_FILE="bootstrap.log"
2022-08-23 21:04:44 +02:00
2024-07-20 14:55:31 +02:00
USER_GROUPS="wheel,floppy,audio,video,cdrom,optical,kvm,xbuilder,users" # Comma separated list
2024-01-01 22:29:15 +01:00
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
2022-08-23 21:04:44 +02:00
error() {
2024-05-25 00:04:59 +02:00
printf "%b\n" "${RED}${BOLD}${1}${RESET}" >&2
2024-07-16 13:52:00 +02:00
echo "$1" >>"$LOG_FILE"
2024-05-25 00:04:59 +02:00
exit 1
2022-08-23 21:04:44 +02:00
}
2024-01-01 22:29:15 +01:00
prompt() {
2024-05-25 00:04:59 +02:00
message=$1
printf "%b" "${BLUE}${message}${RESET}" >"$(tty)"
# shellcheck disable=SC3037,SC2046
read -r x
echo "$x"
unset x
2022-08-23 21:04:44 +02:00
}
2024-01-01 22:29:15 +01:00
emphasize() {
2024-07-16 13:52:00 +02:00
message=$1
printf "%-$(tput cols)b\n" "${GREEN}${BOLD}${message}${RESET}"
echo "$1" >>"$LOG_FILE"
2022-08-23 21:04:44 +02:00
}
2024-01-01 22:29:15 +01:00
info() {
2024-07-16 13:52:00 +02:00
message=$1
printf "%-$(tput cols)b\n" "${message}"
echo "$1" >>"$LOG_FILE"
}
2024-01-01 22:29:15 +01:00
warn() {
2024-07-16 13:52:00 +02:00
message=$1
printf "%-$(tput cols)b\n" "${YELLOW}${BOLD}${message}${RESET}"
echo "$1" >>"$LOG_FILE"
2022-08-23 21:04:44 +02:00
}
2024-06-12 17:18:33 +02:00
print_buffer() {
buffer_size=$1
2024-07-16 13:52:00 +02:00
trim=${2:-y} # 'y' | 'n'
line_length=$(tput cols)
2024-06-12 17:18:33 +02:00
for i in $(seq 1 "$((buffer_size))"); do
if [ "$trim" = "y" ]; then
2024-07-16 13:52:00 +02:00
template="%-${line_length}s\n"
2024-06-12 17:18:33 +02:00
else
2024-07-16 13:52:00 +02:00
template="%-${line_length}.${line_length}s\n"
2024-06-12 17:18:33 +02:00
fi
2024-06-26 18:52:20 +02:00
eval '[ -z "${buffer'"$i"':-}" ] || {
2024-07-16 13:52:00 +02:00
printf "'"$template"'" "${buffer'"$i"'}"
2024-06-26 18:52:20 +02:00
}'
2024-06-12 17:18:33 +02:00
done
}
push_buffer() {
line=$1
buffer_size=$2
2024-06-26 18:52:20 +02:00
for i in $(seq 1 $((buffer_size - 1))); do
eval 'buffer'"$i"'="${buffer'$((i + 1))':-}"'
2024-06-12 17:18:33 +02:00
done
2024-06-26 18:52:20 +02:00
eval 'buffer'"$buffer_size"'=''''"$line"'''''
2024-06-12 17:18:33 +02:00
}
scroll() {
buffer_size=$1 # int
trim=${2:-y} # 'y' | 'n'
tput civis
while read -r line; do
push_buffer "$line" "$buffer_size"
2024-07-16 13:52:00 +02:00
echo "$line" >>"$LOG_FILE"
2024-06-12 17:18:33 +02:00
tput sc
2024-07-16 13:52:00 +02:00
print_buffer "$buffer_size" "$trim"
2024-06-12 17:18:33 +02:00
tput rc
done
tput cnorm
2024-07-16 13:52:00 +02:00
tput sc
2024-06-12 17:18:33 +02:00
for i in $(seq 1 $((buffer_size))); do
2024-07-16 13:52:00 +02:00
printf "%-$(tput cols)s" ""
2024-06-26 18:52:20 +02:00
eval "unset buffer$i"
2024-06-12 17:18:33 +02:00
done
2024-07-16 13:52:00 +02:00
tput rc
print_buffer "$buffer_size" "$trim"
2024-06-12 17:18:33 +02:00
}
2024-01-01 22:29:15 +01:00
check_root() {
2024-06-26 18:52:20 +02:00
[ "$(id -u)" = "0" ] || error "This script needs root!"
2024-01-01 22:29:15 +01:00
}
setup() {
2024-07-20 14:55:31 +02:00
echo "Synchronizing XBPS index..." | tee -pa "$LOG_FILE"
xbps-install -Sy xbps void-repo-nonfree >/dev/null 2>&1 || echo "Failed to synchronize XBPS index! (Try manually running xbps-install -S)" | tee -pa "$LOG_FILE"
2024-01-01 22:29:15 +01:00
2024-05-25 00:04:59 +02:00
if ! xbps-query ntp >/dev/null 2>&1; then
2024-07-20 14:55:31 +02:00
echo "Installing script dependencies..." | tee -pa "$LOG_FILE"
progs="ntp jq yq git stow fzf ncurses"
2024-06-26 18:52:20 +02:00
# shellcheck disable=SC2086
xbps-install -y $progs >/dev/null 2>&1
2024-01-01 22:29:15 +01:00
2024-07-20 14:55:31 +02:00
echo "Synchronizing time..." | tee -pa "$LOG_FILE"
2024-05-25 00:04:59 +02:00
ntpdate "pool.ntp.org" >/dev/null 2>&1 || warn "Failed to synchronize time!"
fi
2024-01-01 22:29:15 +01:00
2024-07-20 14:55:31 +02:00
echo "Done!" | tee -pa "$LOG_FILE"
2024-01-01 22:29:15 +01:00
}
install_packages() {
2024-06-26 18:52:20 +02:00
IFS='
'
2024-07-16 13:52:00 +02:00
set +e
2024-06-26 18:52:20 +02:00
for p in $(yq -c '.packages.[]' <config.yml); do
2024-07-20 14:55:31 +02:00
command="$(echo "$p" | yq -r '.command[0]')"
pre_command="$(echo "$p" | yq -r '.pre[]' 2>/dev/null)"
2024-06-26 18:52:20 +02:00
flags=$(echo "$p" | yq -r '.command[1:].[]')
packages=$(echo "$p" | yq -r '.list[]')
2024-07-20 14:55:31 +02:00
[ -n "${packages}" ] && {
$pre_command
case $(echo "$p" | yq -r '.local') in
true)
echo "$flags" "$packages" | sudo -i -u "$username" xargs "$(echo "$p" | yq -r '.command[0]')" ||
touch /tmp/bootstrapper-failed
;;
false)
echo "$flags" "$packages" | xargs "$command" ||
touch /tmp/bootstrapper-failed
;;
esac
}
2024-07-16 13:52:00 +02:00
done 2>&1 | scroll 7
set -e
if [ -f /tmp/bootstrapper-failed ]; then
warn "Failures during package installation, check logs."
rm /tmp/bootstrapper-failed
else
info "Done!"
fi
2022-08-23 21:04:44 +02:00
}
2024-01-01 22:29:15 +01:00
install_files() {
2024-05-25 00:04:59 +02:00
(
cd "${SCRIPT_DIR}/files" || exit 1
2024-07-24 19:07:37 +02:00
find . -type d -exec mkdir -p "/{}" \;
find . -type f -exec install -o root -g root "{}" "/{}" \;
find . -type l -exec cp -d "{}" "/{}" \;
2024-05-25 00:04:59 +02:00
)
info "Done!"
2022-08-23 21:04:44 +02:00
}
2024-01-01 22:29:15 +01:00
create_user() {
2024-05-25 00:04:59 +02:00
failed=false
while ! echo "$username" | grep "^[a-z_][a-z0-9_-]*$" | grep -qv "root"; do
$failed && warn "Invalid username, try again!"
username=$(prompt "Input Username: ")
failed=true
done
if id -u "$username" >/dev/null 2>&1; then
warn "User \"$username\" already exists, Skipping user creation!"
usermod -G "$USER_GROUPS" "$username"
else
info "Creating user \"$username\" with the following groups: \"$USER_GROUPS\"..."
useradd -m -G "$USER_GROUPS" "$username"
failed=false
2024-07-20 14:55:31 +02:00
while [ -z "${pass1:-}" ] || [ "${pass1:-}" != "${pass2:-}" ]; do
2024-05-25 00:04:59 +02:00
$failed && warn "Passwords do not match or are empty, try again!"
pass1=$(prompt "Input Password: ")
pass2=$(prompt "Repeat Password: ")
failed=true
done
echo "$username:$pass1" | chpasswd
fi
user_home=$(getent passwd "$username" | cut -d ':' -f 6)
[ -z "$username" ] &&
error "\$username variable is empty, this script is bugged!"
[ -z "$user_home" ] &&
error "\$user_home variable is empty, this script is bugged!"
sudo -u "$username" [ -w "$user_home" ] || error "$username can't write to '$user_home'!"
info "Done!"
2024-01-01 22:29:15 +01:00
}
create_directories() {
2024-05-25 00:04:59 +02:00
#shellcheck disable=SC2016
2024-07-16 13:52:00 +02:00
directories=$(yq -c '.directories.[]' <${CONFIG_FILE})
2024-06-26 18:52:20 +02:00
counter=1
num_dirs=$(echo "$directories" | wc -l)
for entry in $directories; do
path=$(echo "$entry" | jq -r '.path')
mode=$(echo "$entry" | jq -r '.mode')
[ "$mode" = "null" ] && mode="0755"
info "Creating directory ${counter} of ${num_dirs}: ~/${path}"
2024-07-20 14:55:31 +02:00
[ -d "${user_home}/${path}" ] || mkdir -m "${mode}" -p "${user_home}/${path}"
2024-06-26 18:52:20 +02:00
counter=$((counter + 1))
2024-07-16 13:52:00 +02:00
done 2>&1 | scroll 7
2024-06-26 18:52:20 +02:00
2024-05-25 00:04:59 +02:00
info "Done!"
2024-01-01 22:29:15 +01:00
}
install_dotfiles() {
2024-05-25 00:04:59 +02:00
info "Cloning dotfiles..."
mkdir -p "${user_home}/${STOW_DIR}"
if [ ! -d "${user_home}/${STOW_DIR}/${DOTS_PACKAGE}/.git" ]; then
if ! git -C "${user_home}/${STOW_DIR}" clone -q --recurse-submodules -b "$DOTS_BRANCH" "$DOTS_REPO" "$DOTS_PACKAGE"; then
warn "Failed to clone dotfiles"
return 1
fi
fi
info "Symlinking dotfiles..."
if ! stow -d "$user_home/$STOW_DIR" -t "$user_home" "$DOTS_PACKAGE" 1>/dev/null 2>&1; then
warn "Failed to symlink dotfiles"
return 2
fi
info "Done!"
2024-01-01 22:29:15 +01:00
}
2024-01-04 14:09:46 +01:00
select_keymap() {
2024-05-25 00:04:59 +02:00
[ -L "${user_home}/.local/share/xkb/compiled/keymap" ] && return
map="$(find "${user_home}/.local/share/xkb/compiled" -type f -printf "%f\n" | fzf --header="Select a keymap keymap:")"
ln -s "$map" "${user_home}/.local/share/xkb/compiled/keymap"
2024-01-04 14:09:46 +01:00
}
2024-07-20 14:55:31 +02:00
# FIX: This has to work in a chroot
2024-01-01 22:29:15 +01:00
enable_services() {
2024-06-26 18:52:20 +02:00
services=$(yq -r '.services[]' <config.yml)
counter=1
num_services=$(echo "$services" | wc -l)
info "Removing old services..."
2024-02-27 10:15:16 +01:00
for sv in /var/service/*; do
2024-06-26 18:52:20 +02:00
echo "$services" | grep -qx "$(basename "$sv")" || rm "$sv"
2024-02-27 10:15:16 +01:00
done
2024-06-26 18:52:20 +02:00
info "Enabling services..."
for service in $services; do
info "Enabling service ${counter} of ${num_services}: ${service}"
[ ! -L "/var/service/${service}" ] && ln -s "/etc/sv/${service}" "/var/service/"
2024-07-20 14:55:31 +02:00
counter=$((counter + 1))
2024-07-16 13:52:00 +02:00
done 2>&1 | scroll 7
2024-06-26 18:52:20 +02:00
2024-05-25 00:04:59 +02:00
info "Done!"
2022-08-23 21:04:44 +02:00
}
finalize() {
2024-05-25 00:04:59 +02:00
gid=$(getent passwd "$username" | cut -d ':' -f 4)
groupname=$(getent group "$gid" | cut -d ':' -f 1)
2024-06-12 13:19:52 +02:00
info "Setting ownership of home directories..."
2024-07-16 13:52:00 +02:00
chown "$username:$groupname" -R "$user_home"
2024-05-25 00:04:59 +02:00
info "Done!"
}
2022-08-23 21:04:44 +02:00
2024-01-01 22:29:15 +01:00
### CONTROL FLOW BEGINS HERE ###
2022-08-23 21:04:44 +02:00
2024-06-12 17:18:33 +02:00
trap 'tput cnorm; tput sgr0; exit' INT TERM EXIT
2024-07-16 13:52:00 +02:00
echo >"$LOG_FILE"
2024-01-01 22:29:15 +01:00
check_root
2024-07-20 14:55:31 +02:00
setup
BOLD="$(tput bold)"
RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
YELLOW="$(tput setaf 3)"
BLUE="$(tput setaf 4)"
RESET="$(tput sgr0)"
2022-08-23 21:04:44 +02:00
2024-01-13 13:29:39 +01:00
emphasize "-- Copying Files --"
install_files
2024-06-26 18:52:20 +02:00
# emphasize "-- Creating Symlinks --"
# create_symlinks
2024-07-20 14:55:31 +02:00
username="${SUDO_USER:-}"
if [ -z "$username" ]; then
2024-05-25 00:04:59 +02:00
emphasize "-- Creating User Account --"
create_user
else
2024-05-25 00:04:59 +02:00
user_home=$(getent passwd "$username" | cut -d ':' -f 6)
fi
2022-08-23 21:04:44 +02:00
2024-01-01 22:29:15 +01:00
emphasize "-- Installing Dotfiles --"
install_dotfiles
2024-01-04 14:09:46 +01:00
select_keymap
2022-08-23 21:04:44 +02:00
2024-07-16 13:52:00 +02:00
emphasize "-- Creating Standard Home Directories --"
create_directories
emphasize "-- Installing Packages --"
install_packages
2024-01-01 22:29:15 +01:00
emphasize "-- Enabling Services --"
enable_services
2022-08-23 21:04:44 +02:00
2024-01-01 22:29:15 +01:00
emphasize "-- Finalizing Installation --"
2022-08-23 21:04:44 +02:00
finalize
2024-01-01 22:29:15 +01:00
emphasize "-- Installation Complete --"