commit 1d9cd3a9c43078693787fe6d3b6bc33b07665715 Author: Yorick Barbanneau Date: Sun Nov 10 02:01:43 2019 +0100 Fist commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc65223 --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +dotinstall - manage dotfiles +---------------------------- + +`dotinstall` is another little script made for install or uninstall dotfiles by +creating symbolic link. You need to provide a boostrap script for it. + +## How to use + +``` +dotinstall bootstrap +``` + +The script can use git to retrieve a repository : + +``` +dotinstall https://git.example.com/user/repo.git +``` + +You can uninstall the dotfiles by adding the `uninstall` keyword before the +bootstrap file : + +``` +dotinstall uninstall bootstrap +``` + +## Boostrap file syntax + +`bootstrap` is a simple bash script sourced by `dotinstall`. The `bootstrap` +file contains instructions and can call functions and use variables from +dotinstall: + +### functions + +#### bin_check + +This function check if binary file is avaible in the `PATH`. + + - **input** : list of executable separated by spaces" + - **return** : no value, but exit the entire script if a executable is not + found with exit code 128 + +``` +bin_check "ls mv git" +``` + +#### conf_process_dirs + +Process a directory that contains others directories an symblink them to the +destination folder : + + - **input** : + - `$1` : source directory + - `$2` : destination directory + - **return** : no value, exit the funtion if there is an error on path + +``` +conf_process_dirs "${repository}/config" "${HOME}/.config" +``` + +#### conf_process_files + +Process a directory than contains files and symblink them to the destination +folder : + + - **input** : + - `$1` : source directory + - `$2` : destination directory + - **return** : no value, exit the funtion if there is an error on path + +``` +conf_process_dirs "${repository}/zshrc" "${HOME}" +``` + +#### bin_install + +Symblink an executable file to the `~/.local/bin` folder. + + - **input** : + - `$1` : source file + - **return** : no value, exit the function if the source file not exist or is + not executable. + +``` +bin_install "bin/check_mails.sh +``` + +#### service_install + +Install a systemd service as user, can be useful for timers for example : + + - **input** : + - `$1` : source file + - `$2` : 1 if service must be activated + - **return** : no value, but exit the function if source file is not found or + the service can't be activated + +``` +service_install "services/myservice.service" 1 +``` + +### Avaible variables + + - `$repository` : the root folder of the repository + - `OVERWRITE_DIRECTORY` : if `1`, the script will erase a directory and replace + it with a symblink if needed. For example if `~/.vim/` exist, but your + boostrap file need to create a symblink from `~/.config/dotrepo/conf/vim` + to `~/.vim`, it will be erased fist. If 0, an error message will be + displayed. + - `OVERWRITE_FILE` : do the same with file + +## Licence + +This softeware is licenced under the GNU-GPL 3 licence, you can found a copy of +the licence [here](https://www.gnu.org/licenses/gpl-3.0.en.html). diff --git a/src/dotinstall.sh b/src/dotinstall.sh new file mode 100755 index 0000000..43df0fa --- /dev/null +++ b/src/dotinstall.sh @@ -0,0 +1,315 @@ +#!/usr/bin/env bash + +# Personnal Dotfiles install script + +BIN_DIRECTORY="${HOME}/.local/bin/" +LIB_DIRECTORY="${HOME}/.local/lib/" +SYD_DIRECTORY="${HOME}/.config/systemd/user" +DOTREPO="${HOME}/.config/dotrepo/" + + +# DEFAULT VALUES +OVERWRITE_DIRECTORY=0 +OVERWRITE_FILE=0 + +repository="" +install=1 + +usage () +{ + printf "\nUSAGE\n" + printf "\n" + printf "%s \n" "$0" + printf "boostrap_file : file contain variables\n" +} + +die () +{ + # Exit script + # $1 : error message before quit + # $2 : exit code - default 99 + # $3 : 1 if print usage + + [ -z $2 ] && $2=99 + printf "\e[31mFATAL : %s\e[0m\n" "$1" + [ $3 == 1 ] && usage + exit $2 +} + +error () +{ + # print error message in red + # $@ : message + + printf "\e[31m%s\e[0m\n" "$*" +} + +private:get_bootstrap_path () +{ + # this function return the absolute path of the bootstrap file + # $* : path of bootstrap file + + # If the path begin with /, this is an absolute part + if [[ "$*" =~ ^/[[:graph:]]*$ ]] + then + repository="$(dirname $*)" + return + fi + + local relative_path="$*" + local current_path="$(pwd)" + while [[ $relative_path =~ ^../[[:graph:]]*$ ]] + do + printf "We found ..\n" + relative_path="${relative_path##../}" + current_path="${current_path%/*}" + done + repository="$(dirname "${current_path}/${relative_path}")" +} + +private:remove_symblink() +{ + # Remove a symblink + # $* source + + printf " -> Remove symblink %s : " "$symblink" + if [ -L "$*" ] + then + local ret; + ret=$(rm "$*" 2>&1) + [ $? -ne 0 ] && { error "can't remove : $ret"; return; } + elif [ ! -f "$*" ] + then + error "$* not exist" + return + else + error "$* is not a symblink" + fi + printf "\e[32mdone\e[0m\n" +} + +private:create_symblink () +{ + # Create a symblink + # $1: source + # $2: destination + + local source="$1" + local dest="$2" + + printf " -> Create a symblink from %s : " "$source" + + # If dest is a symbolic link, delete it. + if [ -L "$dest" ] + then + rm -rf "$dest" + # If source is a directory + elif [ -d "$source" ] + then + if [ -d "$dest" ] + then + if [ $OVERWRITE_DIRECTORY -ne 0 ] + then + error "can't overwrite destination" + return + else + rm -rf $dest + fi + elif [ -e $dest ] + then + error "destination exist but is not a directory" + return + fi + + # If source is a file + elif [ -f "$source" ] + then + if [ -f "$dest" ] + then + if [ $OVERWRITE_FILE -ne 0 ] + then + error "can't overwrite destination" + return + else + rm -rf "$dest" + fi + elif [ -e "$dest" ] + then + error "$dest exist but is not a file" + return + fi + + fi + ln -s "$source" "$dest" + printf "\e[32mdone\e[0m\n" +} + +private:clone_repository () +{ + # Clone a git repository and test if it has a bootstrap file + # $* url + # return : local directory + + local ret + ret=$(git clone -q "$1" "${2}" 2>&1) + [[ $? -ne 0 ]] && die "Can't clone_repository : $ret" 20 0 +} + +process_dirs () { + + # Process a directory than contains subdir, create symblink from each subdir + # to the destination. + # + # $1: source directory + # $2: destination directory + + local dest="${repository}/$1" + printf "\nProcess directory %s\n" "$1" + + [ ! -d "$dest" ] && { error " -> source is not a directory"; return; } + [ ! -d "$dest" ] && { error " -> destination is not a directory"; return; } + + while read d + do + if [ $install -eq 1 ] + then + private:create_symblink "$d" "${2}/$(basename "$d")" + else + local symblink="${2}/$(basename ${d})" + private:remove_symblink "$symblink" + fi + + done < <(ls -d -1 "${dest}"/*/) +} + +process_files () { + + # Process a directory than contains config files, create symblink from each + # files to the destination. + # $1: source directory + # $2: destination directory + + local dest="${repository}/$1" + printf "Process files from directory %s:\n" "$1" + + [ ! -d "$dest" ] && { error " -> source is not a directory"; return; } + [ ! -d "$dest" ] && { error " -> destination is not a directory"; return; } + + while read d + do + if [ $install -eq 1 ] + then + private:create_symblink "$d" "$2/$(basename "$d")" + else + local symblink="${2}/$(basename ${d})" + private:remove_symblink "$symblink" + fi + + done < <(ls -1 "$dest") +} + + +install_service () +{ + # Install an user service with systemd and activate it + # $1 file + # $2 1 to Activate the service + + local source="${repository}/$1" + local ret + [ ! -f "$source" ] && { error "$1 not found"; return; } + local basename=$(basename "$source") + if [ $install -eq 1 ] + then + printf "\nInstall service %s :\n" "$source" + private:create_symblink "$source" "${SYD_DIRECTORY}/${basename}" + + # activate service + if [ $2 ] + then + printf " -> Activate $basename : " + ret=$(systemctl --user enable ${basename} 2>&1) + [[ $? -ne 0 || ! $(systemctl --user is-enabled $basename) ]] && { error "$ret"; return; } + printf "\e[32mdone\e[0m\n" + fi + else + printf "\nUninstall service %s :\n" "$source" + #Deactivate service + if [ $1 ] + then + printf " -> Deactivate $basename : " + ret=$(systemctl --user disable ${basename} 2>&1) + [[ $? -ne 0 ]] && { error "$ret"; return; } + printf "\e[32mdone\e[0m\n" + fi + fi + +} + +check_bin () +{ + # Check if an executable exist + # $1 executables separated with spaces + + printf "Checking required programs :\n" + for p in $1 + do + printf " -> %s:" "$p" + command -v $p >/dev/null 2>&1 || die "not found" 128 + printf " \e[32mfound\e[0m\n" + done +} + +install_bin () +{ + # Install a binary / script file as executable + # Via a symbolic link + # $1 : path to script + + local source="${repository}/${1}" + local dest="${BIN_DIRECTORY}/$(basename $1)" + + printf "\nProcess executable :\n" + [ ! -f "$source" ] && { error "$source is not a file"; return; } + [ ! -x "$source" ] && { error "$source not executable "; return; } + + if [ $install -eq 1 ] + then + private:create_symblink "$source" "$dest" + else + private:remove_symblink "$dest" + fi +} + +## create bin directory +[ ! -d $BIN_DIRECTORY ] && mkdir -p $BIN_DIRECTORY || printf "bin exist\n" + +# Test parameters : we need to know if it's a git repo or a file +[[ $1 == "uninstall" || $1 == "-u" ]] && { install=0; printf "Activate uninstall mode\n"; shift; } +echo $install +[ "$*" = "" ] && die "You must specify a bootstrap file" 10 1 + +if [[ $* =~ ^https://.*\.git$ || $* =~ ^ssh://.*\.git$ ]] +then + bin_check "git" + + # Check + localdir="${DOTREPO}/$(basename $* .git)" + [ -d "$localdir" ] && die "destination folder for git repository already exist" 21 0 + + private:clone_repository "$*" "$localdir" + if [ -f "${localdir}/bootstrap" ] + then + $0 "${localdir}/bootstrap" + exit $? + else + die "Can't find a \`boostrap\` file in ${repo}" 22 0 + fi +else + [ ! -f "$*" ] && die "$* does not exist" 10 1 + private:get_bootstrap_path "$*" + + # Include bootstrap + source $* +fi +exit 0