Skip to content

Commit

Permalink
[Feature/Fix] Backup dotfiles (#73)
Browse files Browse the repository at this point in the history
* Now is different Sloth symlinks and user symlinks
* dot symlinks update use new dotbot.sh library to apply symlinks
* Fixed missing paths for gnutools in init-sloth.sh
* Fixed dot package add loop when try to install docpars or cargo packages
* Fixed no backup when installing Sloth (see CodelyTV#76)
* Added libraries (dotbot.sh and symlinks.sh) to parse dotbot yaml files in symlinks context
* Added python-yq recipe and dependency on dot core install
* Added some options for dot symlinks apply see --help
* Added function readarray in array.sh for those who are not running bash greather than version 4
* Changed mapfile for readarray in all scripts and libraries
* Added files::backup_move_if_path_exists in files.sh
* Added files::fzf that helps including Dotly/.Sloth core libraries and any other libraries in fzf --preview option
  • Loading branch information
gtrabanco committed Jul 12, 2021
1 parent ae60fa9 commit 4277df7
Show file tree
Hide file tree
Showing 35 changed files with 1,051 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Install
run: echo "$HOME/.dotfiles" | bash installer

- name: Debug on error
- name: Debug
if: ${{ always() }}
run: |
echo "DOTLY_REPOSITORY: $DOTLY_REPOSITORY"
Expand Down
6 changes: 3 additions & 3 deletions bin/open
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
SCRIPT_PATH="$(cd -- "$(dirname "$0")" && pwd -P)"
FULL_SCRIPT_PATH="$SCRIPT_PATH/$(basename "$0")"

mapfile -c 1 -t < <(which -a open | grep -v "$FULL_SCRIPT_PATH")
readarray -c 1 -t < <(which -a open | grep -v "$FULL_SCRIPT_PATH")
OPEN_BIN=""
if [[ "${#MAPFILE[@]}" -gt 0 ]]; then
OPEN_BIN="${MAPFILE[0]}"
if [[ "${#readarray[@]}" -gt 0 ]]; then
OPEN_BIN="${readarray[0]}"
fi

os=$(uname | tr '[:upper:]' '[:lower:]')
Expand Down
6 changes: 0 additions & 6 deletions dotfiles_template/symlinks/conf.macos-intel.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,3 @@

- link:
~/.sloth: os/mac/.sloth
~/bin/bash: /usr/local/bin/bash
~/bin/date: /usr/local/bin/gdate
~/bin/find: /usr/local/opt/findutils/bin/gfind
~/bin/make: /usr/local/opt/make/libexec/gnubin/make
~/bin/sed: /usr/local/opt/gnu-sed/libexec/gnubin/sed
~/bin/touch: /usr/local/bin/gtouch
7 changes: 0 additions & 7 deletions dotfiles_template/symlinks/conf.macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,3 @@

- link:
~/.sloth: os/mac/.sloth
~/bin/bash: /opt/homebrew/bin/bash
~/bin/date: /opt/homebrew/bin/gdate
~/bin/find: /opt/homebrew/bin/gfind
~/bin/make: /opt/homebrew/bin/gmake
~/bin/sed: /opt/homebrew/bin/gsed
~/bin/touch: /opt/homebrew/bin/gtouch
~/bin/zsh: /opt/homebrew/bin/zsh
2 changes: 0 additions & 2 deletions dotfiles_template/symlinks/conf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
- create:
- $DOTFILES_PATH/shell/bash/completions
- $DOTFILES_PATH/shell/bash/themes
- ${SLOTH_PATH}/shell/init.scripts
- $DOTFILES_PATH/shell/init.scripts
- $DOTFILES_PATH/shell/init.scripts-enabled

- link:
bin/semver: ${SLOTH_PATH}/modules/semver-tool/src/semver
~/.bash_profile: shell/bash/.bash_profile
~/.bashrc: shell/bash/.bashrc
~/.zimrc: shell/zsh/.zimrc
Expand Down
54 changes: 43 additions & 11 deletions scripts/core/install
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@ elif platform::is_linux; then
install_linux_custom
fi

script::depends_on docpars fzf zsh python
script::depends_on docpars zsh fzf python python-yq jq tee realpath

##? Install dotly and setup dotfiles
##? Install dotly and setup dotfiles. By default use a interactive backup (backups are not done for core symlinks).
##?
##? Usage:
##? install [[-n | --never-backup] | [-b |--always-backup]]
##?
##? Options:
##? -h --help Prints this help
##? -n --never-backup Never do a backup of user symlinks without prompt
##? -b --always-backup Always do a backup of user symlinks without prompt
##? -i --interactive-backup Interactive backup of user symlinks asking for every existing symlink before to be applied (default)
##?
##? install
##?
##? SCRIPT_VERSION "2.0.0"
Expand All @@ -34,22 +42,43 @@ fi
output::answer "Creating dotfiles structure"
"$DOTLY_PATH/bin/dot" dotfiles create | log::file "Creating dotfiles structure" || exit 1

# @todo Backup if exists before
# Apply user symlinks
output::answer "Setting up symlinks"
"$DOTLY_PATH/bin/dot" symlinks apply | log::file "Applying symlinks" || true
symlinks_args=(--interactive-backup)
if [[ "${DOTLY_ENV:-PROD}" == "CI" ]] || ${never_backup:-}; then
symlinks_args=(--ignore-backup)
elif ${always_backup:-}; then
symlinks_args=(--backup)
else
bk=false
output::yesno "Do you want to perform a backup of symlinks before apply them (this will include all existing files)" && bk=true
$bk && output::yesno "Do you want to be asked for every file" || symlinks_args=(--backup)
! $bk && symlinks_args=(--ignore-backup)
fi
"${SLOTH_PATH:-$DOTLY_PATH}/bin/dot" symlinks apply "${symlinks_args[@]}" --continue-on-error --after-core | log::file "Applying symlinks"
touch "$HOME/.z"
unset symlinks_args bk

if ! str::contains zsh "$SHELL" && platform::command_exists chsh && platform::command_exists zsh; then
output::answer "Setting zsh as the default shell"
# ZSH as default shell if we are using zsh
output::answer "Setting zsh as the default shell"
if ! str::contains zsh "$SHELL" && platform::command_exists chsh; then
sudo chsh -s "$(command -v zsh)" | log::file "Setting zsh as default shell"
else
output::error "ZSH could not be setup as default shell"
fi

# If exists zsh install ZIMFW
output::answer "Installing zim"
if platform::command_exists zsh; then
output::answer "Installing zim"
zsh "$ZIM_HOME/zimfw.zsh" install | log::file "Installing zim"
else
output::error "ZIM Framework could not be installed"
output::write "Use \`dot self debug\` to view what happened"
output::write "You will need to run manually the ZIM Framework install command"
output::write " zsh \`$ZIM_HOME/zimfw.zsh\` install"
fi

if [[ "${DOTLY_ENV:-PROD}" != "CI" && "$SHELL" =~ zsh$ ]]; then
if [[ "${DOTLY_ENV:-PROD}" != "CI" ]] && platform::command_exists zsh; then
output::answer "Installing completions"
{
zsh "$DOTLY_PATH/bin/dot" shell zsh reload_completions &> /dev/null &&
Expand All @@ -64,11 +93,14 @@ if [ -d "$install_scripts_path" ]; then
sort |
while read -r install_script; do
#shellcheck disable=SC1090
{ [[ -x "$install_script" ]] && . "$install_script" | log::file "Executing afterinstall: $(basename "$install_script")"; } || {
output::error "Install script error in $(basename "$install_script")"
{
[[ -x "$install_script" ]] && . "$install_script" | log::file "Executing afterinstall: $(basename "$install_script")"
} || {
output::error "Install script error in \`$(basename "$install_script")\`"
}
done
fi

output::empty_line
output::answer '🥳 🎉 SLOTH installed sucessfully'
output::solution 'Now restart your terminal'
output::solution "🏁 Now restart your terminal to finish the installation"
4 changes: 2 additions & 2 deletions scripts/core/lint
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ script::depends_on shfmt
# Avoid zsh because shfmt show some errors because it do not understand some
# valid zsh valid syntax
if ${dotfiles:-false}; then
mapfile -t all_files < <(dotly::list_dotfiles_bash_files | grep -v "${DOTFILES_PATH}/shell/zsh")
readarray -t all_files < <(dotly::list_dotfiles_bash_files | grep -v "${DOTFILES_PATH}/shell/zsh")
else
mapfile -t all_files < <(dotly::list_bash_files | grep -v "${SLOTH_PATH:-$DOTLY_PATH}/shell/zsh")
readarray -t all_files < <(dotly::list_bash_files | grep -v "${SLOTH_PATH:-$DOTLY_PATH}/shell/zsh")
fi

# No color for shfmt
Expand Down
4 changes: 2 additions & 2 deletions scripts/core/migration
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ output::header "SLOTH migration wizard"
[[ -z "${to_version:-}" ]] && [[ -f "$DOTFILES_PATH/.sloth_updated" ]] && to_version="$(uptate::migration_script_exits)"

if [[ ${to_version:-false} == false ]]; then
to_version="$(find "$SLOTH_PATH/migration/" -name "*" -type f,l -executable -print0 -exec echo {} \; | xargs -I _ basename _ | sort --reverse | fzf --header "Select migration script version")"
to_version="$(find "$SLOTH_PATH/migration/" -name "*" -type f,l -executable -print0 | xargs -0 -I _ basename _ | sort --reverse | fzf --header "Select migration script version")"
fi

if [[ -n "$to_version" ]] && [[ -x "$SLOTH_PATH/migration/$to_version" ]]; then
output::write "You will execute migration script for '$to_version' this could"
output::write "You will execute migration script for \`$to_version\` this could"
output::write "result in a damage of your current dotfiles if they are not"
output::write "organized as expected."
output::write "PLEASE PERFORM A BACKUP OF YOUR DOTFILES BEFORE CONTINUE"
Expand Down
2 changes: 1 addition & 1 deletion scripts/core/sloth_lazy_script
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ else
shift
fi

mapfile -d "/" -t _script_paths < <(echo "$SLOTH_SCRIPT_FILE_PATH")
readarray -d "/" -t _script_paths < <(echo "$SLOTH_SCRIPT_FILE_PATH")
_last_script_path=$((${#_script_paths[@]} - 1))
_previous_script_path=$((_last_script_path - 1))
SCRIPT_NAME="${SLOTH_LAZY_SCRIPT_BASE_NAME} ${_script_paths[$_previous_script_path]} ${_script_paths[$_last_script_path]}"
Expand Down
2 changes: 1 addition & 1 deletion scripts/core/src/_main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
if ! ${DOT_MAIN_SOURCED:-false}; then
# platform and output should be at the first place because they are used
# in other libraries
for file in "${SLOTH_PATH:-$DOTLY_PATH}"/scripts/core/src/{platform,output,args,array,async,collections,documentation,dot,files,git,log,package,registry,script,str}.sh; do
for file in "${SLOTH_PATH:-$DOTLY_PATH}"/scripts/core/src/{platform,output,args,array,async,collections,documentation,dot,files,git,json,log,package,registry,script,str,yaml}.sh; do
#shellcheck source=/dev/null
. "$file" || exit 5
done
Expand Down
48 changes: 48 additions & 0 deletions scripts/core/src/array.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,51 @@ array::uniq_unordered() {

declare -p uniq_values
}

if ! type readarray &> /dev/null; then
# Very minimal readarray implementation using read. Does NOT work with lines that contain double-quotes due to eval()
# https://stackoverflow.com/a/64793921
readarray() {
local cmd opt t v=readarray
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help)
echo "minimal substitute readarray for older bash"
exit
;;
-r)
shift
opt="$opt -r"
;;
-t)
shift
#shellcheck disable=SC2034
t=1
;;
-u)
shift
if [ -n "$1" ]; then
opt="$opt -u $1"
shift
fi
;;
*)
if [[ "$1" =~ ^[A-Za-z_]+$ ]]; then
v="$1"
shift
else
echo -en "${C_BOLD}${C_RED}Error: ${C_RESET}Unknown option: '$1'\n" 1>&2
exit
fi
;;
esac
done
cmd="read $opt"
eval "$v=()"
while IFS= eval "$cmd line"; do
#shellcheck disable=SC2001
line=$(echo "$line" | sed -e "s#\([\"\`]\)#\\\\\1#g")
eval "${v}+=(\"$line\")"
done
}
fi
2 changes: 1 addition & 1 deletion scripts/core/src/dot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ dot::parse_script_version() {
SCRIPT_FULL_PATH="${1:-}"

[[ ! -f "$SCRIPT_FULL_PATH" ]] && return 1
mapfile -t versions < <(sed -n 's/.*SCRIPT_VERSION[=| ]"\?\(.[^";]*\)"\?;\?.*/\1/p' "$SCRIPT_FULL_PATH")
readarray -t versions < <(sed -n 's/.*SCRIPT_VERSION[=| ]"\?\(.[^";]*\)"\?;\?.*/\1/p' "$SCRIPT_FULL_PATH")

if [[ "${#versions[@]}" -gt 1 ]]; then
for v in "${versions[@]}"; do
Expand Down
111 changes: 106 additions & 5 deletions scripts/core/src/files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,117 @@ files::check_if_path_is_older() {
[[ -e "$path_to_check" ]] && [[ $(date -r "$path_to_check" +%s) -lt $(date -d "now - $number_of $period" +%s) ]]
}

files::backup_if_file_exists() {
#;
# files::backup_move_if_path_exists
# Move a file or directory if exists by appending a suffix and return its new location
# @param string file_path
# @param string suffix for the backup
# @return void
#"
files::backup_move_if_path_exists() {
local file_path bk_suffix bk_file_path
file_path="$(eval realpath -q -m "${1:-}")"
bk_suffix="${2:-$(date +%s)}"
bk_file_path="$file_path.${bk_suffix}"

if [[ -n "$file_path" ]] &&
{ [[ -f "$file_path" ]] || [[ -d "$file_path" ]]; }; then
eval mv "$file_path" "$bk_file_path" && echo "$bk_file_path" && return 1
if [[ -n "$file_path" ]] && [[ -e "$file_path" ]] && [[ ! -L "$file_path" ]]; then
mv "$file_path" "$bk_file_path" && echo "$bk_file_path"
fi
}

#;
# files::fzf()
# Show fzf but can include libraries in a easier way
#;
files::fzf() {
local arguments preview multiple preview_args preview_path libraries_to_load dot_lib
preview=false
multiple=false
preview_args=()
preview_path="$DOTBOT_BASE_PATH/"
arguments=()

while [ ${#:-0} -gt 0 ]; do
case "${1:-}" in
--default-preview)
preview=true
shift
;;
--preview)
preview=true
preview_args+=("$2;")
shift 2
;;
-p | --preview-path)
#shellcheck disable=SC2034
[[ -d "${2:-}" ]] && preview_path="${2:-}/"
shift 2
;;
-m | --multi)
multiple=true
arguments+=(--multi)
shift

if [[ "${1:-}" =~ ^[0-9]+$ ]]; then
arguments+=("${1:-}")
shift
fi
;;
-c | --sloth-core | --dotly-core)
preview=true
preview_args=(
". \"$DOTLY_PATH/scripts/core/_main.sh\";"
"${preview_args[@]};"
)
shift
;;
--script-libs)
preview=true
libraries_to_load=()
for dot_lib in "${SCRIPT_LOADED_LIBS[@]}"; do
[[ ! -f "$dot_lib" ]] && continue
libraries_to_load+=(
". \"$dot_lib\";"
)
done

preview_args=(
". \"$DOTLY_PATH/scripts/core/_main.sh\";"
"${libraries_to_load[@]}"
"${preview_args[@]}"
)
shift
;;
*)
break 2
;;
esac
done

