#!/bin/sh set -eu # See `parse_args()` for enabling dry run. dry_run="no" is_dry_run() { test "${dry_run}" = "yes" } setup_colors() { if [ -t 1 ]; then sgr0="$(printf '\033[0m')" bold="$(printf '\033[1m')" red="$(printf '\033[31m')" green="$(printf '\033[32m')" yellow="$(printf '\033[33m')" blue="$(printf '\033[34m')" else sgr0='' bold='' red='' green='' yellow='' blue='' fi } setup_commands() { dry='' is_dry_run && dry="echo " APT="${dry}apt" APT_FILE="${dry}apt-file" CD="${dry}cd" CHSH="${dry}chsh" CURL="${dry}curl" DPKG="${dry}dpkg" GIT="${dry}git" GZIP="${dry}gzip" HELP2MAN="${dry}help2man" MKDIR="${dry}mkdir" MV="${dry}mv" PANDOC="${dry}pandoc" RM="${dry}rm" RMDIR="${dry}rmdir" SED="${dry}sed" STOW="${dry}stow" SU="${dry}su" TAR="${dry}tar" UPDATE_GRUB="${dry}update-grub" USERADD="${dry}useradd" USERMOD="${dry}usermod" } usage() { echo "Usage: $(basename "$0") [-h] [-n]" echo "" echo " -h print this help and exit." echo " -n dry run, don't make changes." } error() { printf "${red}ERROR:${sgr0} %s\n" "$1" >&2 exit 1 } info() { printf "${green}->${sgr0} %s\n" "$1" } title() { echo "${blue}${bold}=> ${1}${sgr0}" } skipped() { echo "${yellow}SKIPPED:${sgr0} ${1}" } apt_install() { title "Install APT packages" info "Updating package database" ${APT} update info "Installing APT packages" ${APT} install -y \ apt-file \ ascii \ build-essential \ ca-certificates \ cmake \ cmake-doc \ curl \ exuberant-ctags \ fzf \ g++ \ gcc \ git \ git-crypt \ gnupg \ help2man \ htop \ jq \ make \ man-db \ nodejs \ pandoc \ pkg-config \ psmisc \ python3 \ python3-virtualenv \ ripgrep \ rsync \ shellcheck \ sshpass \ stow \ sudo \ tmux \ unzip \ zsh info "Updating apt-file database" ${APT_FILE} update } grub_disable_timeout() { title "Disable GRUB timeout" ${SED} -i.original -e 's/^GRUB_TIMEOUT=.*/GRUB_TIMEOUT=0/' /etc/default/grub ${UPDATE_GRUB} } ensure_usr_local_man_manN() { title "Make sure we have directories for all man page sections" for dir in $(seq -f '/usr/local/man/man%.0f' 9); do ${MKDIR} -vp "${dir}" done } user_setup() { title "Setup user: $1" if user_exists "$1"; then info "User $1 exists. Updating..." user_update "$1" else info "Creating user $1" user_new "$1" fi user_allow_sudo_nopasswd "$1" } user_exists() { id -u "$1" >/dev/null 2>&1 } user_new() { empty_skel="$(mktemp -d)" ${USERADD} \ -m ` # Create home directory.` \ -k "$empty_skel" ` # Copy files from this directory into the new home.` \ -U ` # Create a groups with the same name as the user.` \ -G staff ` # Other groups the new user will be a member of.` \ -s /bin/zsh ` # The new user's login shell. ` \ "$1" ` # The new user's name.` \ 2>&1 ` # Silently.` \ ${RMDIR} "$empty_skel" } # Add user $1 to the `staff` group... # ...and change shell to `zsh` and get rid of bash files. user_update() { info "Adding $1 to group staff" ${USERMOD} -aG staff "$1" info "Changing login shell to zsh" ${CHSH} -s /bin/zsh "$1" info "Removing bash leftover files" for f in .bash_history .bash_logout .bashrc .profile; do ${RM} -vf "/home/${1}/${f}" done } # Allow `sudo` without password for user $1. user_allow_sudo_nopasswd() { info "Enabling sudo without password for ${1}" contents="$1 ALL=(ALL:ALL) NOPASSWD:ALL" sudoers_file="/etc/sudoers.d/${1}_nopasswd" if is_dry_run; then echo "echo \"$contents\" >$sudoers_file" else echo "$contents" >"$sudoers_file" fi } # Deploy dotfiles for user $1 from URL $2. deploy_dotfiles() { title "Deploy dotfiles" dotfiles_dir="/home/$1/.dotfiles" if [ -d "${dotfiles_dir}" ]; then skipped "${dotfiles_dir} exists" else info "Cloning dotfiles" ${SU} "$1" -c "git clone $2 ${dotfiles_dir}" ( info "Installing dotfiles" ${CD} "${dotfiles_dir}" ${SU} "$1" -c "./install.sh -y" ) fi } install_eza() ( title "Install eza v${1}" ${MKDIR} eza ${CD} eza pkg="eza-${1}" info "Creating directory structure" for subdir in bin share/man/man1 share/man/man5 share/zsh/vendor-completions; do ${MKDIR} -p "${pkg}/${subdir}" done info "Packaging executable from release archive" ${CURL} -LO "https://github.com/eza-community/eza/releases/download/v${1}/eza_x86_64-unknown-linux-gnu.tar.gz" ${TAR} -xf eza_x86_64-unknown-linux-gnu.tar.gz ${MV} -v eza "${pkg}/bin/" info "Cloning repository for man pages and shell completions" ${GIT} clone --branch "v${1}" --depth 1 https://github.com/eza-community/eza.git repo info "Generating man pages" for page in eza.1 eza_colors.5 eza_colors-explanation.5; do ${SED} -i -e "s/\$version/v${1}/g" "repo/man/${page}.md" ${PANDOC} \ --standalone \ --from markdown \ --to man \ --output "${pkg}/share/man/man${page##*.}/${page}" \ "repo/man/${page}.md" ${GZIP} -n9 "${pkg}/share/man/man${page##*.}/${page}" done; info "Packaging zsh comletions" ${MV} -v "repo/completions/zsh/_eza" "${pkg}/share/zsh/vendor-completions/" info "Installing package" ${MV} "${pkg}" /usr/local/stow/ ${STOW} -v -d /usr/local/stow "${pkg}" ) install_fd() ( title "Install fd v${1}" ${MKDIR} fd ${CD} fd pkg="fd-${1}" info "Downloading release archive" ${CURL} -LO "https://github.com/sharkdp/fd/releases/download/v${1}/fd_${1}_amd64.deb" info "Extracting release archive" ${DPKG} --extract "fd_${1}_amd64.deb" . ${MV} -v usr "${pkg}" info "Installing package" ${MV} "${pkg}" /usr/local/stow/ ${STOW} -v -d /usr/local/stow "${pkg}" ) install_git_delta() ( title "Install git-delta v${1}" ${MKDIR} git-delta ${CD} git-delta pkg="git-delta-${1}" info "Downloading release archive" ${CURL} -LO "https://github.com/dandavison/delta/releases/download/${1}/git-delta_${1}_amd64.deb" info "Extracting release archive" ${DPKG} --extract "git-delta_${1}_amd64.deb" . ${MV} -v usr "${pkg}" info "Creating additional directories" for subdir in share/man/man1 share/zsh/vendor-completions; do ${MKDIR} -p "${pkg}/${subdir}" done info "Cloning repository for shell completions" ${GIT} clone --branch "${1}" --depth 1 https://github.com/dandavison/delta.git repo info "Packaging zsh completions" ${MV} -v "repo/etc/completion/completion.zsh" "${pkg}/share/zsh/vendor-completions/_delta" info "Generating man pages" ${HELP2MAN} --no-info --output "git-delta.1" "${pkg}/bin/delta" ${SED} -i -e 's/\x1b\[[0-9;]*[Mm]//g' git-delta.1 ${GZIP} -n9 git-delta.1 ${MV} -v git-delta.1.gz "${pkg}/share/man/man1/" info "Installing package" ${MV} "${pkg}" /usr/local/stow/ ${STOW} -v -d /usr/local/stow "${pkg}" ) parse_args() { while getopts 'hn' opt; do case "$opt" in h) # help usage exit 0 ;; n) # dry run dry_run="yes" ;; *) # invalid argument usage exit 1 ;; esac done } require_root() { [ "$(id -u)" -eq 0 ] || error "This script must be run as root!" } user="fernando" dotfiles_url="https://git.schauenburg.me/fernando/dotfiles.git" main() { setup_colors parse_args "$@" is_dry_run || require_root setup_commands apt_install install_eza 0.20.4 install_fd 10.2.0 install_git_delta 0.18.2 grub_disable_timeout ensure_usr_local_man_manN user_setup "$user" deploy_dotfiles "$user" "$dotfiles_url" } main "$@"