camdez.com

Rule #1: There are no rules.

Switching to MELPA Stable: Why, How, What I Learned

| Comments

Background

Writing my first Emacs package a couple months ago left me more cognizant of how Emacs’ packaging system is put together and raised questions about how I use its capabilities. I had been installing all of my packages from MELPA, but now, as a fancy-schmancy package author I’d become intensely aware that MELPA builds its packages based on the latest commit in a project’s repository1. Suddenly I’d become paranoid about exactly what I pushed to the master branch of my project and worried about leaving things in a broken state.

Generally speaking, I keep the master branch on my projects in a functional state, and—yes—I could adopt a development methodology whereby work is always done on a development or feature branch and QA’d before being merged into master. But even if I have the inclination and discipline to manage my projects this way, all of my other Emacs packages are getting built from whatever happened to be pushed to master in their own project when the MELPA bot decided to make the rounds. I’ve run my fair share of beta software, but I don’t need every commit as it happens (cutting vs. bleeding edge).

As it turns out, there’s a new kid on the block—MELPA Stable—and she’s come to solve this exact problem.

MELPA Stable contains only stable versions of packages2. This means we’re getting new versions of packages when their maintainers feel that the version is ready to ship, rather than any time they make a change to their code.

Stable is also quite cleverly implemented, requiring only a single git tag in semver format to define a release. This not only makes life easy for package maintainers, but promoting semver is good for everyone (even end users!), because a consistent scheme for denoting version compatibility means that developers can write more reliable software.

How to Switch to MELPA Stable

If you’re interested in taking the plunge—and don’t worry, it’s easy to switch back—the first thing to know is that not every package on MELPA is available via MELPA Stable3. If the package creator has not yet defined a stable release, the package will not appear on Stable. Before switching, you’ll probably want to think through the packages you consider critical to your Emacs workflow and type their names into the filter box on the MELPA Stable site to discover if they are available. It is possible to pull packages from two repositories but it’s not trivial and I won’t be covering it here.

Still with me? Let’s get started! The first step, naturally, is to open your .emacs (or .emacs.d/init.el) and change your package-archives form to look like this:

1
2
3
(add-to-list 'package-archives
             '("melpa-stable" . "http://stable.melpa.org/packages/")
             t)

Now we’re set to fetch packages from MELPA Stable, but there are a few more steps to make sure your current set-up keeps on trucking:

If you don’t already have one, I’d recommend creating a list in your .emacs file of packages that you use / always want installed. We can kick-start this process by first adding the following to the file:

1
2
(defvar my/packages
  ')

With point after the ' character, type C-0 M-: package-activated-list RET to insert a list of the currently activated packages:

1
2
(defvar my/packages
  '(artbollocks-mode ace-jump-mode auto-complete checkbox)) ; your list will vary

You may see a few items in your list that you’re not familiar with—feel free to remove anything that you don’t recognize as something you use. These packages are likely to be dependencies of other packages that you use, and they will be automatically included as needed later on. Such packages might include things like queue, dash, pcache, popup, pkg-info, etc.

One “gotcha” of the upgrade process is that the faux version numbers MELPA generates (e.g. 20141116.1658) give the appearance of being higher versions than the proper version numbers from MELPA Stable (e.g. 0.2.0), thus if you have existing packages installed, the package system will never prefer the Stable version, believing that it’s outdated. To solve this you’ll want to delete all of your existing packages. This is easy:

1
$ rm -rf ~/.emacs.d/elpa

Next we will use our new my/packages list to tell Emacs to install all of these packages when it starts (here I’m using the exact code I outlined in a previous blog post), so add this to your Emacs config after the my/packages definition:

1
2
3
4
5
6
7
8
9
10
11
12
(require 'cl-lib)

(defun my/install-packages ()
  "Ensure the packages I use are installed. See `my/packages'."
  (interactive)
  (let ((missing-packages (cl-remove-if #'package-installed-p my/packages)))
    (when missing-packages
      (message "Installing %d missing package(s)" (length missing-packages))
      (package-refresh-contents)
      (mapc #'package-install missing-packages))))

(my/install-packages)

With a little luck, you’re all set! Restart Emacs. It may take a minute for it to install all of your packages, but if it all appears to finish without error, you’re good to go!

If you encounter errors, continue with the next section.

Robustifying your Config

If you encounter errors after restarting Emacs, it’s likely that your configuration assumed the presence of some package that wasn’t re-installed4. Usually you can locate the general source of the error from the error message. For example:

1
2
3
4
5
6
7
Warning (initialization): An error occurred while loading `/Users/camdez/.emacs.d/init.el':

Symbol's function definition is void: keyfreq-mode

To ensure normal operation, you should investigate and remove the
cause of the error in your initialization file.  Start Emacs with
the `--debug-init' option to view a complete error backtrace.

In this case we can tell that we referenced keyfreq-mode before the package which defines it had been loaded. To get out of this situation we have three options:

  1. Remove the offending configuration lines. We can simply find the reference to keyfreq-mode and remove it (or comment it out), but we probably added that for a reason so this should be our last resort.
  2. Install the missing package. Ultimately this is what we want to do. We can typically infer the name of the package by guessing or by searching on stable.melpa.org. For example, keyfreq-mode is defined by the package keyfreq. Once we know the package name we can add it to our my/packages list and restart Emacs.
  3. Make our config robust against the missing package. Even if we ultimately go with option #2, it’s a great idea to make our configuration load whether or not any particular package exists. This means that we can get our (bare-bones) config up and running in any environment, regardless of package availability, including a complete network outage. A complete discussion of this is beyond the scope of this article, but a good starting approach is to wrap the relevant configuration lines in a conditional call which loads the desired package and only executes the configuration line if it succeeds. To do this properly we’ll need to know the symbol used on the (provide '...) line in the package’s source code. Once we have that we can do something like the following:
1
2
3
(when (require 'keyfreq nil 'no-error)
  (keyfreq-mode 1)           ; configuration of keyfreq
  (keyfreq-autosave-mode 1)) ; more configuration of keyfreq

Once we’ve taken care of one piece of the configuration like this, we can restart Emacs and see if we have more success. If we have another error, we repeat the process. Admittedly this is a bit of a hassle, but along the way we improve our Emacs config such that it will continue work in a variety of different environments (missing packages, different versions, etc.).

One final note: if you manage to damage your config file to such a degree that you can no longer use Emacs to edit the file, you can always invoke Emacs with an argument instructing it not to load your config file:

1
$ emacs -q ~/.emacs

This will leave you without any of your customizations, but it will allow you to edit your config file back to a functional state.

My Experience

When I switched to MELPA Stable last November, I found that most of the packages I use were already available. The notable exceptions were guide-key, yaml-mode, and idomenu. I opened an issue for guide-key, added a +1 on the relevant yaml-mode issue, and emailed the author of idomenu, asking to get the packages uploaded to GitHub and tagged with releases for MELPA Stable. These package maintainers have been unanimously awesome, and all of the packages have since been added to Stable.

If you find yourself requesting that package authors / maintainers prepare their package for MELPA Stable, please feel free to use my YASnippet which explains what MELPA Stable is, why it matters, and exactly how to add their package.

Last but not least, between the time I first switched to Stable and now, I’ve personally added / updated four MELPA package recipes for Stable (checkbox, goto-last-change, idomenu, org-table-comment). It’s an easy way to scratch your own itch and make a small contribution to the Emacs community.

That’s it! Enjoy your newfound stability!


  1. Actually the story is a bit more complex because MELPA recipes can pull code from a few different places (e.g. EmacsWiki), but pulling from Git master is probably the most common (and best) story.

  2. With real version numbers, not the timestamp-style identifiers regular MELPA is forced to generate!

  3. At the time of writing, 40% of MELPA packages are available via MELPA Stable (955 / 2397).

  4. A less likely possibility is that your config assumed the presence of a newer version of the package than that which was installed. In this case you don’t have a lot of options—-you’ll need to adapt your configuration to the older version of the package if you wish to stick with Melpa Stable.

Comments