# Default preview
if $preview && [[ -z "${preview_args[*]}" ]]; then
$multiple && preview_args+=(
'echo "Press Tab+Shift to select multiple options.";'
)
#shellcheck disable=SC2016
preview_args+=(
'file={};'
'file_path=\"${preview_path:-}$file\";'
'echo "Press Ctrl+C to exit with no selection.\n";'
'echo "File: $file_path";'
'echo "\n----";'
'[[ -f "$file_path" ]] && cat "$file_path";'
)
fi

# Add the arguments
if $preview && [[ -n "${preview_args[*]:-}" ]]; then
arguments+=(
--preview
"${preview_args[*]}"
)
fi
arguments+=("$@")

return 0
fzf "${arguments[@]}"
}
17 changes: 17 additions & 0 deletions scripts/core/src/json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash

json::to_yaml() {
if [[ -t 0 ]]; then
[[ -f "${1:-}" ]] && yq --yaml-output < "$1"
else
yq --yaml-output < /dev/stdin
fi
}

json::is_valid() {
if [[ -t 0 ]]; then
[[ -n "${1:-}" && -f "$1" ]] && jq -e '.' < "$1" &> /dev/null
else
jq -e '.' < /dev/stdin &> /dev/null
fi
}
Loading

0 comments on commit 4277df7

Please sign in to comment.