config.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env guile
  2. !#
  3. ;;; config.scm --- Deploy config files
  4. ;; Copyright © 2015–2017 Alex Kost
  5. ;; Author: Alex Kost <alezost@gmail.com>
  6. ;; Created: 3 Mar 2015
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;;
  12. ;; This program is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;;
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;; This script may be used to list, show info and deploy (create
  21. ;; symlinks to) various configuration files.
  22. ;;; Code:
  23. (use-modules
  24. (ice-9 format)
  25. (ice-9 match)
  26. (srfi srfi-1)
  27. (srfi srfi-26)
  28. (srfi srfi-37)
  29. (al configs)
  30. (al files)
  31. (al links)
  32. (al messages)
  33. (al places)
  34. (al sources)
  35. (al utils))
  36. (set-locale)
  37. ;;; Configs
  38. (define (my-repo name)
  39. (string-append "https://github.com/alezost/" name ".git"))
  40. (define %main-configs
  41. (list
  42. (config* #:name "config"
  43. #:source (source* #:uri (my-repo "config")
  44. #:directory (config-file))
  45. #:links (list
  46. (link* #:filename (bin-file "config")
  47. #:target (config-file "config.scm"))))
  48. (config* #:name "guile"
  49. #:source (source* #:uri (my-repo "guile-config")
  50. #:directory (config-file "guile")))
  51. (config* #:name "guix"
  52. #:source (source* #:uri (my-repo "guix-config")
  53. #:directory (guix-config-file)))
  54. (config* #:name "shepherd"
  55. #:source (source* #:uri (my-repo "shepherd-config")
  56. #:directory (config-file "shepherd"))
  57. #:links (list
  58. (link* #:filename (home-file ".config/shepherd/init.scm")
  59. #:target (config-file "shepherd/init.scm"))))
  60. (config* #:name "guile-daemon"
  61. #:source (source* #:uri (my-repo "guile-daemon-config")
  62. #:directory (config-file "guile-daemon"))
  63. #:links (list
  64. (link* #:filename (home-file ".config/guile-daemon/init.scm")
  65. #:target (config-file "guile-daemon/init.scm"))))
  66. (config* #:name "shell"
  67. #:source (source* #:uri (my-repo "shell-config")
  68. #:directory (config-file "shell"))
  69. #:links (list
  70. (link* #:filename (home-file ".inputrc")
  71. #:target (config-file "shell/bash/inputrc"))
  72. (link* #:filename (home-file ".bashrc")
  73. #:target (config-file "shell/bash/bashrc"))
  74. (link* #:filename (home-file ".bash_profile")
  75. #:target (config-file "shell/bash/user-profile"))))
  76. (config* #:name "emacs"
  77. #:source (source* #:uri (my-repo "emacs-config")
  78. #:directory (config-file "emacs"))
  79. #:links (list
  80. (link* #:filename (home-file ".emacs.d/init.el")
  81. #:target (config-file "emacs/init/init.el"))))
  82. (config* #:name "stumpwm"
  83. #:source (source* #:uri (my-repo "stumpwm-config")
  84. #:directory (config-file "stumpwm"))
  85. #:links (list
  86. (link* #:filename (home-file ".stumpwmrc")
  87. #:target (config-file "stumpwm/init.lisp"))))
  88. (config* #:name "conkeror"
  89. #:source (source* #:uri (my-repo "conkeror-config")
  90. #:directory (config-file "conkeror"))
  91. #:links (list
  92. (link* #:filename (home-file ".conkerorrc")
  93. #:target (config-file "conkeror/init.js"))))
  94. (config* #:name "wget"
  95. #:links (list
  96. (link* #:filename (home-file ".wgetrc")
  97. #:target (config-file "wget/wgetrc"))))
  98. (config* #:name "top"
  99. #:links (list
  100. (link* #:filename (home-file ".toprc")
  101. #:target (config-file "top/toprc"))))
  102. (config* #:name "sbcl"
  103. #:links (list
  104. (link* #:filename (home-file ".sbclrc")
  105. #:target (config-file "sbcl/sbclrc"))))
  106. (config* #:name "git"
  107. #:links (list
  108. (link* #:filename (home-file ".gitconfig")
  109. #:target (config-file "git/gitconfig"))))
  110. (config* #:name "postgresql"
  111. #:links (list
  112. (link* #:filename (home-file ".postgresql/data/postgresql.conf")
  113. #:target (config-file "postgresql/postgresql.conf"))
  114. (link* #:filename (home-file ".postgresql/data/pg_hba.conf")
  115. #:target (config-file "postgresql/pg_hba.conf"))))
  116. (config* #:name "lirc"
  117. #:links (list
  118. (link* #:filename (home-file ".lircrc")
  119. #:target (config-file "lirc/lircrc"))))
  120. (config* #:name "mime"
  121. #:links (list
  122. (link* #:filename (home-file ".mime.types")
  123. #:target (config-file "mime/mime.types"))))
  124. (config* #:name "rtorrent"
  125. #:links (list
  126. (link* #:filename (home-file ".rtorrent.rc")
  127. #:target (config-file "rtorrent/rc"))))
  128. (config* #:name "tvtime"
  129. #:links (list
  130. (link* #:filename (home-file ".tvtime")
  131. #:target (config-file "tvtime"))))
  132. (config* #:name "mplayer"
  133. #:links (list
  134. (link* #:filename (home-file ".mplayer")
  135. #:target (config-file "mplayer"))))
  136. (config* #:name "mpv"
  137. #:links (list
  138. (link* #:filename (home-file ".config/mpv")
  139. #:target (config-file "mpv"))))
  140. (config* #:name "youtube-dl"
  141. #:links (list
  142. (link* #:filename (home-file ".config/youtube-dl")
  143. #:target (config-file "youtube-dl"))))
  144. (config* #:name "yt-dlp"
  145. #:links (list
  146. (link* #:filename (home-file ".config/yt-dlp")
  147. #:target (config-file "yt-dlp"))))
  148. (config* #:name "openbox"
  149. #:links (list
  150. (link* #:filename (home-file ".config/openbox")
  151. #:target (config-file "openbox"))))
  152. (config* #:name "dunst"
  153. #:links (list
  154. (link* #:filename (home-file ".config/dunst")
  155. #:target (config-file "dunst"))))
  156. (config* #:name "zathura"
  157. #:links (list
  158. (link* #:filename (home-file ".config/zathura")
  159. #:target (config-file "zathura"))))
  160. (config* #:name "fontconfig"
  161. #:links (list
  162. (link* #:filename (home-file ".config/fontconfig")
  163. #:target (config-file "fontconfig"))))
  164. (config* #:name "gtk"
  165. #:links (list
  166. (link* #:filename (home-file ".gtkrc-2.0")
  167. #:target (config-file "gtk/gtkrc-2.0"))
  168. (link* #:filename (home-file ".config/gtk-3.0")
  169. #:target (config-file "gtk/3.0"))))
  170. (config* #:name "X"
  171. #:links (list
  172. (link* #:filename (home-file ".Xmodmap")
  173. #:target (config-file "X/Xmodmap"))
  174. (link* #:filename (home-file ".Xresources")
  175. #:target (config-file "X/Xresources"))
  176. (link* #:filename (home-file "XTerm")
  177. #:target (config-file "X/XTerm"))))))
  178. (define %secret-configs
  179. (catch #t
  180. (lambda () (primitive-load (secret-config-file "config.scm")))
  181. (const '())))
  182. (define %configs
  183. (append %main-configs %secret-configs))
  184. (define (configs-names)
  185. "Return list of all available config names."
  186. (map config-name %configs))
  187. (define (configs-links)
  188. "Return list of all available config links."
  189. (append-map config-links %configs))
  190. ;;; Command-line args
  191. (define (show-help)
  192. (display "Usage: config OPTION [CONFIG]...
  193. List, show info or deploy available/specified configurations.")
  194. (display "\n
  195. Options:
  196. -h, --help display this help and exit
  197. -l, --list list available configurations and exit
  198. -O, --list-old list old configuration files and exit
  199. -o, --ls-old perform 'ls -l' on the old configuration files and exit
  200. -D, --delete-old delete old configuration files and exit
  201. -s, --show show info of the specified configurations
  202. -f, --fetch fetch (git clone) source of the specified configurations
  203. -d, --deploy deploy (create symlinks) the specified configurations")
  204. (display "\n
  205. If '--show', '--fetch' or '--deploy' option is used and no configuration
  206. is specified, then all available ones will be shown, fetched or
  207. deployed. '--fetch' and '--deploy' can be specified together.")
  208. (newline))
  209. (define* (list-strings strings #:key title
  210. (proc (lambda (s)
  211. (format #t "~{~a~%~}" (sort s string-ci<)))))
  212. "Display list of STRINGS using PROC."
  213. (when title
  214. (display title)
  215. (newline))
  216. (proc strings))
  217. (define (show-configs-names)
  218. (list-strings (configs-names)
  219. #:title "Available configurations:"))
  220. (define (call-with-old-files proc)
  221. "Call PROC on old configuration files."
  222. (let ((files (old-files)))
  223. (if (null? files)
  224. (display "There are no old configuration files.\n")
  225. (proc (old-files)))))
  226. (define (show-old-files)
  227. (call-with-old-files
  228. (lambda (files)
  229. (list-strings files
  230. #:title "Old configuration files:"))))
  231. (define (ls-old-files)
  232. "Perform 'ls -l' on the old configuration files."
  233. (call-with-old-files
  234. (lambda (files)
  235. (list-strings
  236. files
  237. #:title "Old configuration files:"
  238. #:proc (lambda (files)
  239. (apply system*
  240. "ls" "-l" "--directory" "--color=auto"
  241. (sort files string-ci<)))))))
  242. (define (delete-old-files)
  243. (call-with-old-files (cut map delete-file-recursively <>)))
  244. (define %options
  245. (list (option '(#\h "help") #f #f
  246. (lambda _
  247. (show-help)
  248. (exit 0)))
  249. (option '(#\l "list") #f #f
  250. (lambda _
  251. (show-configs-names)
  252. (exit 0)))
  253. (option '(#\O "list-old") #f #f
  254. (lambda _
  255. (show-old-files)
  256. (exit 0)))
  257. (option '(#\o "ls-old") #f #f
  258. (lambda _
  259. (ls-old-files)
  260. (exit 0)))
  261. (option '(#\D "delete-old") #f #f
  262. (lambda _
  263. (delete-old-files)
  264. (exit 0)))
  265. (option '(#\s "show") #f #f
  266. (lambda (opt name arg seed)
  267. (alist-cons 'action 'show seed)))
  268. (option '(#\f "fetch") #f #f
  269. (lambda (opt name arg seed)
  270. (alist-cons 'action 'fetch seed)))
  271. (option '(#\d "deploy") #f #f
  272. (lambda (opt name arg seed)
  273. (alist-cons 'action 'deploy seed)))))
  274. (define (parse-args args)
  275. "Return alist of options from command-line ARGS."
  276. (reverse
  277. (args-fold args %options
  278. (lambda (opt name arg seed)
  279. (print-error "Unrecognized option: '~a'" name)
  280. seed)
  281. (lambda (arg seed)
  282. (alist-cons 'config arg seed))
  283. '())))
  284. (define (old-filename filename)
  285. "Return name of an old config file that should be backed up."
  286. (string-append filename "-old"))
  287. (define (old-unique-filename filename)
  288. "Return unique name of an old config file that should be backed up."
  289. (unique-filename (old-filename filename)))
  290. (define (old-files)
  291. "Return list of old config files."
  292. (append-map (lambda (link)
  293. (find-matching-files
  294. (old-filename (link-filename link))))
  295. (configs-links)))
  296. (define deploy-config*
  297. (cut deploy-config <> old-unique-filename))
  298. (define (options->values name opts)
  299. "Return list of values for NAME from OPTS alist.
  300. Example:
  301. (options->values 'a '((a . 1) (b . 2) (a . 3))) => (1 3)"
  302. (filter-map (match-lambda
  303. ((key . value)
  304. (and (eq? key name) value))
  305. (_ #f))
  306. opts))
  307. (define options->config-names
  308. (cut options->values 'config <>))
  309. (define options->action-names
  310. (cut options->values 'action <>))
  311. (define (action-names->action names)
  312. "Return config procedure from action NAMES."
  313. (let ((show? (memq 'show names))
  314. (fetch? (memq 'fetch names))
  315. (deploy? (memq 'deploy names)))
  316. (cond
  317. (show? show-config)
  318. ((and fetch? deploy?)
  319. (lambda (config)
  320. (fetch-config config)
  321. (deploy-config* config)))
  322. (fetch? fetch-config)
  323. (deploy? deploy-config*)
  324. (else #f))))
  325. (define (lookup-config name)
  326. "Return config record from '%configs' list by its NAME."
  327. (find (lambda (config)
  328. (equal? (config-name config) name))
  329. %configs))
  330. (define (lookup-configs names)
  331. "Return config records from '%configs' list by their NAMES."
  332. (filter-map
  333. (lambda (name)
  334. (or (lookup-config name)
  335. (begin (print-error "No '~a' configuration was found" name)
  336. #f)))
  337. names))
  338. (define (main arg0 . args)
  339. (let* ((opts (parse-args args))
  340. (config-names (options->config-names opts))
  341. (action-names (options->action-names opts))
  342. (configs (if (null? config-names)
  343. %configs
  344. (lookup-configs config-names)))
  345. (action (or (action-names->action action-names)
  346. (leave "\
  347. No action is specified, try --help for more information"))))
  348. (map action configs)))
  349. (when (batch-mode?)
  350. (apply main (command-line)))
  351. ;;; config.scm ends here