Skip to content

Commit

Permalink
add basic lock file support
Browse files Browse the repository at this point in the history
docs: document basic usage
elpaca-menu-functions: add elpaca-menu-lock-file
elpaca-load-lockfile: remove
elpaca-lock-file: user option to set lock file
elpaca-menu-lock-file: menu to defer to lock file recipes
elpaca--lock-file-init-p: predicate to exclude non-init elpacas
elpaca-lock-file-functions: filtering hook for elpaca-write-lock-file
elpaca-write-lock-file: command to write lock files

Related: #24 #151 #404 #405
  • Loading branch information
progfolio committed Feb 2, 2025
1 parent 8773a57 commit 722b36c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 26 deletions.
11 changes: 11 additions & 0 deletions doc/elpaca.texi
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Basic concepts
* Orders:: Incomplete, or custom recipes.
* Queues:: Order processing groups.
* Installing Packages::
* Lock Files::
Recipes
Expand Down Expand Up @@ -324,6 +325,7 @@ This includes loading of saved customizations. e.g.
* Orders:: Incomplete, or custom recipes.
* Queues:: Order processing groups.
* Installing Packages::
* Lock Files::
@end menu

@node Recipes
Expand Down Expand Up @@ -884,6 +886,15 @@ This can be used to change a package's recipe prior to rebuilding it.
Note that rebuilding a package does not @strong{reload} a package.
It's best to restart Emacs after a successful rebuild if you wish to have the changes loaded.

@node Lock Files
@subsection Lock Files

A lock file is a collection of recipes for the exact versions of installed packages.
They can be used to build different versions of an Emacs configuration when combined with init file package declarations.

The @samp{elpaca-write-lock-file} command is used to write a lock file to disk.
Setting the @samp{elpaca-lock-file} variable to that file's path will cause Elpaca to use those versions of the recipes when installing packages assuming the @samp{elpaca-lock-file-menu} is the first menu in @samp{elpaca-menu-functions}.

@node use-package Integration
@section use-package Integration

