diff --git a/.editorconfig b/.editorconfig index 361cd4145..84b75025a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,6 +2,10 @@ indent_style = space indent_size = 2 +[*.sh] +indent_style = space +indent_size = 2 + [.zshrc] indent_style = space indent_size = 2 diff --git a/dotfiles_template/.gitignore b/dotfiles_template/.gitignore new file mode 100644 index 000000000..ca2897390 --- /dev/null +++ b/dotfiles_template/.gitignore @@ -0,0 +1,3 @@ +.dotly_update_available +.dotly_updated +.cached_github_api_calls \ No newline at end of file diff --git a/dotfiles_template/shell/bash/.bashrc b/dotfiles_template/shell/bash/.bashrc index 467227a85..de0d19b5d 100644 --- a/dotfiles_template/shell/bash/.bashrc +++ b/dotfiles_template/shell/bash/.bashrc @@ -2,42 +2,9 @@ export DOTFILES_PATH="XXX_DOTFILES_PATH_XXX" export DOTLY_PATH="$DOTFILES_PATH/modules/dotly" export DOTLY_THEME="codely" -if [[ "$(ps -p $$ -ocomm=)" =~ (bash$) ]]; then - __right_prompt() { - RIGHT_PROMPT="" - [[ -n $RPS1 ]] && RIGHT_PROMPT=$RPS1 || RIGHT_PROMPT=$RPROMPT - if [[ -n $RIGHT_PROMPT ]]; then - n=$(($COLUMNS - ${#RIGHT_PROMPT})) - printf "%${n}s$RIGHT_PROMPT\\r" - fi - } - export PROMPT_COMMAND="__right_prompt" -fi - -source "$DOTFILES_PATH/shell/init.sh" - -PATH=$( - IFS=":" - echo "${path[*]}" -) -export PATH - -themes_paths=( - "$DOTFILES_PATH/shell/bash/themes" - "$DOTLY_PATH/shell/bash/themes" -) - -for THEME_PATH in ${themes_paths[@]}; do - THEME_PATH="${THEME_PATH}/$DOTLY_THEME.sh" - [ -f "$THEME_PATH" ] && source "$THEME_PATH" && break -done - -for bash_file in "$DOTLY_PATH"/shell/bash/completions/*; do - source "$bash_file" -done - -if [ -n "$(ls -A "$DOTFILES_PATH/shell/bash/completions/")" ]; then - for bash_file in "$DOTFILES_PATH"/shell/bash/completions/*; do - source "$bash_file" - done -fi +if [[ -f "$DOTLY_PATH/shell/bash/init-dotly.sh" ]] +then + . "$DOTLY_PATH/shell/bash/init-dotly.sh" +else + echo "\033[0;31m\033[1mDOTLY Could not be loaded\033[0m" +fi \ No newline at end of file diff --git a/dotfiles_template/shell/exports.sh b/dotfiles_template/shell/exports.sh index ce47e3e45..e75ae3c13 100644 --- a/dotfiles_template/shell/exports.sh +++ b/dotfiles_template/shell/exports.sh @@ -1,3 +1,6 @@ +export DOTLY_AUTO_UPDATE_PERIOD_IN_DAYS=7 +export DOTLY_AUTO_UPDATE_MODE="minor" # silent, auto, minor*, info, prompt + export JAVA_HOME='/Library/Java/JavaVirtualMachines/amazon-corretto-15.jdk/Contents/Home' export GEM_HOME="$HOME/.gem" export GOPATH="$HOME/.go" @@ -6,22 +9,3 @@ export FZF_DEFAULT_OPTS=' --color=pointer:#ebdbb2,bg+:#3c3836,fg:#ebdbb2,fg+:#fbf1c7,hl:#8ec07c,info:#928374,header:#fb4934 --reverse ' - -export path=( - "$HOME/bin" - "$DOTLY_PATH/bin" - "$DOTFILES_PATH/bin" - "$JAVA_HOME/bin" - "$GEM_HOME/bin" - "$GOPATH/bin" - "$HOME/.cargo/bin" - "/usr/local/opt/ruby/bin" - "/usr/local/opt/python/libexec/bin" - "/opt/homebrew/bin" - "/usr/local/bin" - "/usr/local/sbin" - "/bin" - "/usr/bin" - "/usr/sbin" - "/sbin" -) diff --git a/dotfiles_template/shell/init-scripts.enabled/.gitkeep b/dotfiles_template/shell/init-scripts.enabled/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dotfiles_template/shell/init-scripts/.gitkeep b/dotfiles_template/shell/init-scripts/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dotfiles_template/shell/paths.sh b/dotfiles_template/shell/paths.sh new file mode 100644 index 000000000..af62f351f --- /dev/null +++ b/dotfiles_template/shell/paths.sh @@ -0,0 +1,18 @@ +export path=( + "$HOME/bin" + "$DOTLY_PATH/bin" + "$DOTFILES_PATH/bin" + "$JAVA_HOME/bin" + "$GEM_HOME/bin" + "$GOPATH/bin" + "$HOME/.cargo/bin" + "/usr/local/opt/ruby/bin" + "/usr/local/opt/python/libexec/bin" + "/opt/homebrew/bin" + "/usr/local/bin" + "/usr/local/sbin" + "/bin" + "/usr/bin" + "/usr/sbin" + "/sbin" +) diff --git a/dotfiles_template/shell/zsh/.zshrc b/dotfiles_template/shell/zsh/.zshrc index 514b62698..9cb868544 100644 --- a/dotfiles_template/shell/zsh/.zshrc +++ b/dotfiles_template/shell/zsh/.zshrc @@ -1,26 +1,9 @@ # Uncomment for debuf with `zprof` # zmodload zsh/zprof -# ZSH Ops -setopt HIST_IGNORE_ALL_DUPS -setopt HIST_FCNTL_LOCK -setopt +o nomatch -# setopt autopushd - -# Start zim -source "$ZIM_HOME/init.zsh" - -# Async mode for autocompletion -ZSH_AUTOSUGGEST_USE_ASYNC=true -ZSH_HIGHLIGHT_MAXLENGTH=300 - -source "$DOTFILES_PATH/shell/init.sh" - -fpath=("$DOTFILES_PATH/shell/zsh/themes" "$DOTFILES_PATH/shell/zsh/autocompletions" "$DOTLY_PATH/shell/zsh/themes" "$DOTLY_PATH/shell/zsh/completions" $fpath) - -autoload -Uz promptinit && promptinit -prompt ${DOTLY_THEME:-codely} - -source "$DOTLY_PATH/shell/zsh/bindings/dot.zsh" -source "$DOTLY_PATH/shell/zsh/bindings/reverse_search.zsh" -source "$DOTFILES_PATH/shell/zsh/key-bindings.zsh" +if [[ -f "$DOTLY_PATH/shell/zsh/init-dotly.sh" ]] +then + . "$DOTLY_PATH/shell/zsh/init-dotly.sh" +else + echo "\033[0;31m\033[1mDOTLY Could not be loaded\033[0m" +fi \ No newline at end of file diff --git a/scripts/core/_main.sh b/scripts/core/_main.sh index a220e6a16..2daad58d3 100755 --- a/scripts/core/_main.sh +++ b/scripts/core/_main.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash if ! ${DOT_MAIN_SOURCED:-false}; then - for file in $DOTLY_PATH/scripts/core/{args,collections,documentation,dot,git,log,platform,output,str}.sh; do + for file in $DOTLY_PATH/scripts/core/{args,array,async,collections,documentation,dot,files,git,log,platform,output,str}.sh; do source "$file"; done; unset file; diff --git a/scripts/core/array.sh b/scripts/core/array.sh new file mode 100644 index 000000000..07d4e6dff --- /dev/null +++ b/scripts/core/array.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# Usage: array::* "${arr1[@]}" "${arr2[@]}" +array::union() { echo "${@}" | tr ' ' '\n' | sort | uniq; } +array::disjunction() { echo "${@}" | tr ' ' '\n' | sort | uniq -u; } +array::difference() { echo "${@}" | tr ' ' '\n' | sort | uniq -d; } \ No newline at end of file diff --git a/scripts/core/async.sh b/scripts/core/async.sh new file mode 100644 index 000000000..0f088db3a --- /dev/null +++ b/scripts/core/async.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +# This was literally copied from: https://github.com/zombieleet/async-bash +# check the README.md for information on how to use this script + +# set +eu + +declare -a JOB_IDS +declare -i JOBS=1; + +killJob() { + local jobToKill signal __al__signals isSig + + jobToKill="$1" + signal="$2" + signal=${signal^^} + + [[ ! $jobToKill =~ ^[[:digit:]]+$ ]] && { + printf "%s\n" "\"$jobToKill\" should be an integer "; + return 1; + } + + + { + [[ -z "$signal" ]] && { + signal="SIGTERM" + } + } || { + # for loop worked better than read line in this case + __al__signals=$(kill -l); + isSig=0; + for sig in ${__al__signals}; do + [[ ! $sig =~ ^[[:digit:]]+\)$ ]] && { + [[ $signal == $sig ]] && { + isSig=1; + break; + } + } + done + + (( isSig != 1 )) && { + signal="SIGTERM" + } + } + + + + for job in ${JOB_IDS[@]};do + # increment job to 1 since array index starts from 0 + read -r -d " " -a __kunk__ <<< "${JOB_IDS[$job]}" + (( __kunk__ == jobToKill )) && { + read -r -d " " -a __kunk__ <<< "${JOB_IDS[$job]}" + + kill -${signal} %${__kunk__} + + status=$? + + (( status != 0 )) && { + printf "cannot kill %s %d\n" "${JOB_IDS[$job]}" "${__kunk__}" + return 1; + } + + printf "%d killed with %s\n" "${__kunk__}" "${signal}" + + return 0; + } + done +} + +async() { + local cmdToExec resolve reject _c __temp status + + cmdToExec="$1" + resolve="$2" + reject="$3" + + [[ -z "$cmdToExec" ]] || [[ -z "$reject" ]] || [[ -z "$resolve" ]] && { + printf "%s\n" "Insufficient number of arguments"; + return 1; + } + + + + __temp=( "$cmdToExec" "$reject" "$resolve" ) + + + for _c in "${__temp[@]}"; do + read -r -d " " comm <<<"${_c}" + type "${comm}" &>/dev/null + + status=$? + + (( status != 0 )) && { + printf "\"%s\" is neither a function nor a recognized cmd\n" "${_c}"; + unset _c + return 1; + } + done + + unset __temp _c + + { + __result=$($cmdToExec) + status=$? + + if (( status == 0 )) + then + $resolve "${__result}" + else + $reject "${status}" + fi + unset __result + } & + + JOB_IDS+=( "${JOBS} ${cmd}" ) + + read -r -d " " -a __kunk__ <<< "${JOB_IDS[$(( ${#JOB_IDS[@]} - 1))]}" + + #echo ${__kunk__} + + + : $(( JOBS++ )) + +} \ No newline at end of file diff --git a/scripts/core/debug.sh b/scripts/core/debug.sh new file mode 100644 index 000000000..c18f68695 --- /dev/null +++ b/scripts/core/debug.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +debug::out() { + echo "$@" +} + +debug::log() { + [[ ! -z "$DEBUG" ]] && debug::out "$@" +} + +debug::logg() { + DEBUG_LEVEL=${DEBUG_LEVEL:-1} + [[ $DEBUG_LEVEL -gt 1 ]] && debug::log $@ +} + +debug::log_exec() { + debug::log "Executing: ${@[@]}" + eval "${@[@]}" +} + +debug::set_variable () { + local var_name="$1" + shift + local values=$@ + eval "$var_name=${values[@]}" + + debug::logg "Set varible '$var_name' to '${values[@]}'" +} \ No newline at end of file diff --git a/scripts/core/files.sh b/scripts/core/files.sh new file mode 100644 index 000000000..7b73e3b32 --- /dev/null +++ b/scripts/core/files.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +files::check_if_path_is_older() { + local path_to_check number_of period + path_to_check="$1" + number_of="${2:-0}" + period="${3:-days}" + [[ -e "$path_to_check" ]] && [[ $(date -r "$path_to_check" +%s) -lt $(date -d "now - $number_of $period" +%s) ]] +} \ No newline at end of file diff --git a/scripts/core/github.sh b/scripts/core/github.sh new file mode 100644 index 000000000..0da09d6c7 --- /dev/null +++ b/scripts/core/github.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Api Url +readonly GITHUB_API_URL="https://api.github.com/repos" +readonly GITHUB_RAW_FILES_URL="https://raw.githubusercontent.com" +readonly GITHUB_DOTLY_REPOSITORY="CodelyTV/dotly" +readonly GITHUB_DOTLY_CACHE_PETITIONS="$DOTFILES_PATH/.cached_github_api_calls" +GITHUB_CACHE_PETITIONS_PERIOD_IN_DAYS="${GITHUB_CACHE_PETITIONS_PERIOD_IN_DAYS:-1}" + +[[ -z "${GITHUB_TOKEN:-}" ]] && { + output::error " THIS IS IMPORTANT!!!" + output::answer " If you do not have defined GITHUB_TOKEN variable you could receive" + output::answer " not expected results when calling GITHUB API" +} + +github::get_api_url() { + local user repository branch arguments + + while [ $# -gt 0 ]; do + case $1 in + --user|-u|--organization|-o) + user="$2" + shift 2 + ;; + --repository|-r) + repository="$2" + shift 2 + ;; + --branch|-b) + branch="/branches/$2" + shift 2 + ;; + *) + break 2 + ;; + esac + done + + if [[ -z "$user" ]] && [[ -z "$repository" ]]; then + if [[ "$1" =~ [\/] ]]; then + user="$(echo "$1" | awk -F '/' '{print $1}')" + repository="$(echo "$1" | awk -F '/' '{print $2}')" + shift + else + user="$1" + repository="$2" + shift 2 + fi + fi + + [[ $# -gt 0 ]] && arguments="/$(str::join '/' "$*")" + + echo "$GITHUB_API_URL/$user/$repository${branch:-}${arguments:-}" +} + +github::branch_raw_url() { + local user repository branch arguments + + branch="master" + + while [ $# -gt 0 ]; do + case $1 in + --user|-u|--organization|-o) + user="$2" + shift 2 + ;; + --repository|-r) + repository="$2" + shift 2 + ;; + --branch|-b) + branch="$2" + shift 2 + ;; + *) + break 2 + ;; + esac + done + + if [[ -z "$user" ]] && [[ -z "$repository" ]]; then + if [[ "$1" =~ [\/] ]]; then + user="$(echo "$1" | awk -F '/' '{print $1}')" + repository="$(echo "$1" | awk -F '/' '{print $2}')" + shift + else + user="$1" + repository="$2" + shift 2 + fi + fi + + [[ $# -gt 1 ]] && branch="$1" && shift + [[ $# -gt 0 ]] && file="/$(str::join '/' "$*")" + + echo "$GITHUB_RAW_FILES_URL/$user/$repository/${branch:-master}${file:-}" +} + +github::clean_cache() { + rm -rf "$GITHUB_DOTLY_CACHE_PETITIONS" +} + +github::_command() { + local url CURL_BIN + url="$1"; shift + CURL_BIN="$(which curl)" + + params=(-S -s -L -q -f -k "-H 'Accept: application/vnd.github.v3+json'") + [[ -n "$GITHUB_TOKEN" ]] && params+=("-H 'Authorization: token $GITHUB_TOKEN'") + + echo "$CURL_BIN ${params[*]} ${*} $url" +} + +github::curl() { + local md5command cached_request_file_path _command url cached cache_period + + cached=true + cache_period="$GITHUB_CACHE_PETITIONS_PERIOD_IN_DAYS" + + case "$1" in + --no-cache|-n) + cached=false + shift + ;; + --cached|-c) + shift + ;; + --period-in-days|-p) + cache_period="$2" + shift 2 + ;; + esac + + url=${1:-$(/dev/null" > "$cached_request_file_path" + fi + + cat "$cached_request_file_path" + else + eval "$_command 2>/dev/null" + fi +} + +github::get_latest_dotly_tag() { + github::curl $(github::get_api_url "$GITHUB_DOTLY_REPOSITORY" "tags") | jq -r '.[0].name' | uniq +} \ No newline at end of file diff --git a/scripts/core/output.sh b/scripts/core/output.sh index 4509bfa32..00f54a73e 100644 --- a/scripts/core/output.sh +++ b/scripts/core/output.sh @@ -5,20 +5,47 @@ green='\033[0;32m' bold_blue='\033[1m\033[34m' normal='\033[0m' +platform::is_macos() { + [[ $(uname -s) == "Darwin" ]] +} + output::write() { local -r text="${1:-}" echo -e "$text" } -output::answer() { output::write " > $1"; } -output::error() { output::answer "${red}$1${normal}"; } -output::solution() { output::answer "${green}$1${normal}"; } +output::answer() { output::write " > $@"; } +output::error() { output::answer "${red}$@${normal}"; } +output::solution() { output::answer "${green}$@${normal}"; } output::question() { if [ platform::is_macos ]; then - output::answer "🤔 $1: "; + echo -n " > 🤔 $1: "; read -r "$2"; else - read -rp " $1: " "$2" + read -rp "🤔 $1: " "$2" fi } +output::question_default() { + local question default_value var_name + question="$1" + default_value="$2" + var_name="$3" + + output::question "$question? [$default_value]" "$var_name" + eval "$var_name=\"\${$var_name:-$default_value}\"" +} +output::yesno() { + local question default PROMPT_REPLY values + question="$1" + default="${2:-Y}" + + if [[ "$default" =~ ^[Yy] ]]; then + values="Y/n" + else + values="y/N" + fi + + output::question "$question? [$values]" "PROMPT_REPLY" + [[ "${PROMPT_REPLY:-$default}" =~ ^[Yy] ]] +} output::empty_line() { echo ''; } output::header() { output::empty_line; output::write "${bold_blue}---- $1 ----${normal}"; } diff --git a/scripts/core/platform.sh b/scripts/core/platform.sh index 2e0ba2c3f..67cc2657d 100644 --- a/scripts/core/platform.sh +++ b/scripts/core/platform.sh @@ -23,3 +23,42 @@ platform::is_wsl() { platform::wsl_home_path(){ wslpath "$(wslvar USERPROFILE 2> /dev/null)" } + +platform::semver_is_minor_patch_update() { + normalize_ver() { + echo "${${1//./ }//v/}" + } + + compare_ver() { + [[ $1 -lt $2 ]] && echo -1 && return + [[ $1 -gt $2 ]] && echo 1 && return + + echo 0 + } + + v1="$(normalize_ver $1)" + v2="$(normalize_ver $2)" + + major1="$(echo $v1 | awk '{print $1}')" + major2="$(echo $v2 | awk '{print $1}')" + + minor1="$(echo $v1 | awk '{print $2}')" + minor2="$(echo $v2 | awk '{print $2}')" + + patch1="$(echo $v1 | awk '{print $3}')" + patch2="$(echo $v2 | awk '{print $3}')" + + compare_major="$(compare_ver $major1 $major2)" + compare_minor="$(compare_ver $minor1 $minor2)" + compare_patch="$(compare_ver $patch1 $patch2)" + + [[ $compare_major -eq 0 ]] && { [[ $compare_minor -ne 0 ]] || [[ $compare_patch -ne 0 ]]; } +} + +platform::get_script_path() { + echo "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +} + +platform::get_full_script_path() { + echo "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/$(basename $0)" +} \ No newline at end of file diff --git a/scripts/core/str.sh b/scripts/core/str.sh index 18891e25c..7eb454e61 100755 --- a/scripts/core/str.sh +++ b/scripts/core/str.sh @@ -10,3 +10,9 @@ str::split() { str::contains() { [[ $2 == *$1* ]] } + +str::to_upper() { echo "${@:-$(|] [value1 [... [valueN]] +# echo "template string" | templating::replace_var [value1 [... [valueN]] +# +# echo "Those are my family names: XXX_FAMILY_NAMES_XXX" |\ +# templating::replace_var family-names Miguel Manuel +# +# This will print: +# "Those are my family names: Miguel Manuel" +templating::replace_var () { + local any + if (( $# % 2 == 0 )); then + any="$( [value1 [... [valueN]] +# +# echo "Those are common names in spain: XXX_NAMES_XXX" |\ +# templating::replace_var_join names ', ' Manuel Jorge David Luis Pedro +# +# This will print: +# "Those are common names in spain: Manuel, Jorge, David, Luis, Pedro" +# +templating::replace_var_join() { + local input="$(" --name=Gabriel --email-address=no-email@example.com +# templating::replace "XXX_NAME_XXX " --name Gabriel --email-address no-email@example.com +# templating::replace "XXX_NAME_XXX " name Gabriel email-address no-email@example.com +# echo "XXX_NAME_XXX " |\ +# templating::replace name Gabriel email-address no-email@example.com +# templating::replace /path/to/file --name=Gabriel --email-address=no-email@example.com +# templating::replace /path/to/file --name Gabriel --email-address no-email@example.com +# templating::replace /path/to/file name Gabriel email-address no-email@example.com + +# +# This will print +# "Gabriel " +# +templating::replace() { + local var_name var_value output + case "$1" in + --*=*|--*) + output=$(] +##? init disable [] +##? init status [] +##? +##? Options: +##? -h --help Show this help +##? -v --version Show the program version +##? +##? Author: +##? Gabriel Trabanco Llano + +# Options part its important because assign short and long version of the params +docs::parse "$@" + +SCRIPT_NAME="dot self autoupdater" +SCRIPT_VERSION="1.0.0" + +# Print name and version +if $version; then + output::write "$SCRIPT_NAME v$SCRIPT_VERSION" + exit +fi + +# Create folders if they does not exists +mkdir -p "$DOTLY_INIT_SCRIPTS_PATH" +mkdir -p "$DOTFILES_INIT_SCRIPTS_PATH" +mkdir -p "$ENABLED_INIT_SCRIPTS_PATH" + +[[ ! -d "$ENABLED_INIT_SCRIPTS_PATH" ]] && output::error "The folder path to enable scripts does not exists.\n\tTry with 'dot symlinks apply update core-feature' first." && exit 1 +[[ ! -d "$DOTLY_INIT_SCRIPTS_PATH" ]] && output::error "The init scripts of DOTLY does not exists.\n\tTry with 'dot symlinks apply update core-feature' first." && exit 1 + +# Get the scripts +init_scripts=("$(init::get_scripts)") +enabled_scripts=("$(init::get_enabled)") + +case "${1:-}" in +"enable") + # If the user gives the script_name + if [[ -n "$script_name" ]]; then + status=0 + if init::exists_script "$script_name"; then + init::enable "$script_name" + init::status "$script_name" && output::solution "Enabled" + ! init::status "$script_name" && output::error "Could not be enabled." && status=1 + else + output::error "$script_name does not exists." + status=1 + fi + exit $status + fi + + # If there is no script_name + # If there is nothing that can be enabled or not select scripts to + # be enabled, exit + not_enabled_scripts=("$(array::disjunction "${init_scripts[@]}" "${enabled_scripts[@]}")") + if [[ -n "$not_enabled_scripts" ]]; then + to_enable=$(array::disjunction "${init_scripts[@]}" "${enabled_scripts[@]}" | init::fzf "Choose one or more (Shift + Tab) scripts to enable when init terminal") + else + output::answer "Nothing can be enabled" + fi + [[ -z "$to_enable" ]] && exit 0 + + for item in ${to_enable[@]}; do + init::enable "$item" + + if init::status "$item"; then + output::solution "Init script '$item'... Enabled" + else + output::error "Init script '$item' error... It could not be enabled." + exit 1 + fi + done + ;; +"disable") + # If there is script_name + if [[ -n "$script_name" ]]; then + status=0 + if init::exists_script "$script_name"; then + init::disable "$script_name" + if ! init::status "$script_name"; then + output::solution "Disabled" + fi + + init::status "$script_name" && output::error "Could not be disabled." && status=1 + else + output::error "$script_name does not exists." + status=1 + fi + + exit $status + fi + + # If there is no script_name + # If there are no enabled scripts or nothing to be disabled, exit + if [[ -n "$enabled_scripts" ]]; then + to_disable=$(echo "${enabled_scripts[@]}" | init::fzf "Choose one or more (Shift + Tab) scripts to disable from init terminal") + else + output::answer "Nothing to be disabled" + fi + [[ -z "$to_disable" ]] && exit 0 + + for item in ${to_disable[@]}; do + init::disable "$item" + + if ! init::status "$item"; then + output::solution "Init script '$item'... Disabled" + else + output::error "Init script '$item'... Could not be disabled." + fi + done + ;; +"status") + # Check status, if user gives a script_name + if [ -n "$script_name" ]; then + if init::status "$script_name"; then + output::solution "'$script_name' is enabled" + else + output::error "'$script_name' is disabled" + fi + else + # If there is no script_name, gives the status of all + for item in ${init_scripts[@]}; do + init::status "$item" &&\ + output::solution "'$item'... Enabled." ||\ + output::error "'$item'... Disabled." + done + fi + ;; +*) + # Gives help + "$DOTLY_PATH/bin/dot" self init -h + ;; +esac \ No newline at end of file diff --git a/scripts/self/update b/scripts/self/update index ac8a0ed97..9ed0c97a6 100755 --- a/scripts/self/update +++ b/scripts/self/update @@ -10,10 +10,20 @@ source "$DOTLY_PATH/scripts/core/_main.sh" ##? update docs::parse "$@" +current_dir="$(pwd)" + +cd "$DOTFILES_PATH" +branch=$(git config -f .gitmodules submodule.modules/dotly.branch) + cd "$DOTLY_PATH" git discard >/dev/null 2>&1 -git checkout master >/dev/null 2>&1 +git checkout "$branch" >/dev/null 2>&1 git pull >/dev/null 2>&1 git submodule update --init --recursive > /dev/null 2>&1 output::answer '✅ dotly updated to the latest version' + +rm -f "$DOTFILES_PATH/.dotly_update_available" +[ "$DOTLY_AUTO_UPDATE_MODE" != "silent" ] && touch "$DOTFILES_PATH/.dotly_updated" + +cd "$current_dir" \ No newline at end of file diff --git a/scripts/self/utils/autoupdate.sh b/scripts/self/utils/autoupdate.sh new file mode 100644 index 000000000..25b823df4 --- /dev/null +++ b/scripts/self/utils/autoupdate.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +. "$DOTLY_PATH/scripts/core/github.sh" + +autoupdate::updater() { + local CURRENT_DIR GIT_UPDATE_CHECK + + # Other needed variables + CURRENT_DIR="$(pwd)" + GIT_UPDATE_CHECK="${1:-$DOTLY_PATH}" + + # Change to dotly path + cd "$GIT_UPDATE_CHECK" || return 1 + + [[ -f "$DOTFILES_PATH/.dotly_updated" ]] && { + output::empty_line + output::write " 🥳 🎉 🍾 DOTLY UPDATED 🥳 🎉 🍾 " + output::empty_line + rm "$DOTFILES_PATH/.dotly_updated" + } + + [[ -f "$DOTFILES_PATH/.dotly_update_available" ]] && return 0 + + if files::check_if_path_is_older "$GIT_UPDATE_CHECK" "${DOTLY_AUTO_UPDATE_PERIOD_IN_DAYS:-7}" "days" &&\ + # shellcheck disable=SC2048 + [ "$(git rev-parse HEAD)" != "$(git ls-remote $(git rev-parse --abbrev-ref @{u} | sed 's/\// /g') | cut -f1)" ] + then + touch "$DOTFILES_PATH/.dotly_update_available" + fi + + cd "$CURRENT_DIR" || return 1 +} + +autoupdate::success() { + local latest_github_version current_dotly_version + if [[ -f "$DOTFILES_PATH/.dotly_update_available" ]]; then + case "$(str::to_lower "${DOTLY_AUTO_UPDATE_MODE:-minor}")" in + "silent") + "$DOTLY_PATH/bin/dot" self update + rm -f "$DOTFILES_PATH/.dotly_update_available" + ;; + "auto"|"autoupdate"|"auto-update"|"update") + output::answer "🚀 Updating DOTLY Automatically" + "$DOTLY_PATH/bin/dot" self update + output::solution "Updated, restart your terminal." + rm -f "$DOTFILES_PATH/.dotly_update_available" + ;; + "info") + output::empty_line + output::write " ---------------------------------------------" + output::write "| 🥳🎉🍾 NEW DOTLY VERSION AVAILABLE 🥳🎉🍾 |" + output::write " ---------------------------------------------" + output::empty_line + ;; + "minor"|"only_minor") + # Needs a file in DOTLY_PATH called VERSION with current installed VERSION + latest_github_version=$(github::get_latest_dotly_tag) + current_dotly_version="0.0.0" + [ -f "$DOTLY_PATH/VERSION" ] && current_dotly_version="$(cat "$DOTLY_PATH/VERSION")" + + if [[ -n "$latest_github_version" ]] &&\ + [[ -n "$current_dotly_version" ]] &&\ + platform::semver_is_minor_patch_update "$current_dotly_version" "$latest_github_version" + then + output::answer "🚀 Updating DOTLY Automatically" + "$DOTLY_PATH/bin/dot" self update + output::solution "Updated, restart your terminal." + fi + + rm -f "$DOTFILES_PATH/.dotly_update_available" + ;; + *) # Prompt + #Nothing to do when only show a inbox in prompt + ;; + esac + fi +} + +autoupdate::reject() { + # Nothing to be updated + return 0 +} \ No newline at end of file diff --git a/scripts/self/utils/init.sh b/scripts/self/utils/init.sh new file mode 100644 index 000000000..953f4b562 --- /dev/null +++ b/scripts/self/utils/init.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +DOTLY_INIT_SCRIPTS_PATH=${DOTLY_INIT_SCRIPTS_PATH:-$DOTLY_PATH/shell/init-scripts} +DOTFILES_INIT_SCRIPTS_PATH=${DOTFILES_INIT_SCRIPTS_PATH:-$DOTFILES_PATH/shell/init-scripts} +ENABLED_INIT_SCRIPTS_PATH=${ENABLED_INIT_SCRIPTS_PATH:-$DOTFILES_PATH/shell/init-scripts.enabled} + +init::exists_script() { + [[ -e "$DOTLY_INIT_SCRIPTS_PATH/$1" ]] || [[ -e "$DOTFILES_INIT_SCRIPTS_PATH" ]] +} + +init::status() { + init::exists_script "$1" && [[ -f "$ENABLED_INIT_SCRIPTS_PATH/$1" ]] +} + +init::get_scripts() { + [[ -d "$DOTLY_INIT_SCRIPTS_PATH" ]] &&\ + [[ -d "$DOTFILES_INIT_SCRIPTS_PATH" ]] &&\ + find "$DOTLY_INIT_SCRIPTS_PATH" \ + "$DOTFILES_INIT_SCRIPTS_PATH" -name "*" -type f,l |\ + xargs -I _ basename _ | sort | uniq +} + +init::get_enabled() { + [[ -d "$ENABLED_INIT_SCRIPTS_PATH" ]] &&\ + find "$ENABLED_INIT_SCRIPTS_PATH" -name "*" -type l |\ + xargs -I _ basename _ | sort | uniq +} + +init::fzf() { + local piped_values="$(] +##? +##? Options: +##? -h --help Show this help +##? -v --version Show the program version +##? +docs::parse "$@" + +SCRIPT_NAME="dot symlinks apply" +SCRIPT_VERSION="1.0.0" + +# Print name and version +if $version; then + output::write "$SCRIPT_NAME v$SCRIPT_VERSION" + exit +fi + + +if [[ -z "$symlinks_file" ]]; then + symlinks_file="$DOTLY_PATH/symlinks/$(symlinks::get_files | symlinks::fzf)" + [[ -z "$symlinks_file" ]] && exit 0 +else + for f in "$symlinks_file" "$symlinks_file.yaml" "$symlinks_file.yml"; do + [[ -e "$f" ]] && symlinks_file="$f" && break + done + + if [[ ! -e "$symlinks_file" ]]; then + output::error "The file does not exists" + exit 1 + fi +fi + +if ! output::yesno "This could be danger your current dotfiles. Do you still want to continue"; then + exit 1 +fi + +output::header "Apply dotbot update to your dotfiles" +output::write "This will apply a selected symlinks to apply any dotly update" + +output::empty_line +"$DOTLY_PATH/modules/dotbot/bin/dotbot" -d "$DOTFILES_PATH" -c "$symlinks_file" +output::empty_line + +output::write "Remember to merge this symlinks file to yours in:" +output::answer "$DOTFILES_PATH/symlinks/conf.yaml" + \ No newline at end of file diff --git a/shell/bash/init-dotly.sh b/shell/bash/init-dotly.sh new file mode 100755 index 000000000..a949f125c --- /dev/null +++ b/shell/bash/init-dotly.sh @@ -0,0 +1,48 @@ +if [[ "$(ps -p $$ -ocomm=)" =~ (bash$) ]]; then + __right_prompt() { + RIGHT_PROMPT="" + [[ -n $RPS1 ]] && RIGHT_PROMPT=$RPS1 || RIGHT_PROMPT=$RPROMPT + if [[ -n $RIGHT_PROMPT ]]; then + n=$(($COLUMNS - ${#RIGHT_PROMPT})) + printf "%${n}s$RIGHT_PROMPT\\r" + fi + } + export PROMPT_COMMAND="__right_prompt" +fi + +. "$DOTFILES_PATH/shell/paths.sh" + +PATH=$( + IFS=":" + echo "${path[*]}" +) +export PATH + +[[ -f "$DOTFILES_PATH/shell/init.sh" ]] && . "$DOTFILES_PATH/shell/init.sh" + +themes_paths=( + "$DOTFILES_PATH/shell/bash/themes" + "$DOTLY_PATH/shell/bash/themes" +) + +for THEME_PATH in ${themes_paths[@]}; do + THEME_PATH="${THEME_PATH}/$DOTLY_THEME.sh" + [ -f "$THEME_PATH" ] && source "$THEME_PATH" && break +done + +for bash_file in "$DOTLY_PATH"/shell/bash/completions/_*; do + . "$bash_file" +done + +if [ -n "$(ls -A "$DOTFILES_PATH/shell/bash/completions/")" ]; then + for bash_file in "$DOTFILES_PATH"/shell/bash/completions/_*; do + . "$bash_file" + done +fi + +# Auto Init scripts at the end +init_scripts_path="$DOTFILES_PATH/shell/init-scripts.enabled" +mkdir -p "$init_scripts_path" +find "$init_scripts_path" -mindepth 1 -maxdepth 1 -type l,f -name '*' | while read init_script; do + [[ -e "$init_script" ]] && . "$init_script" + done diff --git a/shell/bash/themes/codely.sh b/shell/bash/themes/codely.sh index 2467425b4..7121eb0fa 100644 --- a/shell/bash/themes/codely.sh +++ b/shell/bash/themes/codely.sh @@ -4,14 +4,23 @@ MIDDLE_CHARACTER="◂" GREEN_COLOR="32" RED_COLOR="31" +prompt_dotly_update() { + if [ -f "$DOTFILES_PATH/.dotly_update_available" ]; then + UPDATE_MESSAGE="📥 | " + fi +} + codely_theme() { LAST_CODE="$?" current_dir=$(dot core short_pwd) STATUS_COLOR=$GREEN_COLOR + UPDATE_MESSAGE="" if [ $LAST_CODE -ne 0 ]; then STATUS_COLOR=$RED_COLOR fi - export PS1="\[\e[${STATUS_COLOR}m\]{\[\e[m\]${MIDDLE_CHARACTER}\[\e[${STATUS_COLOR}m\]}\[\e[m\] \[\e[33m\]${current_dir}\[\e[m\] " + + + export PS1="\$(prompt_dotly_update)\[\e[${STATUS_COLOR}m\]{\[\e[m\]${MIDDLE_CHARACTER}\[\e[${STATUS_COLOR}m\]}\[\e[m\] \[\e[33m\]${current_dir}\[\e[m\] " } diff --git a/shell/init-scripts/dotly_autoupdater b/shell/init-scripts/dotly_autoupdater new file mode 100755 index 000000000..db3c6733c --- /dev/null +++ b/shell/init-scripts/dotly_autoupdater @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +"$DOTLY_PATH/bin/dot" self autoupdate \ No newline at end of file diff --git a/shell/zsh/init-dotly.sh b/shell/zsh/init-dotly.sh new file mode 100755 index 000000000..d0f8718a4 --- /dev/null +++ b/shell/zsh/init-dotly.sh @@ -0,0 +1,39 @@ +# Firstly Paths +#shellcheck source=/dev/null +. "$DOTFILES_PATH/shell/paths.sh" + +# ZSH Ops +setopt HIST_IGNORE_ALL_DUPS +setopt HIST_FCNTL_LOCK +setopt +o nomatch +# setopt autopushd + +# Start zim +#shellcheck source=/dev/null +. "$ZIM_HOME/init.zsh" + +# Async mode for autocompletion +# shellcheck disable=SC2034 +ZSH_AUTOSUGGEST_USE_ASYNC=true +# shellcheck disable=SC2034 +ZSH_HIGHLIGHT_MAXLENGTH=300 + +#shellcheck source=/dev/null +[[ -f "$DOTFILES_PATH/shell/init.sh" ]] && . "$DOTFILES_PATH/shell/init.sh" + +fpath=("$DOTFILES_PATH/shell/zsh/themes" "$DOTFILES_PATH/shell/zsh/autocompletions" "$DOTLY_PATH/shell/zsh/themes" "$DOTLY_PATH/shell/zsh/completions" $fpath) + +autoload -Uz promptinit && promptinit +prompt ${DOTLY_THEME:-codely} + +#shellcheck source=/dev/null +. "$DOTLY_PATH/shell/zsh/bindings/reverse_search.zsh" +#shellcheck source=/dev/null +. "$DOTFILES_PATH/shell/zsh/key-bindings.zsh" + +# Auto Init scripts at the end +init_scripts_path="$DOTFILES_PATH/shell/init-scripts.enabled" +mkdir -p "$init_scripts_path" +find "$init_scripts_path" -mindepth 1 -maxdepth 1 -type f,l -name '*' | while read -r init_script; do + [[ -e "$init_script" ]] && . "$init_script" + done diff --git a/shell/zsh/themes/prompt_codely_setup b/shell/zsh/themes/prompt_codely_setup index ecd784da4..a271f5e0e 100644 --- a/shell/zsh/themes/prompt_codely_setup +++ b/shell/zsh/themes/prompt_codely_setup @@ -18,8 +18,16 @@ prompt_codely_precmd() { (( ${+functions[git-info]} )) && git-info } +prompt_dotly_autoupdate() { + if [ -f "$DOTFILES_PATH/.dotly_update_available" ]; then + print -n "📥 | " + fi +} + prompt_codely_setup() { - local prompt_codely_status='%(?:%F{green}{%F{white}▸%F{green}}:%F{red}{%F{white}▸%F{red}})' + local UPDATE_MESSAGE prompt_codely_status + + prompt_codely_status='%(?:%F{green}{%F{white}▸%F{green}}:%F{red}{%F{white}▸%F{red}})' autoload -Uz add-zsh-hook && add-zsh-hook precmd prompt_codely_precmd @@ -31,7 +39,7 @@ prompt_codely_setup() { zstyle ':zim:git-info:dirty' format '%F{yellow}$DIRTY' zstyle ':zim:git-info:keys' format 'prompt' ' %F{cyan}%b%c %C%D' - PS1="${prompt_codely_status} \$(prompt_codely_pwd)\$(prompt_codely_git)%f " + PS1="\$(prompt_dotly_autoupdate)${prompt_codely_status} \$(prompt_codely_pwd)\$(prompt_codely_git)%f " RPS1='' } diff --git a/symlinks/core-feature.yaml b/symlinks/core-feature.yaml new file mode 100644 index 000000000..af5b96dc8 --- /dev/null +++ b/symlinks/core-feature.yaml @@ -0,0 +1,13 @@ +- defaults: + link: + create: true + +- create: + - $DOTLY_PATH/shell/init-scripts + - $DOTFILES_PATH/shell/init-scripts + - $DOTFILES_PATH/shell/init-scripts.enabled + +- shell: + - [echo ".dotly_update_available" >> $DOTFILES_PATH/.gitignore, Add to gitignore autoupdate files and github api cache path 1/3] + - [echo ".dotly_updated" >> $DOTFILES_PATH/.gitignore, Add to gitignore autoupdate files and github api cache path 2/3] + - [echo ".cached_github_api_calls" >> $DOTFILES_PATH/.gitignore, Add to gitignore autoupdate files and github api cache path 3/3] \ No newline at end of file