doomemacs/lisp/cli/sync.el
Henrik Lissner b01e496405
fix(cli): doom sync: rebuild-all loop
Addresses an edge case where 'doom sync' could fall into an inescapable
'rebuild all packages' loop.

Fix: #7775
2024-03-29 23:41:12 -04:00

110 lines
4.6 KiB
EmacsLisp

;;; lisp/cli/sync.el --- synchronize config command -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(load! "packages")
;;
;;; Variables
(defvar doom-after-sync-hook ()
"Hooks run after 'doom sync' synchronizes the user's config with Doom.")
(defvar doom-before-sync-hook ()
"Hooks run before 'doom sync' synchronizes the user's config with Doom.")
(defvar doom-cli-sync-info-file (file-name-concat doom-profile-data-dir "sync"))
;;
;;; Commands
(defcli! ((sync s))
((noenvvar? ("-e") "Don't regenerate the envvar file")
(update? ("-u") "Update all installed packages after syncing")
(noupdate? ("-U") "Don't update any packages")
(purge? ("--gc") "Purge orphaned package repos & regraft them")
(jobs ("-j" "--jobs" num) "How many threads to use for native compilation")
(rebuild? ("-b" "--rebuild") "Rebuild all installed packages, unconditionally")
(nobuild? ("-B") "Don't rebuild packages when hostname or Emacs version has changed")
&context context)
"Synchronize your config with Doom Emacs.
This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you:
1. Modify your `doom!' block,
2. Add or remove `package!' blocks to your config,
3. Add or remove autoloaded functions in module autoloaded files,
4. Update Doom outside of Doom (e.g. with git),
5. Move your Doom config (either $EMACSDIR or $DOOMDIR) to a new location.
6. When you up (or down) grade Emacs itself.
It will ensure that unneeded packages are removed, all needed packages are
installed, autoloads files are up-to-date and no byte-compiled files have gone
stale.
OPTIONS:
-j, --jobs
Defaults to the maximum number of threads (or 1, if your CPU's threadcount
can't be determined)."
:benchmark t
(when (doom-profiles-bootloadable-p)
(call! '(profiles sync "--reload")))
(when jobs
(setq native-comp-async-jobs-number (truncate jobs)))
(run-hooks 'doom-before-sync-hook)
(add-hook 'kill-emacs-hook #'doom-sync--abort-warning-h)
(print! (item "Using Emacs %s @ %s") emacs-version (path invocation-directory invocation-name))
(print! (start "Synchronizing %S profile..." ) (or (car doom-profile) "default"))
(unwind-protect
(print-group!
;; If the user has up/downgraded Emacs since last sync, or copied their
;; config to a different system, then their packages need to be
;; recompiled. This is necessary because Emacs byte-code is not
;; necessarily back/forward compatible across major versions, and many
;; packages bake in hardcoded data at compile-time.
(pcase-let ((`(,old-version . ,old-host) (doom-file-read doom-cli-sync-info-file :by 'read :noerror t))
(to-rebuild nil))
(when (and old-version (not (equal old-version emacs-version)))
(print! (warn "Emacs version has changed since last sync (from %s to %s)") old-version emacs-version)
(setq to-rebuild t))
(when (and old-host (not (equal old-host (system-name))))
(print! (warn "Your system has changed since last sync"))
(setq to-rebuild t))
(when (and to-rebuild (not (doom-cli-context-suppress-prompts-p context)))
(cond (nobuild?
(print! (warn "Packages must be rebuilt, but -B has prevented it. Skipping...")))
((doom-cli-context-get context 'upgrading)
(print! (warn "Packages will be rebuilt"))
(setq rebuild? t))
((y-or-n-p (format! " %s" "Installed packages must be rebuilt. Do so now?"))
(setq rebuild? t))
((exit! 0)))))
(when (and (not noenvvar?)
(file-exists-p doom-env-file))
(call! '(env)))
(doom-packages-ensure rebuild?)
(unless noupdate? (doom-packages-update (not update?)))
(doom-packages-purge purge? purge? purge? purge? purge?)
(when (doom-profile-generate)
(print! (item "Restart Emacs or use 'M-x doom/reload' for changes to take effect"))
(run-hooks 'doom-after-sync-hook))
(when (or rebuild? (not (file-exists-p doom-cli-sync-info-file)))
(with-temp-file doom-cli-sync-info-file
(prin1 (cons emacs-version (system-name)) (current-buffer))))
t)
(remove-hook 'kill-emacs-hook #'doom-sync--abort-warning-h)))
;;
;;; Helpers
(defun doom-sync--abort-warning-h ()
(print! (warn "Script was abruptly aborted, leaving Doom in an incomplete state!"))
(print! (item "Run 'doom sync' to repair it.")))
(provide 'doom-cli-sync)
;;; sync.el ends here