🎉 feat: Initial bootstrapping
This commit is contained in:
parent
98e9b2539c
commit
963f480ff6
5 changed files with 599 additions and 0 deletions
8
git/gitconfig.local.symlink.example
Normal file
8
git/gitconfig.local.symlink.example
Normal file
|
@ -0,0 +1,8 @@
|
|||
# vim:set ft=toml sw=4:
|
||||
|
||||
[user]
|
||||
name = AUTHORNAME
|
||||
email = AUTHOREMAIL
|
||||
|
||||
[credential]
|
||||
helper = GIT_CREDENTIAL_HELPER
|
0
homebrew/brew.sh
Normal file
0
homebrew/brew.sh
Normal file
0
os/pref.sh
Normal file
0
os/pref.sh
Normal file
344
script/dot.sh
Normal file
344
script/dot.sh
Normal file
|
@ -0,0 +1,344 @@
|
|||
#!/usr/bin/env bash
|
||||
# vim:set ft=bash:
|
||||
|
||||
declare -r GITHUB_REPO="punkfairie/dotfiles"
|
||||
|
||||
declare -r DOTFILES_ORIGIN="git@github.com:$GITHUB_REPO.git"
|
||||
declare -r DOTFILES_TARBALL="https://github.com/$GITHUB_REPO/tarball/main"
|
||||
declare -r DOTFILES_UTILS="https://raw.githubusercontent.com/$GITHUB_REPO/main/scripts/utils.sh"
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
declare dotfiles_dir="$HOME/dotfiles"
|
||||
declare yes_to_all=false
|
||||
|
||||
################################################################################
|
||||
# Download Dotfiles #
|
||||
################################################################################
|
||||
|
||||
download()
|
||||
{
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
|
||||
if command -v "curl" &> /dev/null; then
|
||||
curl \
|
||||
--location \
|
||||
--silent \
|
||||
--show-error \
|
||||
--output "$output" \
|
||||
"$url" \
|
||||
&> /dev/null
|
||||
|
||||
return $?
|
||||
|
||||
elif command -v "wget" &> /dev/null; then
|
||||
wget \
|
||||
--quiet \
|
||||
--output-document="$output" \
|
||||
"$url" \
|
||||
&> /dev/null
|
||||
|
||||
return $?
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
download_utils()
|
||||
{
|
||||
local tmp_file=""
|
||||
|
||||
tmp_file="$(mktemp /tmp/XXXXX)"
|
||||
|
||||
download "$DOTFILES_UTILS" "$tmp_file" \
|
||||
&& . "$tmp_file" \
|
||||
&& rm -rf "$tmp_file" \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
extract()
|
||||
{
|
||||
local archive="$1"
|
||||
local output_dir="$2"
|
||||
|
||||
if command -v "tar" &> /dev/null; then
|
||||
tar \
|
||||
--extract \
|
||||
--gzip \
|
||||
--file "$archive" \
|
||||
--strip-components 1 \
|
||||
--directory "$output_dir"
|
||||
|
||||
return $?
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
download_dotfiles()
|
||||
{
|
||||
local tmp_file=""
|
||||
|
||||
print_title "Download and extract dotfiles archive"
|
||||
|
||||
tmp_file="$(mktemp /tmp/XXXXX)"
|
||||
|
||||
download "$DOTFILES_TARBALL" "$tmp_file"
|
||||
print_result $? "Download archive" "true"
|
||||
printf "\n"
|
||||
|
||||
if ! $yes_to_all; then
|
||||
|
||||
while [[ -e $dotfiles_dir ]]; do
|
||||
ask "'$dotfiles_dir' already exists, do you want to (o)verwrite or (b)ackup the existing directory?"
|
||||
answer="$(get_answer)"
|
||||
|
||||
case $answer in
|
||||
o ) rm -rf "$dotfiles_dir"; break;;
|
||||
b ) mv "$dotfiles_dir" "$dotfiles_dir.bak"; break;;
|
||||
* ) print_warning "Please enter a valid option."
|
||||
esac
|
||||
|
||||
done
|
||||
|
||||
else
|
||||
rm -rf "$dotfiles_dir" &> /dev/null
|
||||
fi
|
||||
|
||||
mkdir -p "$dotfiles_dir"
|
||||
print_result $? "Create '$dotfiles_dir'" "true"
|
||||
|
||||
# Extract archive.
|
||||
extract "$tmp_file" "$dotfiles_dir"
|
||||
print_result $? "Extract archive" "true"
|
||||
|
||||
rm -rf "$tmp_file"
|
||||
print_result $? "Remove archive"
|
||||
|
||||
cd "$dotfiles_dir/script" \
|
||||
|| return 1
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Setup Gitconfig #
|
||||
################################################################################
|
||||
|
||||
setup_gitconfig()
|
||||
{
|
||||
cd "$dotfiles_dir"
|
||||
|
||||
if ! [[ -f $dotfiles_dir/git/gitconfig.local.symlink ]]; then
|
||||
print_title "Set up gitconfig"
|
||||
|
||||
git_credential="cache"
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
git_credential="osxkeychain"
|
||||
fi
|
||||
|
||||
print_question "What is your Github author name?"
|
||||
read -e git_authorname
|
||||
|
||||
print_question "What is your Github author email?"
|
||||
read -e git_authoremail
|
||||
|
||||
sed -e "s/AUTHORNAME/$git_authorname/g" \
|
||||
-e "s/AUTHOREMAIL/$git_authoremail/g" \
|
||||
-e "s/GIT_CREDENTIAL_HELPER/$git_credential/g" \
|
||||
$dotfiles_dir/git/gitconfig.local.symlink.example > $dotfiles_dir/gitconfig.local.symlink
|
||||
|
||||
print_result $? "gitconfig"
|
||||
fi
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Install Dotfiles #
|
||||
################################################################################
|
||||
|
||||
link_file()
|
||||
{
|
||||
local src=$1
|
||||
local dst=$2
|
||||
|
||||
local overwrite=
|
||||
local backup=
|
||||
local skip=
|
||||
local action=
|
||||
|
||||
if [[ -f "$dst" -o -d "$dst" -o -L "$dst" ]]; then
|
||||
|
||||
if ! $overwrite_all && ! $backup_all && ! $skip_all; then
|
||||
local current_src="$(readlink $dst)"
|
||||
|
||||
if [[ "$current_src" == "$src" ]]; then
|
||||
skip=true
|
||||
|
||||
else
|
||||
print_question "File already exists: $dst ($(basename "$src")), what do you want to do?\n\
|
||||
[s]kip, [S]kip all, [o]verwrite, [O]verwrite all, [b]ackup, [B]ackup all?"
|
||||
read -n 1 action
|
||||
|
||||
case "$action" in
|
||||
o ) overwrite=true ;;
|
||||
O ) overwrite_all=true ;;
|
||||
b ) backup=true ;;
|
||||
B ) backup_all=true ;;
|
||||
s ) skip=true ;;
|
||||
S ) skip_all=true ;;
|
||||
* ) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
overwrite=${overwrite:-$overwrite_all}
|
||||
backup=${backup:-$backup_all}
|
||||
skip=${skip:-$skip_all}
|
||||
|
||||
if $overwrite; then
|
||||
rm -rf "$dst"
|
||||
print_success "Removed $dst"
|
||||
fi
|
||||
|
||||
if $backup; then
|
||||
mv "$dst" "${dst}.bak"
|
||||
print_success "Moved $dst to ${dst}.bak"
|
||||
fi
|
||||
|
||||
if $skip; then
|
||||
print_success "Skipped $src"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if ! $skip; then
|
||||
ln -s "$src" "$dst"
|
||||
print_success "Linked $src to $dst"
|
||||
fi
|
||||
}
|
||||
|
||||
install_dotfiles()
|
||||
{
|
||||
print_title "Installing dotfiles"
|
||||
|
||||
local overwrite_all=false
|
||||
local backup_all=false
|
||||
local skip_all=false
|
||||
|
||||
for src in $(find -H "$dotfiles_dir" -maxdepth 2 -name "*.symlink" -not -path "*.git*"); do
|
||||
dst="$HOME/$(basename "${src%.*}")"
|
||||
link_file "$src" "$dst"
|
||||
done
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Initialize Git Repo #
|
||||
################################################################################
|
||||
|
||||
git_init()
|
||||
{
|
||||
print_title "Initialize Git repository"
|
||||
|
||||
if [[ -z "$DOTFILES_ORIGIN" ]]; then
|
||||
print_error "Please provide a URL for the Git origin"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! is_git_repository; then
|
||||
cd $dotfiles_dir || print_error "Failed to cd $dotfiles_dir"
|
||||
|
||||
execute \
|
||||
"git init && git remote add origin $DOTFILES_ORIGIN" \
|
||||
"Initialize the dotfiles Git repository"
|
||||
fi
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Restart OS #
|
||||
################################################################################
|
||||
|
||||
restart_os()
|
||||
{
|
||||
print_title "Restart"
|
||||
|
||||
ask_for_confirmation "Do you want to restart?"
|
||||
printf "\n"
|
||||
|
||||
if answer_is_yes; then
|
||||
sudo shutdown -r now &> /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Main #
|
||||
################################################################################
|
||||
|
||||
main()
|
||||
{
|
||||
if [[ "$(uname)" != "Linux" ]] && [[ "$(uname)" != "Darwin" ]]; then
|
||||
printf "Sorry, this script is intended only for macOS and Ubuntu!"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Load utils.
|
||||
|
||||
if [[ -x "${dotfiles_dir}/script/utils.sh" ]]; then
|
||||
. "${dotfiles_dir}/script/utils.sh" || exit 1
|
||||
else
|
||||
download_utils || exit 1
|
||||
fi
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
skip_questions "$@" \
|
||||
&& yes_to_all=true
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
ask_for_sudo
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
# Check if this script was run directly, and if not, dotfiles will need to be
|
||||
# downloaded.
|
||||
|
||||
printf "%s" "${BASH_SOURCE[0]}" | grep "dot.sh" &> /dev/null \
|
||||
|| download_dotfiles
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
setup_gitconfig
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
install_dotfiles
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
$dotfiles_dir/os/pref.sh
|
||||
|
||||
$dotfiles_dir/homebrew/brew.sh
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
find . -name install.sh | while read installer ; do sh -c "${installer}" ; done
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
if cmd_exists "git"; then
|
||||
if [[ "$(git config --get remote.origin.url)" != "$DOTFILES_ORIGIN" ]]; then
|
||||
git_init
|
||||
fi
|
||||
fi
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
if ! $yes_to_all; then
|
||||
restart_os
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
247
script/utils.sh
Normal file
247
script/utils.sh
Normal file
|
@ -0,0 +1,247 @@
|
|||
#!/usr/bin/env bash
|
||||
# vim:set ft=bash:
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
skip_questions()
|
||||
{
|
||||
while :; do
|
||||
case $1 in
|
||||
-y | --yes ) return 0 ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
|
||||
shift 1
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
ask_for_sudo()
|
||||
{
|
||||
sudo -v &> /dev/null
|
||||
|
||||
# Update existing 'sudo' timestamp until this script has finished.
|
||||
#
|
||||
# https://gist.github.com/cowboy/3118588
|
||||
|
||||
while true; do
|
||||
sudo -n true
|
||||
sleep 60
|
||||
kill -0 "$$" || exit
|
||||
done &> /dev/null &
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
print_in_color()
|
||||
{
|
||||
string=$(echo "$1" | tr -s " ")
|
||||
|
||||
printf "%b" \
|
||||
"$(tput setaf "$2" 2> /dev/null)" \
|
||||
"$string" \
|
||||
"$(tput sgr0 2> /dev/null)"
|
||||
}
|
||||
|
||||
print_in_red()
|
||||
{
|
||||
print_in_color "$1" 1
|
||||
}
|
||||
|
||||
print_in_yellow()
|
||||
{
|
||||
print_in_color "$1" 3
|
||||
}
|
||||
|
||||
print_in_green()
|
||||
{
|
||||
print_in_color "$1" 2
|
||||
}
|
||||
|
||||
print_in_purple()
|
||||
{
|
||||
print_in_color "$1" 5
|
||||
}
|
||||
|
||||
print_title()
|
||||
{
|
||||
print_in_purple "\n • $1\n\n"
|
||||
}
|
||||
|
||||
print_success()
|
||||
{
|
||||
print_in_green " [✔] $1\n"
|
||||
}
|
||||
|
||||
print_warning()
|
||||
{
|
||||
print_in_yellow " [!] $1\n"
|
||||
}
|
||||
|
||||
print_error()
|
||||
{
|
||||
print_in_red " [✖] $1 $2\n"
|
||||
}
|
||||
|
||||
print_question()
|
||||
{
|
||||
print_in_yellow " [?] $1\n"
|
||||
}
|
||||
|
||||
print_result()
|
||||
{
|
||||
if [[ "$1" == 0 ]]; then
|
||||
print_success "$2"
|
||||
else
|
||||
print_error "$2"
|
||||
fi
|
||||
|
||||
return "$1"
|
||||
}
|
||||
|
||||
print_error_stream()
|
||||
{
|
||||
while read -r line; do
|
||||
print_error "↳ ERROR: $line"
|
||||
done
|
||||
}
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
ask()
|
||||
{
|
||||
print_question "$1"
|
||||
read -r
|
||||
}
|
||||
|
||||
get_answer()
|
||||
{
|
||||
printf "%s" "$REPLY"
|
||||
}
|
||||
|
||||
ask_for_confirmation()
|
||||
{
|
||||
print_question "$1 (y/n) "
|
||||
read -r -n 1
|
||||
printf "\n"
|
||||
}
|
||||
|
||||
answer_is_yes()
|
||||
{
|
||||
[[ "$REPLY" =~ ^[Yy]$ ]] \
|
||||
&& return 0 \
|
||||
|| return 1
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
show_spinner()
|
||||
{
|
||||
local -r FRAMES='/-\|'
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
local -r NUMBER_OF_FRAMES=${#FRAMES}
|
||||
|
||||
local -r CMDS="$2"
|
||||
local -r MSG="$3"
|
||||
local -r PID="$1"
|
||||
|
||||
local i=0
|
||||
local frame_text=""
|
||||
|
||||
# Provide more space so that the text hopefully doesn't reach the bottom line
|
||||
# of the terminal window.
|
||||
#
|
||||
# This is a workaround for escape sequences not tracking the buffer position
|
||||
# (accounting for scrolling).
|
||||
#
|
||||
# See also: https://unix.stackexchange.com/a/278888
|
||||
|
||||
printf "\n\n\n"
|
||||
tput cuu 3
|
||||
tput sc
|
||||
|
||||
while kill -0 "$PID" &> /dev/null; do
|
||||
frame_text=" [${FRAMES:i++%NUMBER_OF_FRAMES:1}] $MSG"
|
||||
|
||||
# Print frame text.
|
||||
printf "%s" "$frame_text"
|
||||
|
||||
sleep 0.2
|
||||
|
||||
# Clear frame text.
|
||||
tput rc
|
||||
done
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
cmd_exists()
|
||||
{
|
||||
command -v "$1" &> /dev/null
|
||||
}
|
||||
|
||||
is_git_repository()
|
||||
{
|
||||
git rev-parse &> /dev/null
|
||||
}
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
set_trap()
|
||||
{
|
||||
trap -p "$1" | grep "$2" &> /dev/null \
|
||||
|| trap '$2' "$1"
|
||||
}
|
||||
|
||||
kill_all_subproccesses()
|
||||
{
|
||||
local i=""
|
||||
|
||||
for i in $(jobs -p); do
|
||||
kill "$i"
|
||||
wait "$i" &> /dev/null
|
||||
done
|
||||
}
|
||||
|
||||
execute()
|
||||
{
|
||||
local -r CMDS="$1"
|
||||
local -r MSG="${2:-$1}"
|
||||
local -r TMP_FILE="$(mktemp /tmp/XXXXX)"
|
||||
|
||||
local exit_code=0
|
||||
local cmds_pid=""
|
||||
|
||||
# If the current process is ended, also end all its subproccesses.
|
||||
set_trap "EXIT" "kill_all_subproccesses"
|
||||
|
||||
# Execute commands in background
|
||||
# shellcheck disable=SC2261
|
||||
eval "$CMDS" \
|
||||
&> /dev/null \
|
||||
2> "$TMP_FILE" &
|
||||
|
||||
cmds_pid=$!
|
||||
|
||||
# Show a spinner if the commands require more time to complete.
|
||||
show_spinner "$cmds_pid" "$CMDS" "$MSG"
|
||||
|
||||
# Wait for the commands to no longer be executing in the background, and then
|
||||
# get their exit code.
|
||||
wait "$cmds_pid" &> /dev/null
|
||||
exit_code=$?
|
||||
|
||||
# Print output based on what happened.
|
||||
print_result $exit_code "$MSG"
|
||||
|
||||
if [[ $exit -ne 0 ]]; then
|
||||
print_error_stream < "$TMP_FILE"
|
||||
fi
|
||||
|
||||
rm -rf "$TMP_FILE"
|
||||
|
||||
return $exit_code
|
||||
}
|
Loading…
Reference in a new issue