Managing Local and Project-wide Development Parameters in Leiningen

Little tip. Long headline.

In any project there are often settings that are specific to the context the project is run in (think of an environment parameter) and then there are parameters that are specifc to the developer/workstation they're run on. This is a guide to separate these two things nicely in Leiningen-based Clojure projects.

So you have a project setup that uses environ to determine the context the project is run in (development vs. production).

; in project.clj:
(defproject your-app "0.1.0-SNAPSHOT"
  ; ...
  :profiles {:dev {:env {:environment "development"}}})

Now you also want to use environment variables (or anything else thats supported by environ) to store AWS credentials to access Amazon S3. You don't want to commit these credentials into version control, therefore you can't add them to project.clj. The way to go is to create a file profiles.clj in your project to store workstation specific information. Naively this could look something like this

{:dev {:env {:aws-access-key "abc"
             :aws-secret-key "xyz"
             :s3-bucket "mybucket"}}}

If you run your project with this profiles.clj you will be able to access your AWS credentials. You might also notice that (environ/env :environment) is nil. That wasn't intended.

The problem here is that Leiningen will override keys in profiles defined in project.clj if the same profile has also been defined in profiles.clj. To recursively merge Leiningen profiles combine them like so:

;; in project.clj:
(defproject your-app "0.1.0-SNAPSHOT"
  ;; ...
  :profiles {:dev [:project/dev :local/dev]
             :project/dev {:env {:environment "development"}}})

;; in profiles.clj
{:local/dev {:env {:secret-key "xyz"}}}

Now both, :envrionment and :secret-key should be defined when you retrieve them using environ.

This is largely inspired by James Reeves' duct Leiningen template.

@martinklepsch, June 2015