elpaca icon indicating copy to clipboard operation
elpaca copied to clipboard

[Bug/Support]: How to install packages using emacs --batch?

Open shackra opened this issue 2 months ago • 7 comments

Confirmation

  • [x] I have checked the documentation (README, Wiki, docstrings, etc)
  • [ ] I am checking these without reading them.
  • [x] I have searched previous issues to see if my question is a duplicate.

Elpaca Version

commit 10e6544

Operating System

NixOS

Description

I'm writing a starter kit using leaf.el and elpaca as package manager. I want to implement a command that executes Emacs in batch mode and makes elpaca pull all packages enabled by the user, however this has proven unsuccessful as it only downloads recipes of Melpa and exists:

.emacs.d on  version-1 (a9aea78) [!?⇡] via ❄  impure (devenv-shell-env)
➜  emacs --batch --load early-init.el --load init.el --eval "(elpaca-wait)"
  INFO     Scraping 15 files for loaddefs...
  INFO     Scraping 15 files for loaddefs...done
  GEN      ../elpaca-autoloads.el
Loading ./elpaca-autoloads (source)...
Loading /home/jorge/.backpack.d/init.el (source)...
Loading /home/jorge/.emacs.d/gears/ui/theme.el (source)...
Downloading MELPA recipes...
Downloading MELPA recipes...100%

.emacs.d on  version-1 (a9aea78) [!?⇡] via ❄  impure (devenv-shell-env) took 8s
➜

I have found success by forgoing the usage of batch mode and instead using --kill -nw:

 $ emacs --kill -nw --eval "(elpaca-wait)"
# Emacs open, shows a panel of elpaca narrating what's happening and then exits

shackra avatar Oct 07 '25 02:10 shackra

I modified the installer to install another package of mine like so:

;; Elpaca Installer -*- lexical-binding: t; -*-
;; Copy below this line into your init.el
(defvar elpaca-installer-version 0.11)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
                              :ref nil :depth 1 :inherit ignore
                              :files (:defaults "elpaca-test.el" (:exclude "extensions"))
                              :build (:not elpaca--activate-package)))
(let* ((repo  (expand-file-name "elpaca/" elpaca-repos-directory))
       (build (expand-file-name "elpaca/" elpaca-builds-directory))
       (order (cdr elpaca-order))
       (default-directory repo))
  (add-to-list 'load-path (if (file-exists-p build) build repo))
  (unless (file-exists-p repo)
    (make-directory repo t)
    (when (<= emacs-major-version 28) (require 'subr-x))
    (condition-case-unless-debug err
        (if-let* ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                  ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
                                                  ,@(when-let* ((depth (plist-get order :depth)))
                                                      (list (format "--depth=%d" depth) "--no-single-branch"))
                                                  ,(plist-get order :repo) ,repo))))
                  ((zerop (call-process "git" nil buffer t "checkout"
                                        (or (plist-get order :ref) "--"))))
                  (emacs (concat invocation-directory invocation-name))
                  ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
                                        "--eval" "(byte-recompile-directory \".\" 0 'force)")))
                  ((require 'elpaca))
                  ((elpaca-generate-autoloads "elpaca" repo)))
            (progn (message "%s" (buffer-string)) (kill-buffer buffer))
          (error "%s" (with-current-buffer buffer (buffer-string))))
      ((error) (warn "%s" err) (delete-directory repo 'recursive))))
  (unless (require 'elpaca-autoloads nil t)
    (require 'elpaca)
    (elpaca-generate-autoloads "elpaca" repo)
    (let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
(elpaca doct)

I saved it to /tmp/testing/. From that directory I ran the following shell command:

$ emacs --script installer.el --eval '(elpaca-wait)'

Which seemed to exit immediately and did not result in a proper build. However, adding --init-directory=. did the trick:

$ emacs --init-directory=. --script installer.el --eval '(elpaca-wait)'

This is because the installer expands file names against user-emacs-directory. Does that help?

progfolio avatar Oct 16 '25 00:10 progfolio

Hi, Progfolio

I'll need to test on my end.

On mié, oct 15 2025, Nicholas Vollmer wrote:

progfolio left a comment (progfolio/elpaca#486)

I modified the installer to install another package of mine like so:

;; Elpaca Installer -- lexical-binding: t; -- ;; Copy below this line into your init.el (defvar elpaca-installer-version 0.11) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (<= emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "elpaca-bootstrap")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory "." 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca @.***)) (elpaca doct)

I saved it to /tmp/testing/.

$ emacs --script installer.el --eval '(elpaca-wait)'

Seemed to exit immediately and did not result in a proper build. However, adding --init-directory=. did the trick:

$ emacs --init-directory=. --script installer.el --eval '(elpaca-wait)'

This is because the installer expands file names against user-emacs-directory. Does that help?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

-- Jorge Araya

Contacto: Telegram: t.me/shackra · Signal: Shackra.28

shackra avatar Oct 16 '25 02:10 shackra

Okay, I gave it a try and... I saw Emacs exit immediately, just like before.

This is the shell script I use to run the "ensuring" part:

8<---------------cut here---------------start------------->8--- #!/usr/bin/env sh

set -eu

ACTION="${1:-}"

if [ -z "$ACTION" ]; then echo "❌ Error: missing argument" >&2 echo "Usage: $0 " >&2 exit 1 fi

Buscar directorio de configuración

if [ -d "$HOME/.emacs.d" ]; then INIT_DIR="$HOME/.emacs.d" elif [ -d "$XDG_CONFIG_HOME/emacs" ]; then INIT_DIR="$XDG_CONFIG_HOME/emacs" else echo "❌ Error: Emacs configuration directory not found ($HOME/.emacs.d or $XDG_CONFIG_HOME/emacs)" >&2 exit 1 fi

if [ "$ACTION" = "ensure" ]; then echo "✅ Running Emacs in batch mode..." emacs --init-directory="$INIT_DIR" --script "$INIT_DIR"/ensure.el exit $? else echo "❌ Error: unknown action '$ACTION'" >&2 echo "available actions: ensure" >&2 exit 1 fi 8<---------------cut here---------------end--------------->8---

This is the Emacs Lisp code referenced above with adjustments:

8<---------------cut here---------------start------------->8--- (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package)))

(load-file (expand-file-name "early-init.el" user-emacs-directory)) ;; needed because of --script

(progn (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (unless (file-exists-p repo) (make-directory repo t) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "elpaca-bootstrap")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory "." 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (let ((load-source-file-function nil)) (load "./elpaca-autoloads"))))

(elpaca @.***))

(add-hook 'elpaca-post-queue-hook (lambda () ;; (save-default-bg-fg-colors) (unless (gearp! :ui -treesit) (when (fboundp 'treesit-auto-install-all) (message "compiling tree-sitter grammars") (let ((treesit-auto-install t)) (treesit-auto-install-all)))) (kill-emacs 0)))

