From 23b5a45077ef14683e8c80883610626f4af18242 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Tue, 5 Oct 2021 10:52:03 +0200 Subject: [PATCH 1/8] update README --- README.org | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index e0e804e..f3b2665 100644 --- a/README.org +++ b/README.org @@ -74,12 +74,12 @@ -u, --unlink Remove current links #+end_example -** Adding configuration files for only one host +** Add configuration files for only one host Add the files to =machines//=. If you want to keep all configuration files in the =common= directory and just use symlinks in the =machines= directory, you can follow the instructions below. -** Adding configuration files for multiple host +** Add configuration files for multiple hosts It is important that the links from the =machines= directory to the =common= directory are relative. Follow these steps to add new configurations for multiple machines: From 879637964d83bf20e91e065d59ea5a275c8a5b4a Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Mon, 1 Nov 2021 21:36:00 +0100 Subject: [PATCH 2/8] update error output --- dotlink | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dotlink b/dotlink index 79f584c..c090a21 100755 --- a/dotlink +++ b/dotlink @@ -60,8 +60,7 @@ dotfiles="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" # Check if dotfiles for machine exist if [ ! -d "$dotfiles/machines/$HOSTNAME" ]; then - error "No dotfiles for machine $text_bold$HOSTNAME$text_reset found" - exit 1 + error "No dotfiles for machine $text_bold$HOSTNAME$text_reset found, make sure the directory $text_bold$dotfiles/machines/$HOSTNAME$text_reset exists" fi # Get dotfiles for current machine From 6f04744a1189339d41a1d5d03d5849ff822c25bc Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Wed, 8 Dec 2021 11:33:33 +0100 Subject: [PATCH 3/8] rename machines to hosts --- README.org | 38 +++++++++---------- dotlink | 16 ++++---- {machines => hosts}/host1/.config/mpv | 0 {machines => hosts}/host1/.offlineimaprc | 0 .../host2/.config/beets/config.yaml | 0 {machines => hosts}/host2/.config/mpv | 0 6 files changed, 27 insertions(+), 27 deletions(-) rename {machines => hosts}/host1/.config/mpv (100%) rename {machines => hosts}/host1/.offlineimaprc (100%) rename {machines => hosts}/host2/.config/beets/config.yaml (100%) rename {machines => hosts}/host2/.config/mpv (100%) diff --git a/README.org b/README.org index f3b2665..b8a1bbf 100644 --- a/README.org +++ b/README.org @@ -4,30 +4,30 @@ After trying several approaches and switching between different setups, this method finally met all of my requirements: - As simple as possible - - Multiple machines are managed in one repository - - Identical configurations for one program on several machines only need to be adjusted in one place - - Different configurations for one program on several machines are no problem + - Multiple hosts are managed in one repository + - Identical configurations for one program on several hosts only need to be adjusted in one place + - Different configurations for one program on several hosts are no problem - Set everything up with one command - Updating configurations shall require nothing more than =git pull= - This is a reference repository which contains a bash script (=dotlink=) and some example dotfiles for two machines (=host1= and =host2=). + This is a reference repository which contains a bash script (=dotlink=) and some example dotfiles for two hosts (=host1= and =host2=). *Warning*: If you want to try this make a backup of your dotfiles! The script doesn't overwrite existing files but you never know. ** Concept - The concept is based on symlinks and two directories, =common= and =machines=. + The concept is based on symlinks and two directories, =common= and =hosts=. - The =machines= directory contains subdirectories for all machines on which dotfiles are managed. - They need to match the /hostname/ of the machine (in this repository =host1= and =host2=) and mimic the corresponding =$HOME= directories. + The =hosts= directory contains subdirectories for all hosts on which dotfiles are managed. + They need to match the /hostname/ of the host (in this repository =host1= and =host2=) and mimic the corresponding =$HOME= directories. - The =common= directory contains configs which are present on multiple machines. + The =common= directory contains configs which are present on multiple hosts. It doesn't follow any specific structure, you can choose what suits your setup. Subdirectories with program names, followed by the configuration files (in this repository only =mpv=) make probably the most sense but its up to you. The =common= directory should never contain symlinks. - Symlinks from the =machines= directory to the =common= directory make the configs available on multiple machines and they can be adjusted in one place (see =mpv= in this repository). + Symlinks from the =hosts= directory to the =common= directory make the configs available on multiple hosts and they can be adjusted in one place (see =mpv= in this repository). Here is the tree output from this repository: @@ -37,7 +37,7 @@ │   └── mpv │   ├── input.conf │   └── mpv.conf - ├── machines + ├── hosts │   ├── host1 │   │   ├── .config │   │   │   └── mpv -> ../../../common/mpv/ @@ -52,17 +52,17 @@ The *mpv* configuration is shared between hosts, =host1= has an *OfflineIMAP* configuration in his home directory and =host2= has a *beets* configuration in his =.config= directory. - Every file from every =machines/= directory can then be linked to the corresponding path into the home directory of the machine. + Every file from every =hosts/= directory can then be linked to the corresponding path into the home directory of the host. This can be done by hand or with help of the =dotlink= script. If you updated a configuration somewhere else just call =git pull= and thats it. ** Script usage - When the =dotlink= script is executed, all files from the =machines/= directory, which matches the current /hostname/, are linked to their destination in the =$HOME= directory. + When the =dotlink= script is executed, all files from the =hosts/= directory, which matches the current /hostname/, are linked to their destination in the =$HOME= directory. Executing the script is only neccessary when new files were added which are not linked yet. - The script can be executed from everywhere, it is just important that it's stored next to a =machines= directory like in this repository. + The script can be executed from everywhere, it is just important that it's stored next to a =hosts= directory like in this repository. #+begin_example Usage: dotlink [OPTIONS] @@ -76,14 +76,14 @@ ** Add configuration files for only one host - Add the files to =machines//=. - If you want to keep all configuration files in the =common= directory and just use symlinks in the =machines= directory, you can follow the instructions below. + Add the files to =hosts//=. + If you want to keep all configuration files in the =common= directory and just use symlinks in the =hosts= directory, you can follow the instructions below. ** Add configuration files for multiple hosts - It is important that the links from the =machines= directory to the =common= directory are relative. - Follow these steps to add new configurations for multiple machines: + It is important that the links from the =hosts= directory to the =common= directory are relative. + Follow these steps to add new configurations for multiple hosts: 1. Add the files somewhere to the =common= directory - 2. Execute =ln -rs common/ machines//= for every machine on which the files should be present - 3. Execute the =dotlink= script on every modified machine + 2. Execute =ln -rs common/ hosts//= for every host on which the files should be present + 3. Execute the =dotlink= script on every modified host diff --git a/dotlink b/dotlink index c090a21..19e9d95 100755 --- a/dotlink +++ b/dotlink @@ -58,13 +58,13 @@ done # Get current dotfile directory for later linking dotfiles="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -# Check if dotfiles for machine exist -if [ ! -d "$dotfiles/machines/$HOSTNAME" ]; then - error "No dotfiles for machine $text_bold$HOSTNAME$text_reset found, make sure the directory $text_bold$dotfiles/machines/$HOSTNAME$text_reset exists" +# Check if dotfiles for host exist +if [ ! -d "$dotfiles/hosts/$HOSTNAME" ]; then + error "No dotfiles for host $text_bold$HOSTNAME$text_reset found, make sure the directory $text_bold$dotfiles/hosts/$HOSTNAME$text_reset exists" fi -# Get dotfiles for current machine -cd "$dotfiles/machines/$HOSTNAME" +# Get dotfiles for current host +cd "$dotfiles/hosts/$HOSTNAME" files=( $(find -L -type f -printf '%P\n')) if [ "$unlink" == true ]; then @@ -78,7 +78,7 @@ for file in "${files[@]}"; do # Unlink files if [ "$unlink" == true ]; then - if [ -L "$HOME/$file" ] && [ "$(readlink $HOME/$file)" == "$dotfiles/machines/$HOSTNAME/$file" ]; then + if [ -L "$HOME/$file" ] && [ "$(readlink $HOME/$file)" == "$dotfiles/hosts/$HOSTNAME/$file" ]; then rm "$HOME/$file" log "Unlinked $text_bold$HOME/$file$text_reset" @@ -96,7 +96,7 @@ for file in "${files[@]}"; do # Check if target is a link if [ -L "$HOME/$file" ]; then - if [ "$(readlink $HOME/$file)" != "$dotfiles/machines/$HOSTNAME/$file" ]; then + if [ "$(readlink $HOME/$file)" != "$dotfiles/hosts/$HOSTNAME/$file" ]; then warning "$text_bold$HOME/$file$text_reset is a link but doesn't point to this repository, it will not be linked" continue fi @@ -114,7 +114,7 @@ for file in "${files[@]}"; do mkdir -p "$(dirname $HOME/$file)" # Link file - ln -s "$dotfiles/machines/$HOSTNAME/$file" "$HOME/$file" + ln -s "$dotfiles/hosts/$HOSTNAME/$file" "$HOME/$file" log "Linked $text_bold$dotfiles/$file$text_reset to $text_bold$HOME/$file$text_reset" fi fi diff --git a/machines/host1/.config/mpv b/hosts/host1/.config/mpv similarity index 100% rename from machines/host1/.config/mpv rename to hosts/host1/.config/mpv diff --git a/machines/host1/.offlineimaprc b/hosts/host1/.offlineimaprc similarity index 100% rename from machines/host1/.offlineimaprc rename to hosts/host1/.offlineimaprc diff --git a/machines/host2/.config/beets/config.yaml b/hosts/host2/.config/beets/config.yaml similarity index 100% rename from machines/host2/.config/beets/config.yaml rename to hosts/host2/.config/beets/config.yaml diff --git a/machines/host2/.config/mpv b/hosts/host2/.config/mpv similarity index 100% rename from machines/host2/.config/mpv rename to hosts/host2/.config/mpv From a18dd2faf3730ae7bd0203e7a127730396ab4e3f Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Wed, 8 Dec 2021 12:45:35 +0100 Subject: [PATCH 4/8] update usage output --- README.org | 2 +- dotlink | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index b8a1bbf..53401cc 100644 --- a/README.org +++ b/README.org @@ -67,7 +67,7 @@ #+begin_example Usage: dotlink [OPTIONS] - Simple dotfile management based on symlinks. + Link all files from hosts/$HOSTNAME to $HOME. OPTIONS -h, --help Show this help message diff --git a/dotlink b/dotlink index 19e9d95..2b66b56 100755 --- a/dotlink +++ b/dotlink @@ -24,7 +24,7 @@ function print_usage { cat < Date: Fri, 28 Jan 2022 11:16:06 +0100 Subject: [PATCH 5/8] shellcheck --- dotlink | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/dotlink b/dotlink index 2b66b56..b18a1bc 100755 --- a/dotlink +++ b/dotlink @@ -27,7 +27,7 @@ Usage: dotlink [OPTIONS] Link all files from hosts/\$HOSTNAME to \$HOME. OPTIONS - -h, --help Show this help message + -h, --help Show this help message and exit -u, --unlink Remove current links EOF exit @@ -46,7 +46,7 @@ while (( "$#" )); do unlink=true shift ;; - -*|--*=) + -*) error "Unsupported flag: $1" ;; *) @@ -56,37 +56,39 @@ while (( "$#" )); do done # Get current dotfile directory for later linking -dotfiles="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +dotfiles="$(dirname "$(realpath "$0")")" # Check if dotfiles for host exist -if [ ! -d "$dotfiles/hosts/$HOSTNAME" ]; then - error "No dotfiles for host $text_bold$HOSTNAME$text_reset found, make sure the directory $text_bold$dotfiles/hosts/$HOSTNAME$text_reset exists" +if [ ! -d "${dotfiles}/hosts/${HOSTNAME}" ]; then + error "No dotfiles for host ${text_bold}${HOSTNAME}${text_reset} found, make sure the directory ${text_bold}${dotfiles}/hosts/${HOSTNAME}${text_reset} exists" fi # Get dotfiles for current host -cd "$dotfiles/hosts/$HOSTNAME" -files=( $(find -L -type f -printf '%P\n')) +mapfile -t files < <(find -L "${dotfiles}/hosts/${HOSTNAME}" -type f -printf '%P\n') if [ "$unlink" == true ]; then - log "Unlinking $text_bold${#files[@]}$text_reset files..\n" + log "Unlinking ${text_bold}${#files[@]}${text_reset} files..\n" else - log "Linking $text_bold${#files[@]}$text_reset files..\n" + log "Linking ${text_bold}${#files[@]}${text_reset} files..\n" fi for file in "${files[@]}"; do + src="${dotfiles}/hosts/${HOSTNAME}/${file}" + trgt="${HOME}/${file}" + # Unlink files if [ "$unlink" == true ]; then - if [ -L "$HOME/$file" ] && [ "$(readlink $HOME/$file)" == "$dotfiles/hosts/$HOSTNAME/$file" ]; then + if [ -L "$trgt" ] && [ "$(readlink "$trgt")" == "$src" ]; then - rm "$HOME/$file" - log "Unlinked $text_bold$HOME/$file$text_reset" + rm "$trgt" + log "Unlinked ${text_bold}${trgt}${text_reset}" - # Remove base directory if empty - if ! [ "$(ls -A $(dirname $HOME/$file))" ]; then - rmdir "$(dirname $HOME/$file)" - log "Removed empty directory $text_bold$(dirname $HOME/$file)$text_reset" + # Remove target directory if empty + if ! [ "$(ls -A "$(dirname "$trgt")")" ]; then + rmdir "$(dirname "$trgt")" + log "Removed empty directory ${text_bold}$(dirname "$trgt")${text_reset}" fi fi @@ -94,28 +96,28 @@ for file in "${files[@]}"; do else # Check if target is a link - if [ -L "$HOME/$file" ]; then + if [ -L "$trgt" ]; then - if [ "$(readlink $HOME/$file)" != "$dotfiles/hosts/$HOSTNAME/$file" ]; then - warning "$text_bold$HOME/$file$text_reset is a link but doesn't point to this repository, it will not be linked" + if [ "$(readlink "$trgt")" != "$src" ]; then + warning "${text_bold}${trgt}${text_reset} is a link but doesn't point to this repository, it will not be linked" continue fi # Check if target is a file or directory - elif [ -f "$HOME/$file" ]; then + elif [ -f "$trgt" ]; then - warning "$text_bold$HOME/$file$text_reset exists and will not be linked" + warning "${text_bold}${trgt}${text_reset} exists and will not be linked" continue # Create link else # Create target directory if not existent - mkdir -p "$(dirname $HOME/$file)" + mkdir -p "$(dirname "$trgt")" # Link file - ln -s "$dotfiles/hosts/$HOSTNAME/$file" "$HOME/$file" - log "Linked $text_bold$dotfiles/$file$text_reset to $text_bold$HOME/$file$text_reset" + ln -s "$src" "$trgt" + log "Linked ${text_bold}${src}${text_reset} to ${text_bold}${trgt}${text_reset}" fi fi From c6e88a985a4e3dcfd786d7097f610fd803bfd947 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Fri, 28 Jan 2022 11:16:21 +0100 Subject: [PATCH 6/8] update README --- README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.org b/README.org index 53401cc..bc8a834 100644 --- a/README.org +++ b/README.org @@ -70,7 +70,7 @@ Link all files from hosts/$HOSTNAME to $HOME. OPTIONS - -h, --help Show this help message + -h, --help Show this help message and exit -u, --unlink Remove current links #+end_example From 932792ee159c4d54c8b920a6d90800927b14eaf4 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Tue, 8 Mar 2022 23:17:07 +0100 Subject: [PATCH 7/8] update README --- README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.org b/README.org index bc8a834..add5608 100644 --- a/README.org +++ b/README.org @@ -76,7 +76,7 @@ ** Add configuration files for only one host - Add the files to =hosts//=. + Add the files to =hosts//= and execute the =dotlink= script on the modified host. If you want to keep all configuration files in the =common= directory and just use symlinks in the =hosts= directory, you can follow the instructions below. ** Add configuration files for multiple hosts From 8ce0106d005efaafd70d6a246a836800540540db Mon Sep 17 00:00:00 2001 From: pgehring Date: Sat, 11 Jun 2022 15:51:27 +0200 Subject: [PATCH 8/8] handle hostnames of subdomains and without env variable --- dotlink | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dotlink b/dotlink index b18a1bc..797fcda 100755 --- a/dotlink +++ b/dotlink @@ -55,16 +55,20 @@ while (( "$#" )); do esac done +# Get hostname of this machine and filter out hostnames of subdomains like .local +hostname="$(hostname)" +hostname="${hostname%%.*}" + # Get current dotfile directory for later linking dotfiles="$(dirname "$(realpath "$0")")" # Check if dotfiles for host exist -if [ ! -d "${dotfiles}/hosts/${HOSTNAME}" ]; then - error "No dotfiles for host ${text_bold}${HOSTNAME}${text_reset} found, make sure the directory ${text_bold}${dotfiles}/hosts/${HOSTNAME}${text_reset} exists" +if [ ! -d "${dotfiles}/hosts/${hostname}" ]; then + error "No dotfiles for host ${text_bold}${hostname}${text_reset} found, make sure the directory ${text_bold}${dotfiles}/hosts/${hostname}${text_reset} exists" fi # Get dotfiles for current host -mapfile -t files < <(find -L "${dotfiles}/hosts/${HOSTNAME}" -type f -printf '%P\n') +mapfile -t files < <(find -L "${dotfiles}/hosts/${hostname}" -type f -printf '%P\n') if [ "$unlink" == true ]; then log "Unlinking ${text_bold}${#files[@]}${text_reset} files..\n" @@ -74,7 +78,7 @@ fi for file in "${files[@]}"; do - src="${dotfiles}/hosts/${HOSTNAME}/${file}" + src="${dotfiles}/hosts/${hostname}/${file}" trgt="${HOME}/${file}" # Unlink files