Compare commits
10 commits
6ca1c6688f
...
1dcd14cb6d
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dcd14cb6d | |||
|
|
8ce0106d00 | ||
| 932792ee15 | |||
| c6e88a985a | |||
| 7947dabdc8 | |||
| a18dd2faf3 | |||
| 6f04744a11 | |||
| f3c923bca6 | |||
| 879637964d | |||
| 23b5a45077 |
6 changed files with 57 additions and 52 deletions
46
README.org
46
README.org
|
|
@ -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
63
dotlink
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue