aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-03 19:10:44 -0500
committerCraig Jennings <c@cjennings.net>2026-05-03 19:10:44 -0500
commit9acaf3899aedb69300a29a0d0c8b468e5f4ad729 (patch)
treee6b4371107685f0a7225876b882145fbc363347d
parent475d6305e150c0a8ac61738eabe434c432acd991 (diff)
downloaddotemacs-9acaf3899aedb69300a29a0d0c8b468e5f4ad729.tar.gz
dotemacs-9acaf3899aedb69300a29a0d0c8b468e5f4ad729.zip
fix: expand local ELPA mirror paths with expand-file-name
`(concat user-home-dir ".elpa-mirrors/")` was producing `/home/cjennings.elpa-mirrors/` because `getenv HOME` doesn't return a trailing slash on Linux. The local mirrors were silently dropping out of `package-archives` because `file-accessible-directory-p` couldn't find the bogus path. I replaced the `concat` calls for `elpa-mirror-location` and `localrepo-location` with `expand-file-name`, which handles the slash for us. I also lifted the four per-archive subdirs into their own constants (`elpa-mirror-gnu-location`, `nongnu`, `melpa`, `stable-melpa`) so the archive registration block stops splicing `concat` strings inline. I added `tests/test-early-init-paths.el`. It loads `early-init.el` against a temp HOME with the package side effects stubbed and asserts each constant and each `package-archives` entry resolves to the right path.
-rw-r--r--early-init.el38
-rw-r--r--tests/test-early-init-paths.el101
2 files changed, 129 insertions, 10 deletions
diff --git a/early-init.el b/early-init.el
index 9f90f292..49a9da92 100644
--- a/early-init.el
+++ b/early-init.el
@@ -116,14 +116,32 @@ Set to nil to use only local repos.")
(defconst user-home-dir (getenv "HOME")
"The user's home directory per the environment variable.")
-(defconst elpa-mirror-location (concat user-home-dir ".elpa-mirrors/")
+(defconst elpa-mirror-location
+ (expand-file-name ".elpa-mirrors/" user-home-dir)
"The path to the elpa mirror location.")
-(defconst localrepo-location (concat user-emacs-directory ".localrepo/")
+(defconst localrepo-location
+ (expand-file-name ".localrepo/" user-emacs-directory)
"The path to your local Emacs package repository.
For more information about the local Emacs package repository, see comments in
early-init.el.")
+(defconst elpa-mirror-gnu-location
+ (expand-file-name "gnu/" elpa-mirror-location)
+ "The path to the local GNU ELPA mirror.")
+
+(defconst elpa-mirror-nongnu-location
+ (expand-file-name "nongnu/" elpa-mirror-location)
+ "The path to the local NonGNU ELPA mirror.")
+
+(defconst elpa-mirror-melpa-location
+ (expand-file-name "melpa/" elpa-mirror-location)
+ "The path to the local MELPA mirror.")
+
+(defconst elpa-mirror-stable-melpa-location
+ (expand-file-name "stable-melpa/" elpa-mirror-location)
+ "The path to the local MELPA Stable mirror.")
+
(setq package-archives nil) ;; package-archives will be added below
;; LOCAL REPOSITORY (packages in version control)
@@ -132,20 +150,20 @@ early-init.el.")
(add-to-list 'package-archive-priorities '("localrepo" . 200)))
;; LOCAL REPOSITORY ELPA MIRRORS
-(when (file-accessible-directory-p (concat elpa-mirror-location "gnu"))
- (add-to-list 'package-archives (cons "gnu-local" (concat elpa-mirror-location "gnu/")) t)
+(when (file-accessible-directory-p elpa-mirror-gnu-location)
+ (add-to-list 'package-archives (cons "gnu-local" elpa-mirror-gnu-location) t)
(add-to-list 'package-archive-priorities '("gnu-local" . 125)))
-(when (file-accessible-directory-p (concat elpa-mirror-location "nongnu"))
- (add-to-list 'package-archives (cons "nongnu-local" (concat elpa-mirror-location "nongnu/")) t)
+(when (file-accessible-directory-p elpa-mirror-nongnu-location)
+ (add-to-list 'package-archives (cons "nongnu-local" elpa-mirror-nongnu-location) t)
(add-to-list 'package-archive-priorities '("nongnu-local" . 120)))
-(when (file-accessible-directory-p (concat elpa-mirror-location "melpa"))
- (add-to-list 'package-archives (cons "melpa-local" (concat elpa-mirror-location "melpa/")) t)
+(when (file-accessible-directory-p elpa-mirror-melpa-location)
+ (add-to-list 'package-archives (cons "melpa-local" elpa-mirror-melpa-location) t)
(add-to-list 'package-archive-priorities '("melpa-local" . 115)))
-(when (file-accessible-directory-p (concat elpa-mirror-location "stable-melpa"))
- (add-to-list 'package-archives (cons "melpa-stable-local" (concat elpa-mirror-location "stable-melpa/")) t)
+(when (file-accessible-directory-p elpa-mirror-stable-melpa-location)
+ (add-to-list 'package-archives (cons "melpa-stable-local" elpa-mirror-stable-melpa-location) t)
(add-to-list 'package-archive-priorities '("melpa-stable-local" . 100)))
;; ONLINE REPOSITORIES
diff --git a/tests/test-early-init-paths.el b/tests/test-early-init-paths.el
new file mode 100644
index 00000000..2051da73
--- /dev/null
+++ b/tests/test-early-init-paths.el
@@ -0,0 +1,101 @@
+;;; test-early-init-paths.el --- Tests for early-init path construction -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Load early-init.el with package side effects stubbed so path construction can
+;; be tested without touching real package archives or the network.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ert)
+(require 'package)
+
+(defvar cj/use-online-repos nil
+ "Test binding for early-init online repository setup.")
+
+(defconst test-early-init-paths--repo-root
+ (file-name-directory
+ (directory-file-name
+ (file-name-directory (or load-file-name buffer-file-name))))
+ "Repository root for early-init path tests.")
+
+(defun test-early-init-paths--load-with-temp-home (home emacs-home)
+ "Load early-init.el with HOME and EMACS-HOME as isolated roots."
+ (let ((process-environment (copy-sequence process-environment))
+ (user-emacs-directory (file-name-as-directory emacs-home))
+ (package-user-dir (expand-file-name "elpa/" emacs-home))
+ (package-archives nil)
+ (package-archive-priorities nil)
+ (debug-on-error nil)
+ (debug-on-quit nil))
+ (setq cj/use-online-repos nil)
+ (setenv "HOME" home)
+ (cl-letf (((symbol-function 'package-initialize) #'ignore)
+ ((symbol-function 'package-installed-p) (lambda (_package) t))
+ ((symbol-function 'package-refresh-contents) #'ignore)
+ ((symbol-function 'package-install) #'ignore))
+ (load (expand-file-name "early-init.el" test-early-init-paths--repo-root) nil t))
+ (list :user-home-dir user-home-dir
+ :elpa-mirror-location elpa-mirror-location
+ :localrepo-location localrepo-location
+ :gnu elpa-mirror-gnu-location
+ :nongnu elpa-mirror-nongnu-location
+ :melpa elpa-mirror-melpa-location
+ :stable-melpa elpa-mirror-stable-melpa-location
+ :package-archives package-archives
+ :package-archive-priorities package-archive-priorities)))
+
+(ert-deftest test-early-init-paths-normal-expanded-under-home ()
+ "Normal: local mirror paths expand under HOME, not beside it."
+ (let* ((home (make-temp-file "early-init-home-" t))
+ (emacs-home (make-temp-file "early-init-emacs-" t))
+ (_ (dolist (dir '(".localrepo"
+ ".elpa-mirrors/gnu"
+ ".elpa-mirrors/nongnu"
+ ".elpa-mirrors/melpa"
+ ".elpa-mirrors/stable-melpa"))
+ (make-directory (expand-file-name dir emacs-home) t)))
+ ;; Mirror directories intentionally live under HOME, while .localrepo
+ ;; lives under user-emacs-directory.
+ (_ (dolist (dir '(".elpa-mirrors/gnu"
+ ".elpa-mirrors/nongnu"
+ ".elpa-mirrors/melpa"
+ ".elpa-mirrors/stable-melpa"))
+ (make-directory (expand-file-name dir home) t)))
+ (result (test-early-init-paths--load-with-temp-home home emacs-home)))
+ (should (string= (plist-get result :user-home-dir) home))
+ (should (string= (plist-get result :elpa-mirror-location)
+ (expand-file-name ".elpa-mirrors/" home)))
+ (should (string= (plist-get result :localrepo-location)
+ (expand-file-name ".localrepo/" emacs-home)))
+ (should (string= (plist-get result :gnu)
+ (expand-file-name ".elpa-mirrors/gnu/" home)))
+ (should-not (string= (plist-get result :elpa-mirror-location)
+ (concat home ".elpa-mirrors/")))))
+
+(ert-deftest test-early-init-paths-normal-local-archives-use-expanded-paths ()
+ "Normal: local package archives use expanded path constants."
+ (let* ((home (make-temp-file "early-init-home-" t))
+ (emacs-home (make-temp-file "early-init-emacs-" t)))
+ (make-directory (expand-file-name ".localrepo/" emacs-home) t)
+ (dolist (dir '(".elpa-mirrors/gnu"
+ ".elpa-mirrors/nongnu"
+ ".elpa-mirrors/melpa"
+ ".elpa-mirrors/stable-melpa"))
+ (make-directory (expand-file-name dir home) t))
+ (let* ((result (test-early-init-paths--load-with-temp-home home emacs-home))
+ (archives (plist-get result :package-archives)))
+ (should (equal (cdr (assoc "localrepo" archives))
+ (expand-file-name ".localrepo/" emacs-home)))
+ (should (equal (cdr (assoc "gnu-local" archives))
+ (expand-file-name ".elpa-mirrors/gnu/" home)))
+ (should (equal (cdr (assoc "nongnu-local" archives))
+ (expand-file-name ".elpa-mirrors/nongnu/" home)))
+ (should (equal (cdr (assoc "melpa-local" archives))
+ (expand-file-name ".elpa-mirrors/melpa/" home)))
+ (should (equal (cdr (assoc "melpa-stable-local" archives))
+ (expand-file-name ".elpa-mirrors/stable-melpa/" home))))))
+
+(provide 'test-early-init-paths)
+;;; test-early-init-paths.el ends here