]> arthur.barton.de Git - ax-zsh.git/blob - bin/axzshctl
homebrew: Set HOMEBREW_AUTO_UPDATE_SECS to 10 minutes
[ax-zsh.git] / bin / axzshctl
1 #!/usr/bin/env zsh
2 #
3 # AX-ZSH: Alex' Modular ZSH Configuration
4 # Copyright (c) 2015-2020 Alexander Barton <alex@barton.de>
5 #
6
7 # Include "ax-common.sh", if available:
8 for dir ("$HOME/lib" "$HOME/.ax" /usr/local /opt/ax /usr); do
9         [[ -z "$ax_common_sourced" ]] || break
10         ax_common="${dir}/lib/ax/ax-common.sh"
11         [[ -r "$ax_common" ]] && source "$ax_common"
12 done
13 if [[ -z "$ax_common_sourced" ]]; then
14         function ax_msg {
15                 shift
16                 echo "$@"
17         }
18         function ax_error {
19                 ax_msg 2 "$@" >&2
20         }
21 fi
22 unset dir ax_common ax_common_sourced
23
24 function Usage {
25         echo "Usage: $NAME <command> [...]"
26         echo
27         echo "  enable"
28         echo "    Enable AX-ZSH altogether."
29         echo "  disable"
30         echo "    Disable AX-ZSH altogether."
31         echo
32         echo "  enable-plugin <name|directory> [<name|directory> [...]]"
33         echo "    Enable plugin(s)."
34         echo "  disable-plugin <name> [<name> [...]]"
35         echo "    Disable plugin(s)."
36         echo "  list-enabled"
37         echo "    List enabled plugins."
38         echo
39         echo "  reset-plugins"
40         echo "    Reset active plugins to the default set."
41         echo "  enable-default-plugins"
42         echo "    Enable all default plugins."
43         echo "  check-plugins"
44         echo "    Detect plugins which are \"useful\" on this system."
45         echo
46         echo "  set-theme <name>|-"
47         echo "    Set active theme to <name>, or to the default."
48         echo
49         echo "  upgrade"
50         echo "    Upgrade AX-ZSH installation (requires Git)."
51         echo "  update-caches"
52         echo "    Force rebuild of all cache files."
53         echo
54         exit 0
55 }
56
57 function UpdatePluginCache {
58         [[ -r "$AXZSH/cache" ]] || return 0
59
60         [[ "$1" = "-v" ]] && ax_msg - "Updating plugin cache ..."
61         rm -rf \
62                 $AXZSH/cache/zlogin.cache \
63                 $AXZSH/cache/zlogout.cache \
64                 $AXZSH/cache/zprofile.cache \
65                 $AXZSH/cache/zshrc.cache \
66                 || return 1
67         echo "Regenerating cache files ..."
68         zsh -ilc '' >/dev/null
69 }
70
71 function NormalizedPluginName {
72         if [[ "$1" =~ "^[[:alnum:]-]+/[[:alnum:]_-]+$" ]]; then
73                 echo "${1:gs/\//#}"
74         elif [[ "$1" =~ "/" ]]; then
75                 echo "${1:t}"
76         else
77                 echo "$1"
78         fi
79 }
80
81 function EnableAXZSH {
82         for f (~/.zlogin ~/.zlogout ~/.zprofile ~/.zshrc); do
83                 ln -s "$AXZSH/ax.zsh" "$f" \
84                         || ax_error "Failed to create symbolic link for \"$f\"!"
85         done
86 }
87
88 function DisableAXZSH {
89         for f (~/.zlogin ~/.zlogout ~/.zprofile ~/.zshrc); do
90                 if [[ -h "$f" ]]; then
91                         rm "$f" || ax_msg 2 "Failed to remove \"$f\"!"
92                 elif [[ -e "$f" ]]; then
93                         ax_error "Error: Not removing \"$f\", it is not a symbolic link!"
94                 else
95                         ax_msg 1 "Warning: \"$f\" already does not exist. Ok."
96                 fi
97         done
98 }
99
100 function EnablePlugin {
101         local plugin=$(NormalizedPluginName "$1")
102         local dir="$AXZSH/active_plugins"
103
104         if [[ -h "$dir/$plugin" ]]; then
105                 ax_msg 1 "Plugin \"$1\" already active!"
106                 return 1
107         fi
108
109         if [[ "$1" =~ "^[[:alnum:]-]+/[[:alnum:]_-]+$" ]]; then
110                 # GitHub plugin
111                 mkdir -p "$AXZSH/repos"
112                 if [[ ! -e "$AXZSH/repos/$plugin" ]]; then
113                         ax_msg - "Cloning module from GitHub ..."
114                         git clone --depth=1 "https://github.com/$1.git" \
115                          "$AXZSH/repos/$plugin" \
116                                 || ax_error "Failed to clone repository!"
117                 fi
118                 # Try to enable a theme in this "foreign module", but ignore
119                 # errors: we don't know if this module provides a theme or is
120                 # a "regular" plugin ...
121                 if SetTheme "$plugin" 2>/dev/null; then
122                         ax_msg 0 "Module \"$1\" was enabled as theme \"${plugin#*#}\"."
123                         # A theme was enabled: So assume that this is a theme
124                         # and don't enable it as plugin.
125                         return 0
126                 fi
127                 echo "Trying to enable \"$1\" as plugin ..."
128         fi
129
130         for dname (
131                 "$plugin:A"
132                 "$AXZSH_PLUGIN_D/$plugin"
133                 "$ZSH_CUSTOM/$plugin"
134                 "$AXZSH/custom_plugins/$plugin"
135                 "$AXZSH/repos/$plugin"
136                 "$AXZSH/plugins/$plugin"
137                 "$AXZSH/default_plugins/$plugin"
138                 "$AXZSH/core/$plugin"
139         ); do
140                 [[ ! -d "$dname" ]] && continue
141                 mkdir -p "$dir"
142                 if ! (
143                         cd "$dir" || exit 9
144                         ln -s "$dname" "$PWD"
145                 ); then
146                         ax_error "Failed to create link!"
147                         return 1
148                 fi
149                 ax_msg 0 "Plugin \"$plugin\" enabled."
150                 return 0
151         done
152
153         ax_error "Plugin \"$1\" not found!"
154         return 1
155 }
156
157 function DisablePlugin {
158         local plugin=$(NormalizedPluginName "$1")
159         local dir="$AXZSH/active_plugins"
160         local r=-1
161
162         # Active theme?
163         if [[ $(readlink "$AXZSH/active_theme") = "$AXZSH/repos/$plugin/"* ]]; then
164                 rm "$AXZSH/active_theme"; r=$?
165         fi
166
167         # Active plugin?
168         if [[ -h "$dir/$plugin" ]]; then
169                 rm "$dir/$plugin"; r=$?
170         fi
171
172         if [[ $r -eq -1 ]]; then
173                 ax_msg 1 "Plugin \"$1\" not active, nothing to do?"
174                 r=1
175         fi
176
177         if [[ "$plugin" = *"#"* ]]; then
178                 # Name matches a cloned repository, try to clean up!
179                 echo "Cleaning up cloned repository ..."
180                 rm -fr "$AXZSH/repos/$plugin"
181         fi
182
183         return $r
184 }
185
186 function ListEnabledPlugins {
187         for plugin ($AXZSH/active_plugins/*(N)); do
188                 print ${plugin:t:s/#/\//}
189         done
190         return 0
191 }
192
193 function ResetPlugins {
194         local dir="$AXZSH/active_plugins"
195         local r1=0, r2=0
196
197         if [[ -e "$dir" ]]; then
198                 ax_msg - "Removing all symbolic links in $dir ..."
199                 find "$dir" -type l -print -delete; r1=$?
200         fi
201
202         ax_msg - "Removing all external repositories in \"$AXZSH/repos\" ..."
203         rm -fr "$AXZSH/repos"; r2=$?
204
205         [[ $r1 == 0 && $r2 == 0 ]] && return 0 || return 1
206 }
207
208 function EnableDefaultPlugins {
209         local dir="$AXZSH/active_plugins"
210
211         ax_msg - "Activating default plugins ..."
212         mkdir -p "$dir"
213         (
214                 cd "$dir" || exit 9
215                 ln -sf "$AXZSH/default_plugins/"* "$PWD"
216         )
217         return $?
218 }
219
220 function SetTheme {
221         local link_name="$AXZSH/active_theme"
222
223         if [[ "$1" = "-" ]]; then
224                 rm -f "$link_name" || return 1
225                 ax_msg 0 "Theme settings have been reset."
226                 return 0
227         fi
228
229         if [[ -r "$1" ]]; then
230                 theme="$1"
231         elif [[ -r "$AXZSH/custom_themes/$1.axzshtheme" ]]; then
232                 theme="$AXZSH/custom_themes/$1.axzshtheme"
233         elif [[ -r "$AXZSH/themes/$1.axzshtheme" ]]; then
234                 theme="$AXZSH/themes/$1.axzshtheme"
235         else
236                 # Look for theme in specific remote module:
237                 for f (
238                         "$AXZSH/repos/$1/"*.axzshtheme(N[1])
239                         "$AXZSH/repos/$1/"*.zsh-theme(N[1])
240                 ); do
241                         if [[ -r "$f" ]]; then
242                                 theme="$f"
243                                 break
244                         fi
245                 done
246
247                 # Look for theme inside of installed plugins:
248                 for dname (
249                         "$AXZSH/custom_themes"
250                         "$AXZSH/custom_plugins/"*(N)
251                         "$AXZSH/repos/"*(N)
252                 ); do
253                         if [[ -r "$dname/$1.axzshtheme" ]]; then
254                                 theme="$dname/$1.axzshtheme"
255                                 break
256                         elif [[ -r "$dname/$1.zsh-theme" ]]; then
257                                 theme="$dname/$1.zsh-theme"
258                                 break
259                         fi
260                 done
261
262                 if [[ -z "$theme" ]]; then
263                         ax_error "Theme \"$1\" not found!"
264                         return 1
265                 fi
266         fi
267         ln -fs "$theme" "$link_name" || return 1
268         return $?
269 }
270
271 function UpgradeAXZSH {
272         if [[ $+commands[git] -eq 0 ]]; then
273                 ax_error "The git(1) command is not available!"
274                 return 1
275         fi
276         if [[ ! -d "$AXZSH/.git" ]]; then
277                 ax_error "AX-ZSH seems not to be installed using Git. Can't upgrade!"
278                 return 1
279         fi
280
281         ax_msg - "Upgrading AX-ZSH in \"$AXZSH\" using git(1) ..."
282         ( cd "$AXZSH" && git pull --ff-only )
283 }
284
285 function UpgradeForeignPlugins {
286         if [[ $+commands[git] -eq 0 ]]; then
287                 ax_error "The git(1) command is not available!"
288                 return 1
289         fi
290
291         for dir ($AXZSH/repos/*(N)); do
292                 name=${dir:t:s/#/\//}
293                 if [[ -d "$dir/.git" ]]; then
294                         ax_msg - "Upgrading \"$name\" [git] ..."
295                         (
296                                 cd "$dir"
297                                 git pull --ff-only || ax_error "Pull failed!"
298                         )
299                 else
300                         ax_error "Unknown repository type!"
301                 fi
302         done
303 }
304
305 function CheckPlugins {
306         missing_plugins=()
307         invalid_plugins=()
308
309         ax_msg - "Checking plugins ..."
310         for dir ($AXZSH/plugins/*(N)); do
311                 plugin=${dir:t}
312
313                 # Test if plugin is already enabled
314                 [[ -e "$AXZSH/active_plugins/$plugin" ]] \
315                         && enabled=" (enabled)" \
316                         || unset enabled
317
318                 # Test plugin ...
319                 printf " - \"%s\"%s ... " "$plugin" "$enabled"
320                 new_plugin=""
321                 for script ($AXZSH/plugins/$plugin/$plugin.{zshrc,zprofile}); do
322                         [[ -r "$script" ]] || continue
323                         AXZSH_PLUGIN_CHECK=1 zsh -i -c "source $script"; r=$?
324                         if [[ $r -eq 0 ]]; then
325                                 new_plugin=$plugin
326                                 break
327                         fi
328                 done
329                 if [[ -n "$new_plugin" ]]; then
330                         detected_plugins+=($new_plugin)
331                         [[ -n "$enabled" ]] || missing_plugins+=($new_plugin)
332                         ax_msg 0 "OK."
333                 elif [[ $r -eq 91 ]]; then
334                         ax_msg 1 "ignored."
335                 elif [[ $r -eq 92 ]]; then
336                         ax_msg 1 "optional."
337                 else
338                         [[ -n "$enabled" ]] && invalid_plugins+=($plugin)
339                         ax_msg 2 "failed."
340                 fi
341         done
342         echo
343
344         result=0
345         if [[ -n "$missing_plugins" ]]; then
346                 ax_msg 1 "Run the following command to enable all missing plugins:"
347                 echo "$AXZSH/bin/axzshctl enable-plugin" $missing_plugins
348                 echo
349                 result=1
350         else
351                 ax_msg 0 "All detected plugins are already enabled."
352         fi
353
354         if [[ -n "$invalid_plugins" ]]; then
355                 ax_msg 1 "Run the following command to disable all failed plugins:"
356                 echo "$AXZSH/bin/axzshctl disable-plugin" $invalid_plugins
357                 result=1
358         else
359                 ax_msg 0 "No failed plugins are enabled."
360         fi
361
362         echo
363         return $result
364 }
365
366 NAME="$0:t"
367
368 [[ $# -gt 0 ]] || Usage
369
370 if [[ -z "$AXZSH" || ! -r "$AXZSH/ax.zsh" ]]; then
371         [[ -r "$HOME/.axzsh/ax.zsh" ]] && AXZSH="$HOME/.axzsh"
372         if [[ ! -r "$AXZSH/ax.zsh" ]]; then
373                 ax_error "Oops, \"AXZSH\" is not set or invalid and can't be autodetected!"
374                 exit 3
375         fi
376 fi
377
378 cmd="$1"
379 shift
380
381 case "$cmd" in
382         "enable")
383                 [[ $# -eq 0 ]] || Usage
384                 EnableAXZSH
385                 ;;
386         "disable")
387                 [[ $# -eq 0 ]] || Usage
388                 DisableAXZSH
389                 ;;
390         "enable-plugin")
391                 [[ $# -gt 0 ]] || Usage
392                 for plugin in "$@"; do
393                         EnablePlugin "$plugin"
394                 done
395                 UpdatePluginCache
396                 ;;
397         "disable-plugin")
398                 [[ $# -gt 0 ]] || Usage
399                 for plugin in "$@"; do
400                         DisablePlugin "$plugin"
401                 done
402                 UpdatePluginCache
403                 ;;
404         "list-enabled")
405                 [[ $# -eq 0 ]] || Usage
406                 ListEnabledPlugins
407                 ;;
408         "reset-plugins")
409                 [[ $# -eq 0 ]] || Usage
410                 ResetPlugins
411                 EnableDefaultPlugins
412                 UpdatePluginCache
413                 ;;
414         "enable-default-plugins")
415                 [[ $# -eq 0 ]] || Usage
416                 EnableDefaultPlugins && UpdatePluginCache
417                 ;;
418         "check-plugins")
419                 [[ $# -eq 0 ]] || Usage
420                 CheckPlugins
421                 ;;
422         "set-theme")
423                 [[ $# -eq 1 ]] || Usage
424                 SetTheme "$1"
425                 ;;
426         "upgrade")
427                 [[ $# -eq 0 ]] || Usage
428                 UpgradeAXZSH
429                 UpgradeForeignPlugins
430                 UpdatePluginCache
431                 ;;
432         "update-caches")
433                 [[ $# -eq 0 ]] || Usage
434                 UpdatePluginCache -v
435                 ;;
436         "--help")
437                 Usage
438                 ;;
439         *)
440                 ax_error "Invalid command \"$cmd\"!"
441                 ax_error "Try \"$0 --help\" for more information."
442                 exit 2
443 esac