(backpack-load-gear-files) (elpaca-wait)) 8<---------------cut here---------------end--------------->8---

On mié, oct 15 2025, Nicholas Vollmer wrote:

progfolio left a comment (progfolio/elpaca#486)

I modified the installer to install another package of mine like so:

;; Elpaca Installer -- lexical-binding: t; -- ;; Copy below this line into your init.el (defvar elpaca-installer-version 0.11) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :inherit ignore :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (<= emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let* ((buffer (pop-to-buffer-same-window "elpaca-bootstrap")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let* ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory "." 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (let ((load-source-file-function nil)) (load "./elpaca-autoloads")))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca @.***)) (elpaca doct)

I saved it to /tmp/testing/.

$ emacs --script installer.el --eval '(elpaca-wait)'

Seemed to exit immediately and did not result in a proper build. However, adding --init-directory=. did the trick:

$ emacs --init-directory=. --script installer.el --eval '(elpaca-wait)'

This is because the installer expands file names against user-emacs-directory. Does that help?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

-- Jorge Araya

Contacto: Telegram: t.me/shackra · Signal: Shackra.28

shackra avatar Oct 16 '25 02:10 shackra

Okay, I gave it a try and... I saw Emacs exit immediately, just like before.

Are you trying the script I posted, or the one you posted in your most recent comment?

progfolio avatar Oct 17 '25 23:10 progfolio

mine with attention to place anything missing from yours in mine

On vie, oct 17 2025, Nicholas Vollmer wrote:

progfolio left a comment (progfolio/elpaca#486)

Okay, I gave it a try and... I saw Emacs exit immediately, just like before.

Are you trying the script I posted, or the one you posted in your most recent comment?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

-- Jorge Araya

Contacto: Telegram: t.me/shackra · Signal: Shackra.28

shackra avatar Oct 18 '25 12:10 shackra

Please try the steps I posted unaltered. It will help us to start from confirming whether or not the simple case works on your end.

progfolio avatar Oct 18 '25 12:10 progfolio

alright, alright. I'll give it a try and report back soon.

On sáb, oct 18 2025, Nicholas Vollmer wrote:

progfolio left a comment (progfolio/elpaca#486)

Please try the steps I posted unaltered. It will help us to start from confirming whether or not the simple case works on your end.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.

-- Jorge Araya

Contacto: Telegram: t.me/shackra · Signal: Shackra.28

shackra avatar Oct 18 '25 13:10 shackra

I'll consider this closed for now. If you are able to demonstrate that the simple case is failing, feel free to leave a comment and we can investigate further. Thank you!

progfolio avatar Nov 24 '25 05:11 progfolio