The other day, I stumbled upon fish, the “Friendly Interactive SHell” and lolcommits. I fell in love with both of them. So I wanted to properly configure them. For lolcommits I needed two environment variables. Neither did fish’s universal variables work, nor did the common bash-ways for setting persistent environment variables work without starting fish from bash. As fish became my standard-shell in Apple’s Terminal-app, I wanted to know how to properly set global, persistent environment variables in macOS.

It seems there have been many changes in how to properly do that overtime. The current (macOS 10.12) way to do it, seems to be setting up launch agents for launchd, to execute launchctl setenv <var-name> <var-value>. By the way, this is the way to set a “temporary” environment variable, like export <var-name> <var-value> in bash. Using launchctl directly, the variable will be gone after the next reboot.

The launch agent plist-files reside in /Library/LaunchAgents for global and in ~/Library/LaunchAgents for the user’s launch agents. If you look at the filenames, they are named using reverse-domain-notation, like many things in macOS. For my environment variables I use the format setenv.<var-name>.plist.

For that case, their format define the command launchctl setenv <var-name> <var-value> as an array of arguments:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>setenv.[var-name]</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/launchctl</string>
        <string>setenv</string>
        <string>[var-name]</string>
        <string>[var-value]</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

This will lead to launchd execute the command and therefore set an environment variable each startup.

To make use of the mechanism without rebooting, you can execute the launch-agent using launchctl:

launchctl load -w path/to/file.plist

Though, you have to restart the app using the variable, e.g. Terminal.

One of the variables I need for lolcommits is LOLCOMMITS_ANIMATE to define the number of seconds to record. So the plist-file would be ~/Library/LaunchAgents/setenv.LOLCOMMITS_ANIMATE.plist and contain:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>setenv.LOLCOMMITS_ANIMATE</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/launchctl</string>
        <string>setenv</string>
        <string>LOLCOMMITS_ANIMATE</string>
        <string>3</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>