Expand Down
10 changes: 10 additions & 0 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- [elpaca-order-functions](#elpaca-order-functions)
- [Queues](#queues)
- [Installing Packages](#installing-packages)
- [Lock Files](#lock-files)
- [use-package Integration](#use-package-integration)
- [UI](#ui)
- [Searching](#searching)
Expand Down Expand Up @@ -716,6 +717,15 @@ This macro is for programmatic use in one’s init file. Any of the followin
Interactively evaluating an `elpaca` declaration will re-process the order. This can be used to change a package’s recipe prior to rebuilding it. Note that rebuilding a package does not **reload** a package. It’s best to restart Emacs after a successful rebuild if you wish to have the changes loaded.


<a id="lock-files"></a>

### Lock Files

A lock file is a collection of recipes for the exact versions of installed packages. They can be used to build different versions of an Emacs configuration when combined with init file package declarations.

The `elpaca-write-lock-file` command is used to write a lock file to disk. Setting the `elpaca-lock-file` variable to that file&rsquo;s path will cause Elpaca to use those versions of the recipes when installing packages assuming the `elpaca-lock-file-menu` is the first menu in `elpaca-menu-functions`.


<a id="use-package-integration"></a>

## use-package Integration
Expand Down
12 changes: 12 additions & 0 deletions doc/manual.org
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,18 @@ This can be used to change a package's recipe prior to rebuilding it.
Note that rebuilding a package does not *reload* a package.
It's best to restart Emacs after a successful rebuild if you wish to have the changes loaded.

*** Lock Files
:PROPERTIES:
:CUSTOM_ID: lock-files
:END:

A lock file is a collection of recipes for the exact versions of installed packages.
They can be used to build different versions of an Emacs configuration when combined with init file package declarations.

The =elpaca-write-lock-file= command is used to write a lock file to disk.
Setting the =elpaca-lock-file= variable to that file's path will cause Elpaca to use those versions of the recipes when installing packages assuming the =elpaca-lock-file-menu= is the first menu in =elpaca-menu-functions=.


** use-package Integration
:PROPERTIES:
:CUSTOM_ID: use-package-integration
Expand Down
69 changes: 43 additions & 26 deletions elpaca.el
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Simplified, faster version of `alist-get'."
:build '(:not elpaca--compile-info))))))))

(defcustom elpaca-menu-functions
'( elpaca-menu-extensions elpaca-menu-org elpaca-menu-melpa
'( elpaca-menu-lock-file elpaca-menu-extensions elpaca-menu-org elpaca-menu-melpa
elpaca-menu-non-gnu-elpa elpaca-menu-gnu-elpa elpaca-menu-declarations)
"Abnormal hook to lookup packages in menus.
Each function is passed a request, which may be any of the following symbols:
Expand Down Expand Up @@ -1971,32 +1971,25 @@ If INTERACTIVE is non-nil, process queues."
(not (string-empty-p (elpaca-process-output
"git" "-c" "status.branch=false" "status" "--short")))))

(defun elpaca-load-lockfile (&optional lockfile _force)
"Load LOCKFILE. If FORCE is non-nil, @TODO."
(interactive "fLockfile: ")
(message "%S" lockfile))
(defcustom elpaca-lock-file nil "Path of `elpaca-menu-lock-file' cache." :type 'file)

(defun elpaca-write-lockfile (path)
"Write lockfile to PATH for current state of package repositories."
(interactive "FWrite lockfile to: ")
(elpaca--write-file path
(pp (nreverse
(cl-loop with seen
for (item . e) in (elpaca--queued)
unless (member item seen)
for rev =
(let ((default-directory (elpaca<-repo-dir e)))
(elpaca-with-process-call ("git" "rev-parse" "HEAD")
(if success
(string-trim stdout)
(error "Unable to write lockfile: %s %S" item stderr))))
for recipe = (copy-tree (elpaca<-recipe e))
do (setq recipe (plist-put recipe :ref rev))
;;@MAYBE: recipe (plist-put recipe :pin t))
collect (cons item (list :source "lockfile"
:date (current-time)
:recipe recipe))
do (push item seen))))))
(defvar elpaca-menu-lock-file--cache nil)
(defun elpaca-menu-lock-file (request &optional item)
"If REQUEST is `index`, return `elpaca-lock-file' ITEM, otherwise update menu."
(when elpaca-lock-file
(if-let* (((eq request 'index))
(cache (or elpaca-menu-lock-file--cache (elpaca-menu-lock-file 'update))))
(if item (alist-get item cache) cache)
(setq elpaca-menu-lock-file--cache (elpaca--read-file elpaca-lock-file)))))

(defun elpaca--lock-file-init-p (e)
"Return non-nil if E declared in init file during startup."
(elpaca<-init e))

(defcustom elpaca-lock-file-functions (list #'elpaca--lock-file-init-p)
"List of functions which take an E as a first argument.
Any function returning nil will prevent the E from being written to the file."
:type 'hook)

;;;###autoload
(defmacro elpaca-with-dir (id type &rest body)
Expand All @@ -2007,6 +2000,30 @@ TYPE is either `repo` or `build`, for repo or build directory."
(default-directory (,(intern (format "elpaca<-%s-dir" (symbol-name type))) e)))
,@body))

;;;###autoload
(defun elpaca-write-lock-file (path &optional elpacas)
"Write lock file to PATH for current state of queued ELPACAS."
(interactive "FWrite lock-file to: ")
(elpaca--write-file path
(cl-loop
with print-circle = t with seen with es
for (id . e) in (or elpacas (elpaca--queued))
do (when (and (not (member id seen))
(run-hook-with-args-until-failure 'elpaca-lock-file-functions e))
(push `(,id
( :source "elpaca-menu-lock-file" :date ,(current-time)
:recipe
,(plist-put (copy-tree (elpaca<-recipe e))
:ref
(elpaca-with-dir id repo
(elpaca-with-process-call ("git" "rev-parse" "HEAD")
(if success (string-trim stdout)
(error "Unable to write lock-file: %s %S" id stderr)))))))
es)
(push id seen))
finally (message "wrote %d elpacas to %s" (length es) path)
(pp (nreverse es)))))

(declare-function elpaca-ui-current-package "elpaca-ui")
;;;###autoload
(defun elpaca-visit (&optional id build)
Expand Down

0 comments on commit 722b36c

Please sign in to comment.