Coder Social home page Coder Social logo

envrc's Introduction

Melpa Status Melpa Stable Status Build Status Support me

envrc.el

A GNU Emacs library which uses direnv to set environment variables on a per-buffer basis. This means that when you work across multiple projects which have .envrc files, all processes launched from the buffers "in" those projects will be executed with the environment variables specified in those files. This allows different versions of linters and other tools to be used in each project if desired.

Design notes

There is already an excellent direnv.el package available, which works in a certain way that has particular trade-offs. I wanted to explore a different approach. The existing direnv package has many happy users, and I make no claim that this one is better: it is just different. Additionally, the envrc.el package is at an earlier stage of development, and likely has bugs.

By default, Emacs has a single global set of environment variables used for all subprocesses, stored in the process-environment variable. direnv.el switches that global environment using values from direnv when the user performs certain actions, such as switching between buffers in different projects.

In practice, this is simple and mostly works very well. But some trade-offs of this simple approach are:

  • When switching to a buffer that is not "inside" a project with an .envrc file, the buffer will see the last project's environment. I would prefer it to see the default Emacs environment.

  • When direnv fails to execute in the course of switching to a buffer in a new project with an .envrc file (e.g. because that .envrc file is disallowed), buffers in the new project will see the environment variables from the previous project.

  • Background buffers from a previous project will start seeing the new project's environment, so any processes they launch asynchronously after the switch will use the wrong environment. (This is probably quite rare in practice.)

Now, it is also possible to set process-environment locally in a buffer. If this value could be correctly maintained in all buffers based on their various respective .envrc files, then buffers across multiple projects could simultaneously be "connected" to the environments of their corresponding project directories. I wrote envrc.el to explore this approach.

envrc.el uses a global minor mode (envrc-global-mode) to hook into practically every buffer created by Emacs, including hidden and temporary ones. When a buffer is found to be "inside" an .envrc-managed project, process-environment is set buffer-locally by running direnv, the results of which are also cached indefinitely so that this is not too costly overall. Each buffer has a local minor mode (envrc-mode) with an indicator which displays whether or not a direnv is in effect in that buffer. (Hooking into every buffer is important, rather than just those with certain major modes, since separate temporary, compilation and repl buffers are routinely used for executing processes.)

This approach also has some trade-offs:

  • Buffers like *Help* will have envrc-mode enabled based on the directory of the buffer which caused them to be created initially, and then those buffers often live for a long time. If you launch programs from such buffers while working on a different project, the results might not be what you expect. I might exclude certain modes to minimise confusion, but users will always have to be aware of the fact that environments are buffer-specific.

  • There's a (very small) overhead every time a buffer is created, and that happens quite a lot.

  • direnv updates are not automatic. direnv.el re-executes direnv when switching between buffers that visit files in different directories, whereas envrc-mode caches the environment until the user refreshes it explicitly with envrc-reload.

I hope that these will be worthwhile, since my ultimate goal is to integrate with lorri, a daemon which re-builds Nix projects automatically and presents their environments to direnv: if we can hook into lorri's rebuilds, then envrc.el will allow Emacs to programmatically refresh the environment of specific subsets of buffers, which is not currently possible with direnv.el.

It's also possible that there's a way to call direnv more aggressively by allowing it to see values of DIRENV_* obtained previously such that it becomes a no-op.

Installation

Installable packages are available via MELPA: do M-x package-install RET envrc RET.

Alternatively, download the latest release or clone the repository, and install envrc.el with M-x package-install-file.

Usage

Add the following to your init.el (after calling package-initialize):

(envrc-global-mode)

It's probably wise to do this late in your startup sequence: you normally want envrc-mode to be initialized in each buffer before other minor modes like flycheck-mode which might look for executables. Counter-intuitively, this means that envrc-global-mode should be enabled after other global minor modes, since each prepends itself to various hooks.

You should only enable the mode if direnv is installed and available in the default Emacs exec-path. (There is a local minor mode envrc-mode, but you should not try to enable this granularly, e.g. for certain modes or projects, because compilation and other buffers might not get set up with the right environment.)

Regarding interaction with the mode, see envrc-mode-map, and the commands envrc-reload, envrc-allow and envrc-deny. (There's also envrc-reload-all as a "nuclear" reset, for now!)

In particular, you can enable keybindings for the above commands by binding your preferred prefix to envrc-command-map in envrc-mode-map, e.g.

(with-eval-after-load 'envrc
  (define-key envrc-mode-map (kbd "C-c e") 'envrc-command-map))

๐Ÿ’ Support this project and my other Open Source work via Patreon

๐Ÿ’ผ LinkedIn profile

โœ sanityinc.com

๐Ÿฆ @sanityinc

envrc's People

Contributors

damiencassou avatar purcell avatar swflint avatar wakira avatar wbolster avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.