I’ve blogged about a little elisp snippet I use to install my preferred base set of Emacs packages before. Thanks for all the feedback, it definitely helped improve the code.

One issue that kept annoying me is that there is no simple way to tell ELPA to mainly pull packages from melpa-stable and only fall back to melpa for those packages I can’t get on melpa-stable yet. I decided to extend my code to handle that situation with some manual inputs as I know which packages can’t be found on melpa-stable. It proved surprisingly easy to do so after mulling over the problem a little.

First, I updated my function install-required-packages so that it accepts an optional parameter containing a list of packages repositories. When the parameter is non-nil, I make a temporary copy of the existing packages-archives list to preserve my default settings and replace it with the list that’s been passed in. Then the function checks and install the packages as before and then restores the original package-archives variable. The code now looks like this:

 
(defun install-required-packages (package-list &optional package-archive-list)
  (when (>= emacs-major-version 24)
    (if package-archive-list
        (setq temp-package-archives package-archives
              package-archives package-archive-list))
    (package-refresh-contents)

    (mapc (lambda (package)
            (unless (require package nil t)
              (package-install package)))
          package-list)
    (if package-archive-list
        (setq package-archives temp-package-archives))))
 

As you can see, the function is now a just little more complicated thanks to the additional state preservation code. The big bonus  is that it now lets me specify which packages I don’t want to pull from my list of default repositories. To make things easier I also pre-populated the lists of my preferred ELPA repos:

 (setq stable-package-archives '(("ELPA" . "http://tromey.com/elpa/")
                                ("gnu" . "http://elpa.gnu.org/packages/")
                                ("melpa-stable" . "http://stable.melpa.org/packages/")
                                ("marmalade" . "http://marmalade-repo.org/packages/"))
      unstable-package-archives '(("melpa" . "http://melpa.org/packages/")))
 

Now I can simply tell ELPA which packages should be pulled from the default repositories and which ones come from the special repositories:

 
(install-required-packages '(smex zenburn-theme zen-and-art-theme htmlize cider clojure-mode rainbow-delimiters))
(install-required-packages '(bm icicles) unstable-package-archives)
 

Quite simple, isn’t it?

Obviously this is still work in progress. The whole approach feels clunky to me and that suggests there is room for improvement. Yes, it works and there is a lot to be said for that, but ideally I would like to build it out in such a way that I specify with package to pull from which repository and add functionality to semi-automatically update the packages as well. I don’t like the idea of fully automated upgrades – especially not from melpa – as I’ve ended up with broken packages before when I took that approach, but a manually triggered auto-update.

Isn’t it great that we spend hours customizing our tools to save five minutes?

10 thoughts on “Set up Emacs to use both melpa and melpa-stable”

  1. If you’ve taken the plunge into Emacs 24.4, package.el now has this feature built-in via the package-pinned-packages variable. It’s about as easy to use as your version, though it’s not perfect – you have to specify each package and the repository you want to use for it one-by-one.

    Here’s an example of my config to use the stable versions of the Clojure tools: https://github.com/MatthewDarling/sandhu-emacs/blob/master/lisp/init-packages.el#L9-L14

    (though the linked bit of code doesn’t actually work right now because the variable name changed from package-pinned-archives to package-pinned-packages)

    1. Matthew and Brad,

      thanks for pointing this out. I’m not on 24.4 on all machines yet but that should provide me with another incentive to finally update all machines. Fortunately the list of “basic” packages isn’t that big in my setup so having to pin each package separately isn’t that big a deal at this point.

  2. In Emacs 24.4 and higher there’s a new configuration option:

    (when (boundp ‘package-pinned-packages)
    (setq-default package-pinned-packages
    ‘((smex . “melpa-stable”))))

    This allows you to pin a particular package (the car of the list) to the named archive (the cdr).

  3. Have been pondering the topic of your post for the past two weeks (and more), before you posted it. Cask is now my preferred approach. Thinking is still required, but Cask seems to do what you would expect and want.

    Lately I’ve played around with a combination of package, el-get, and cask, and they all provide a nice approach for my use cases.

    1. Grant,

      I think yours is an interesting approach and one I should look into further.

      The reason I am currently going with plain ELPA is that I use this setup to bootstrap my Emacs – I can carry/email around my .emacs file and quickly establish the basic environment I want on a new machine. So I guess I should be putting cask into this part of the configuration.

      1. Some time ago I had wanted to easily support different package repositories, and Cask was the only pleasant manner in which to do so. Ideally, I would love to drop a config onto any machine and have Emacs do all of the work. It seems that given the information shared in the comments here, that Emacs can how handle such a set up just fine. Cask does a lot more, and is worth learning. Right now it seems that digging into this pinning feature is also part of the path forward, at least for me.

  4. Hi,
    Just saw this and wanted to let you know that it would be simpler and more standard to use a let-construct (local assignment):

    (let ((package-archives (or package-archives-list package-archives)))
    … code body …
    )

    Cheers!

Leave a Reply