config

A simple configuration manager

config (4081B)

  1 #!/bin/bash
  2 #                   __ _       
  3 #   ___ ___  _ __  / _(_) __ _ 
  4 #  / __/ _ \| '_ \| |_| |/ _` |
  5 # | (_| (_) | | | |  _| | (_| |
  6 #  \___\___/|_| |_|_| |_|\__, |
  7 #                        |___/ 
  8 #
  9 # A simple configuration manager
 10 # 2021 (C) Pablo
 11 # Free use of this software is granted under the terms of the GPL-3.0 License
 12 
 13 print_files() 
 14 {
 15   IFS='' read -r file
 16 
 17   if [ -n "$file" ]
 18   then
 19     printf '\033[0;%sm%s files:\033[0m\n' "$2" "$1"
 20     while true
 21     do
 22       printf '\033[0;%sm%s\033[0m\n' "$2" "$file"
 23       IFS='' read -r file
 24       [ -z "$file" ] && break
 25     done
 26   fi
 27 }
 28 
 29 # Get the path to the git repository that should be used for storing stuff
 30 if [ -n "$DOTFILES_REPO" ]
 31 then
 32   dotfiles_repo="$DOTFILES_REPO"
 33 elif [ -n "$XDG_DATA_HOME" ]
 34 then
 35   dotfiles_repo="$XDG_DATA_HOME/dotfiles"
 36 else
 37   dotfiles_repo="$HOME/.local/share/dotfiles"
 38 fi
 39 
 40 # Get the name of the files that holds the list of all dotfiles that should be
 41 # tracked
 42 if [ -n "$DOTFILES_LIST" ]
 43 then
 44   dotfiles_list="$DOTFILES_LIST"
 45 elif [ -n "$XDG_CONFIG_HOME" ]
 46 then
 47   dotfiles_list="$XDG_CONFIG_HOME/dotfiles.list"
 48 else
 49   dotfiles_list="$HOME/.config/dotfiles.list"
 50 fi
 51 
 52 # Calls git in the appropriate repository
 53 call_git() 
 54 {
 55   git --git-dir="$dotfiles_repo" --work-tree="$HOME" "$@"
 56 }
 57 
 58 # Given a path, print the files and directories matching this path that are
 59 # included in the list of dotfiles
 60 get_pathspec()
 61 {
 62   path="$1"
 63 
 64   if [ -f "$path" ]
 65   then
 66     tmp_path="$(realpath "$path")"
 67 
 68     # Check if the file is contained in any of the directories listed in the
 69     # dotfiles list are the parents of path. If this is the case, print path
 70     while [ "$tmp_path" != '/' ]
 71     do
 72       if grep -Fx "$tmp_path" "$dotfiles_list" > /dev/null
 73       then
 74         echo "$path"
 75         return
 76       fi
 77 
 78       tmp_path="$(dirname "$tmp_path")"
 79     done
 80   elif [ -d "$path" ]
 81   then
 82     # Print all of the files and directories from the list of dotfiles
 83     # contained in path
 84 
 85     # TODO: The regex should only math occurances of path starting at the
 86     # beggining of the line
 87     grep -F "$(realpath "$path")" "$dotfiles_list"
 88   fi
 89 
 90   # TODO: Print a warning if we get to here: No such file or directory
 91 }
 92 
 93 # Parse the commands
 94 case "$1" in
 95   # Add files to the list of tracked files
 96   track)
 97     shift
 98 
 99     case "$1" in
100       # Remove files from the list of dotfiles
101       -d|--delete)
102         shift
103 
104         # Remove files from the list of dotfiles
105         for item in "$@"
106         do
107           # TODO: Properly escape pattern
108           echo "Removing '$item' from the list of dotfile"
109           pattern="$(realpath "$item")"
110           pattern="${pattern//\//\\\/}"
111 
112           # Remove the files from the git repository and from the list of files
113           # Awk is used because 'item' may be a directory
114           awk -i inplace "!/^$pattern/ { print }" "$dotfiles_list"
115           call_git rm "$item" --cached -r
116         done
117         ;;
118       *)
119         # Add files to the list of dotfiles
120         for item in "$@"
121         do
122           echo "Adding '$item' to the list of dotfiles"
123           realpath "$item" >> "$dotfiles_list"
124         done
125         ;;
126     esac
127     ;;
128 
129   # Add the files
130   add)
131     shift
132     for item in "$@"
133     do
134       get_pathspec "$item" | call_git add --pathspec-from-file -
135     done
136     ;;
137 
138   # List the dotfiles specified in configurations list
139   list)
140     cat "$dotfiles_list"
141     ;;
142 
143   # Show the configuration files that have been modified or deleted
144   status)
145     call_git status --porcelain \
146       | awk '/^A / { print "✔️  ~/"$2 }; /^ A/ { print "   ~/"$2 }' \
147       | print_files 'new' '30'
148 
149     call_git status --porcelain \
150       | awk '/^M / { print "✔️  ~/"$2 }; /^ M/ { print "   ~/"$2 }' \
151       | print_files 'modified' '33'
152 
153     call_git status --porcelain \
154       | awk '/^D / { print "✔️  ~/"$2 }; /^ D/ { print "   ~/"$2 }' \
155       | print_files 'deleted' '31'
156     ;;
157 
158   # Just pass the following arguments to git
159   --)
160     shift
161     call_git "$@"
162     ;;
163 
164   # Just pass the arguments to git
165   *)
166     call_git "$@"
167     ;;
168 esac
169