Compare commits

...

10 commits

Author SHA1 Message Date
1dcd14cb6d
Merge pull request #1 from pgehring/feature/handle_subdomain
handle hostnames of subdomains and without env variable
2022-06-13 08:43:56 +02:00
pgehring
8ce0106d00 handle hostnames of subdomains and without env variable 2022-06-11 16:25:54 +02:00
932792ee15 update README 2022-03-08 23:17:07 +01:00
c6e88a985a update README 2022-01-28 11:16:21 +01:00
7947dabdc8 shellcheck 2022-01-28 11:16:06 +01:00
a18dd2faf3 update usage output 2021-12-08 12:45:35 +01:00
6f04744a11 rename machines to hosts 2021-12-08 11:33:33 +01:00
f3c923bca6 Merge branch 'master' of git.opaque.tech:denis/dotlink 2021-11-01 21:36:16 +01:00
879637964d update error output 2021-11-01 21:36:00 +01:00
23b5a45077 update README 2021-10-05 10:52:03 +02:00
6 changed files with 57 additions and 52 deletions

View file

@ -4,30 +4,30 @@
After trying several approaches and switching between different setups, this method finally met all of my requirements: After trying several approaches and switching between different setups, this method finally met all of my requirements:
- As simple as possible - As simple as possible
- Multiple machines are managed in one repository - Multiple hosts are managed in one repository
- Identical configurations for one program on several machines only need to be adjusted in one place - Identical configurations for one program on several hosts only need to be adjusted in one place
- Different configurations for one program on several machines are no problem - Different configurations for one program on several hosts are no problem
- Set everything up with one command - Set everything up with one command
- Updating configurations shall require nothing more than =git pull= - 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! *Warning*: If you want to try this make a backup of your dotfiles!
The script doesn't overwrite existing files but you never know. The script doesn't overwrite existing files but you never know.
** Concept ** 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. The =hosts= directory contains subdirectories for all hosts 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. 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. 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. 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. 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: Here is the tree output from this repository:
@ -37,7 +37,7 @@
│   └── mpv │   └── mpv
│   ├── input.conf │   ├── input.conf
│   └── mpv.conf │   └── mpv.conf
├── machines ├── hosts
│   ├── host1 │   ├── host1
│   │   ├── .config │   │   ├── .config
│   │   │   └── mpv -> ../../../common/mpv/ │   │   │   └── mpv -> ../../../common/mpv/
@ -52,38 +52,38 @@
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. 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/<hostname>= directory can then be linked to the corresponding path into the home directory of the machine. Every file from every =hosts/<hostname>= 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. 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. If you updated a configuration somewhere else just call =git pull= and thats it.
** Script usage ** Script usage
When the =dotlink= script is executed, all files from the =machines/<hostname>= 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/<hostname>= 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. 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 #+begin_example
Usage: dotlink [OPTIONS] Usage: dotlink [OPTIONS]
Simple dotfile management based on symlinks. Link all files from hosts/$HOSTNAME to $HOME.
OPTIONS OPTIONS
-h, --help Show this help message -h, --help Show this help message and exit
-u, --unlink Remove current links -u, --unlink Remove current links
#+end_example #+end_example
** Adding configuration files for only one host ** Add configuration files for only one host
Add the files to =machines/<hostname>/<path_in_home>=. Add the files to =hosts/<hostname>/<path_in_home>= 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 =machines= directory, you can follow the instructions below. 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.
** 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. 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 machines: Follow these steps to add new configurations for multiple hosts:
1. Add the files somewhere to the =common= directory 1. Add the files somewhere to the =common= directory
2. Execute =ln -rs common/<config_or_directory> machines/<hostname>/<path_in_home>= for every machine on which the files should be present 2. Execute =ln -rs common/<config_or_directory> hosts/<hostname>/<path_in_home>= for every host on which the files should be present
3. Execute the =dotlink= script on every modified machine 3. Execute the =dotlink= script on every modified host

63
dotlink
View file

@ -24,10 +24,10 @@ function print_usage {
cat <<EOF cat <<EOF
Usage: dotlink [OPTIONS] Usage: dotlink [OPTIONS]
Simple dotfile management based on symlinks. Link all files from hosts/\$HOSTNAME to \$HOME.
OPTIONS OPTIONS
-h, --help Show this help message -h, --help Show this help message and exit
-u, --unlink Remove current links -u, --unlink Remove current links
EOF EOF
exit exit
@ -46,7 +46,7 @@ while (( "$#" )); do
unlink=true unlink=true
shift shift
;; ;;
-*|--*=) -*)
error "Unsupported flag: $1" error "Unsupported flag: $1"
;; ;;
*) *)
@ -55,39 +55,44 @@ while (( "$#" )); do
esac esac
done done
# Get current dotfile directory for later linking # Get hostname of this machine and filter out hostnames of subdomains like .local
dotfiles="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" hostname="$(hostname)"
hostname="${hostname%%.*}"
# Check if dotfiles for machine exist # Get current dotfile directory for later linking
if [ ! -d "$dotfiles/machines/$HOSTNAME" ]; then dotfiles="$(dirname "$(realpath "$0")")"
error "No dotfiles for machine $text_bold$HOSTNAME$text_reset found"
exit 1 # 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 fi
# Get dotfiles for current machine # Get dotfiles for current host
cd "$dotfiles/machines/$HOSTNAME" mapfile -t files < <(find -L "${dotfiles}/hosts/${hostname}" -type f -printf '%P\n')
files=( $(find -L -type f -printf '%P\n'))
if [ "$unlink" == true ]; then if [ "$unlink" == true ]; then
log "Unlinking $text_bold${#files[@]}$text_reset files..\n" log "Unlinking ${text_bold}${#files[@]}${text_reset} files..\n"
else else
log "Linking $text_bold${#files[@]}$text_reset files..\n" log "Linking ${text_bold}${#files[@]}${text_reset} files..\n"
fi fi
for file in "${files[@]}"; do for file in "${files[@]}"; do
src="${dotfiles}/hosts/${hostname}/${file}"
trgt="${HOME}/${file}"
# Unlink files # Unlink files
if [ "$unlink" == true ]; then if [ "$unlink" == true ]; then
if [ -L "$HOME/$file" ] && [ "$(readlink $HOME/$file)" == "$dotfiles/machines/$HOSTNAME/$file" ]; then if [ -L "$trgt" ] && [ "$(readlink "$trgt")" == "$src" ]; then
rm "$HOME/$file" rm "$trgt"
log "Unlinked $text_bold$HOME/$file$text_reset" log "Unlinked ${text_bold}${trgt}${text_reset}"
# Remove base directory if empty # Remove target directory if empty
if ! [ "$(ls -A $(dirname $HOME/$file))" ]; then if ! [ "$(ls -A "$(dirname "$trgt")")" ]; then
rmdir "$(dirname $HOME/$file)" rmdir "$(dirname "$trgt")"
log "Removed empty directory $text_bold$(dirname $HOME/$file)$text_reset" log "Removed empty directory ${text_bold}$(dirname "$trgt")${text_reset}"
fi fi
fi fi
@ -95,28 +100,28 @@ for file in "${files[@]}"; do
else else
# Check if target is a link # Check if target is a link
if [ -L "$HOME/$file" ]; then if [ -L "$trgt" ]; then
if [ "$(readlink $HOME/$file)" != "$dotfiles/machines/$HOSTNAME/$file" ]; then if [ "$(readlink "$trgt")" != "$src" ]; then
warning "$text_bold$HOME/$file$text_reset is a link but doesn't point to this repository, it will not be linked" warning "${text_bold}${trgt}${text_reset} is a link but doesn't point to this repository, it will not be linked"
continue continue
fi fi
# Check if target is a file or directory # 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 continue
# Create link # Create link
else else
# Create target directory if not existent # Create target directory if not existent
mkdir -p "$(dirname $HOME/$file)" mkdir -p "$(dirname "$trgt")"
# Link file # Link file
ln -s "$dotfiles/machines/$HOSTNAME/$file" "$HOME/$file" ln -s "$src" "$trgt"
log "Linked $text_bold$dotfiles/$file$text_reset to $text_bold$HOME/$file$text_reset" log "Linked ${text_bold}${src}${text_reset} to ${text_bold}${trgt}${text_reset}"
fi fi
fi fi