el-get in a portable emacs configuration solution - emacs

el-get goes a long way in helping achieve a portable emacs configuration setup. The idea is to declare the packages you want in the emacs config file, push that file to a repo, and pull it on all the computers where you want an identical emacs configuration. This is how the code might look in elisp:
(setq my-packages (append '(el-get switch-window yasnippet ...)
(mapcar 'el-get-source-name el-get-sources)))
(el-get 'sync my-packages)
el-get will make sure that the packages get automatically installed and properly initialized. However, my understanding is that when you dereference a package, it doesn't get uninstalled. And if you uninstall it manually, you'll have to do it across all the computers, also manually. In other words, el-get goes only half the way in achieving a truly portable solution. My question is if anybody has written elisp code that will uninstall the packages just by dereferencing them in init.el? Or whether I should look elsewhere for a fully portable declarative dependency management solution for emacs?

I'm answering myself here because in the end I opted for an alternate solution.
phils' answer is still valid, but I found it troublesome to have the .emacs.d directory under version control, and to be fair I didn't want to bother with fake submodules.
What I did instead: I contacted el-get's maintainer, Dimitri, and presented him with the problem.
Dimitri said:
I could see us adding an el-get-cleanup function that you would have to call with the current list of packages and that would el-get-remove any package already installed locally but not on the provided list.
(el-get-cleanup my-packages)
You could then use that from your user-init-file if you want to, or do
that as a routine every now and then.
With his guidance, I then wrote the function in question.
(defun el-get-cleanup (packages)
"Remove packages not explicitly declared"
(let* ((packages-to-keep (el-get-dependencies (mapcar 'el-get-as-symbol packages)))
(packages-to-remove (set-difference (mapcar 'el-get-as-symbol
(el-get-list-package-names-with-status
"installed")) packages-to-keep)))
(mapc 'el-get-remove packages-to-remove)))
Ah, the joys of open source...
(See also my blog post)

You should use el-get in conjunction with some form of version control. That provides the portability, so that when you remove a package and commit the result to your repository, the package will also be uninstalled for the other instances once they have pulled those changes.
If you are leaving the package files to el-get to manage, then those files may still exist on the other copies after the package is removed from one instance but, provided that el-get's status and autoload files are in your repo, I think the state of each package should be correct.
Personally, I recommend committing all files to your repository after installing a package. That way when you remove a package, commit the changes, and pull those changes from another instance, both copies are in the same state.
Moreover, I would never trust the availability, consistency, or permanence of a remote source when it comes to setting up a new instance of my Emacs configuration -- the act of cloning my repository is all that should be required to obtain a working system.
So: use el-get for installing and updating packages, and use version control to make it portable.

Related

use-package does not download packages

I don't quite get the use-package package. I thought of it as a replacement of the older require statement in config files but on github it reads
This loads in the package XYZ, but only if XYZ is available on your system
So do I have to ensure that I have the package before?
Because sometimes it seems, all I need is the use-package statement for a fresh Emacs install to get the dependency.
Please clarify.
I think the point is to let you share your init.el across systems and simply have it ignore packages which are not available on any particular system.
It also appears to allow you to specify, but defer the evaluation of, your own customizations for any package you use until the package is actually loaded, so unlike require, it doesn't force you to load a package you are not going to use in this session.
There is a facility for requiring a package to be installed if it's not installed; look for the :ensure keyword and the use-package-always-ensure configuration variable in the documentation.

Whats the best way to package my Emacs installation (packages and config) so that it can quickly be installed anywhere?

I generally write code by logging on to a remote machine (typically AWS). I have a fairly big list of packages that I use and a fairly large .emacs.el. While I can quickly install emacs on nay remote machine, I am looking for a way to "package" my emacs and house it somewhere so that I can quickly install it on any machine I login to. Whats the best way to do this?
First you would obviously bundle everything into source control (except for a private file). Bitbucket and Gitlab offer private repos.
You can see this wiki for a way to list all the needed packages in your init file. (this is actually used by Prelude)
Then I see some options.
Use Cask
Some use Cask to manage package dependencies, some do not
The Cask file lists all dependencies:
(depends-on "cask")
(depends-on "dash")
(depends-on "evil")
Use org-mode
Some write their config in org-mode and load it with a call to org-babel, which is doable in one line in ~/.emacs.d/init.el:
(require 'org)
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "~/.emacs.d/myemacs.org"))
Split your config in multiple files
and some split it in multiple elisp files.
Here some nice configs worth taking inspiration from:
Noahfrederick uses org-mode and Cask: https://github.com/noahfrederick/dots/tree/master/emacs.d
Purcell uses multiple elisp files where each require the needed package: https://github.com/purcell/emacs.d/tree/master/lisp
In init-elpa.el, he defines a function that takes a package in argument and installs it if it is not present:
(defun require-package (package &optional min-version no-refresh)
"Install given PACKAGE, optionally requiring MIN-VERSION.
If NO-REFRESH is non-nil, the available package lists will not be
re-downloaded in order to locate PACKAGE."
(if (package-installed-p package min-version)
t
(if (or (assoc package package-archive-contents) no-refresh)
(package-install package)
(progn
(package-refresh-contents)
(require-package package min-version t)))))
and in every file, he uses:
(require-package 'dired+)
Also commit the installed packages
And for your config to install quicker, you may add the installed packages into source control too. That way you can also ensure to have identical environments.
I always recommend putting the entire ~/.emacs.d under version control, including all packages and other libraries.
It's a bit more hassle, and maybe a bit messier, but if you want to guarantee the state of the configuration every time you install it somewhere new, you need to have a complete copy.
Using version control is my firm preference, because that makes it trivial to revert changes to packages, etc. So if you upgrade a package and Emacs breaks, it's a single step to put things back they way they were (and doing so doesn't require you to remember how they were).
With this approach the act of cloning a single repository is all that is required to obtain a working system in a known state; and it limits your dependence upon the availability, consistency, and permanence of remote sources to just that one repository (and if you have it locally, or even carry a copy with you, you have no remote dependencies at all).
That said, lots of people don't bother and don't experience any issues, so it's not a critical point for most people.
It would be helpful if you clarified a little more what you mean by "any" computer. It sounds like emacs already installed on the machine and you just want to configure emacs to use your packages and settings. Do you have physical access to the machine or network from which you could load files from a memory stick? If not, can you access cloud storage?
My own setup looks like this:
I have a Windows 7 machine at work and Linux Mint at home.
I have a dropbox account that holds all my emacs configuration files and packages. This dropbox account is synchronized to a local folder on each machine and my .emacs file on each computer is only one line:
(load-file "~/Dropbox/dotemacs.el")
I often tweak package files and configurations. Using dropbox keeps my settings synchronized across all computers.
If you can't install Dropbox, you could manually sync to cloud storage like git.
check https://github.com/redguardtoo/elpa-mirror,
create your own repository on a usb memory stick, it's stable because all the package versions are the version you are already using for a very long time.
That's the quickest way, basically you can get your setup on any machine in 1 min.
As the README for my repo emacs for rails devs says it is as easy as pushing your ~/.emacs.d contents to github and on the target machine:
Install emacs using homebrew (chances are you are on OS X) brew install emacs --HEAD --use-git-head --cocoa --srgb
Git clone your repo to ~/.emacs.d/
Boot emacs
If there is magic, it's using the built in package manager with ELPA and marmalade and having that check to see if packages are installed and if not, install them.
Works for me and my boxes & servers.

emacs el-get does not load packages

Using Emacs 24.3 on OS X, I have setup el-get as described. I am able to install, remove, and init packages using the el-get-* commands.
However, none of the packages ever actually load. The simple example I use is for the package 'ascii-table'. I do the following:
M-x el-get-install ascii-table
M-x el-get-init ascii-table
But when I do
M-x ascii-table
Emacs says it is not found. If I explicitly eval the ascii-table.el file that was downloaded by el-get, it works as expected.
Is there something I must do, after installing a package, to actually use the package? Or do I still need to put the necessary load-file or whatever in my init.el file to load the packages?
It sounds like, from the el-get documentation, that there was nothing else that needed to be done.
You can instruct el-get to require features from a package by adding the property features to the package's recipe file. From the documentation (do C-hvel-get-sourcesRET)
:features
List of features el-get will `require' for you.
For your particular case do M-xel-get-find-recipe-fileRETascii-tableRET, this open ascii-table's recipe file for you, then add the following to the recipe
:features (ascii-table)
The full recipe will be
(:name ascii-table
:auto-generated t
:type emacswiki
:description "simple ASCII table"
:website "https://raw.github.com/emacsmirror/emacswiki.org/master/ascii-table.el"
:features (ascii-table))
My advice would be not edit the original recipe but keep this in your personal recipes folder to avoid any conflicts when updating el-get.
That said you can always manually load a package, see #Drew's comment
Is el-get supposed to load libraries? I doubt it.
To load a library, put (require 'FEATURE-NAME) in your init file, where FEATURE is the feature provided by the library. If it does not provide any feature, then use the file name instead (between " chars).

Load plugin from specific location

In my computer I have an Emacs plugin installed with the Debian package system, but this plugin is obsolete and I'm trying to install it inside my home directory with package-install. I have installed a newer version, but if I check which version is loaded, the older one is.
I tried with load-file and the global path to the new version, but it still loads the old version.
How can I force the load of the new one? Please, imagine that I'm not the sysadmin of the computer and I cannot uninstall any software package.
When a library contains multiple files one can assume that a single main file will load the others as necessary; but if the directory isn't in the load-path then it won't be able to load them (or at least not those versions), so load-file on its own isn't going to do the trick.
I don't make much use of package.el myself, but I'd really have thought that it would manage the load-path such that it took precedence over anything in site-lisp (which is presumably where the debian package is installing things.)
Try running emacs --no-site-lisp and check that the correct version of the library is loaded. If you don't want anything from the site libraries, then that might even be your solution.
If that works, then check the load-path variable after starting Emacs normally. Unless an absolute path is given, Emacs will look at those directories in sequence, and use the first one which matches. I am guessing that for some reason your site-lisp directory is appearing before the one created by package.el.
Or perhaps the package didn't install correctly at all.
That all said, in Emacs 24.3 at least (package-initialize) is called automatically and I don't think you should need to manually load anything. Check the package-load-list variable.
Edit:
On that last note, refer to cannot open load dired-details.
At the time I hadn't noticed that Emacs doesn't initialize packages until after the user init file has loaded, so you generally will need to initialize them manually in your init file.
Try this
(defun please-load-my-stuff ()
(interactive)
(load "PATH-TO-STUFF/STUFF))
When having access to init, put just form "(load ...)" there.
If more than one file is needed, load-path must be set also that way - before load, so the other required files are accessible.

Delete built-in packages in Emacs

Is it possible to remove built-in Emacs packages like "tetris"? They can't be marked to be deleted in package list as of 24.1. It would be nice to have a minimal installation of Emacs - even though barely useful - by deleting some or all built-in packages. Is it possible to do somehow, and will this ability added in future?
Emacs should start and be useable even if the whole lisp directory is empty (note that we rarely/never test it, so I don't guarantee that it'll work, but at least in principle it should and if it doesn't you should report it with M-x report-emacs-bug). So feel free to remove any and all packages in there you don't find useful, in order to create a trimmed-down version of Emacs.
You could just remove the elc files of all of the packages you want.
For example, in the version of emacs located in the ubuntu repository the tetris package is located in:
/usr/share/emacs/23.3/lisp/play/tetris.elc
If you move or remove it, emacs will continue to work, but you won't be able to play tetris anymore.
You might want to inspect the package--builtins variable. That said - there is little sense in removing any packages installed via package.el since package.el extracts and loads automatically only a package's autoloads - therefore having many installed packages doesn't result in any significant overhead. I'm quite certain that removing built-in packages will never be a feature of package.el.