Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for automatic activation of profiles via new :active? expression #1228

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions doc/PROFILES.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,46 @@ Multiple profiles may be executed in series with colons:

$ lein with-profile 1.3:1.4 test :database

You may also add/remove profiles from the active profile list by prefixing their
names with `+` and `-` respectively. Refer to `lein help with-profile` for more
details.


## Activating Profiles Automatically

You might want to activate some profiles implicitly based on certain
conditions. In order to activate a profile implicitly, set the `:active?` key to a
boolean expression.

In the following example, profile `:sticky` gets always activated:

```clj
...
:profiles {:sticky {:active? true
... }
```

In the following example, lein activates the `:linux` profile only when it is running on Linux:

```clj
...
:profiles {:linux {:active? (= (System/getProperty "os.name") "Linux")
:dependencies [[org.blah/linux-specific-dep "1.1.0"]] }
```


The value of keyword `:active?` may also be a predicate function receiving the
lein project as its only argument:

```clj
...
:profiles {:non-gpl {:active? (fn [project]
(not (= (-> project :license :name) "GPL" )))
:dependencies [[org.blah/impure-lib "2.0.0"]] }
```

You can override the implicit activation of a profile using `lein with-profile -profilename`. Refer to `lein help with-profile` for more details.

## Composite Profiles

Sometimes it is useful to define a profile as a combination of other
Expand Down
24 changes: 24 additions & 0 deletions leiningen-core/dev-resources/autoprofiles.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
(defproject autoprofiles "0.0.1"
:description "Test automatic activation of profiles via :active? expression"

:profiles {

:no_activation {:no_activation true }

:literal_false {:active? false
:literal_false true }

:literal_true {:active? true
:literal_true true }

:expression_true {:active? (< 1 2)
:expression_true true }

:expression_false {:active? (> 1 2)
:expression_false true }

:fn_false {:active? (fn [project] (not (= (:name project) "autoprofiles" )) )
:fn_false true }

:fn_true {:active? (fn [project] (= (:name project) "autoprofiles" ) )
:fn_true true } } )
36 changes: 29 additions & 7 deletions leiningen-core/src/leiningen/core/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,15 @@
right)))

(defn- apply-profiles [project profiles]
(reduce (fn [project profile]
(with-meta
(meta-merge project profile)
(meta-merge (meta project) (meta profile))))
project
profiles))

(let [transient-keys [:active?]]

(reduce (fn [project profile]
(with-meta
(meta-merge project (apply dissoc profile transient-keys))
(meta-merge (meta project) (meta profile))))
project
profiles)))

(defn- lookup-profile
"Lookup a profile in the given profiles map, warning when the profile doesn't
Expand Down Expand Up @@ -643,6 +646,22 @@
[:profiles] merge
profiles-map)}))


(defn- auto-active-profiles
"Given a project with :profiles metadata, return the keys of those profiles
for which the `:active?` expression or function `(fn [project])` evaluates to
true"
[project]

(map key (filter (fn [[k profile]]
(let [activation (eval (:active? profile))]
(if (fn? activation)
(activation project)
activation)))

(-> project meta :profiles))))


(defn read
"Read project map out of file, which defaults to project.clj."
([file profiles]
Expand All @@ -656,6 +675,9 @@
(throw (Exception. (format "%s must define project map" file))))
;; return it to original state
(ns-unmap 'leiningen.core.project 'project)
(init-profiles (project-with-profiles @project) profiles))))

(let [project (project-with-profiles @project)]
(init-profiles project (concat profiles (auto-active-profiles project)))))))

([file] (read file [:default]))
([] (read "project.clj")))
19 changes: 19 additions & 0 deletions leiningen-core/test/leiningen/core/test/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,22 @@
[["central" {:url "http://repo1.maven.org/maven2/"
:snapshots false}]
["clojars" {:url "https://clojars.org/repo/"}]]}})))))))



(deftest test-read-auto-profiles
(let [actual (read (.getFile (io/resource "autoprofiles.clj")))]

(testing "Profiles with truthy `:active?` expressions/functions must be active"
(is (:literal_true actual))
(is (:expression_true actual))
(is (:fn_true actual)))

(testing "Profiles with falsy or missing `:active?` expressions must not be active"
(is (not (:no_activation actual)))
(is (not (:literal_false actual)))
(is (not (:expression_false actual)))
(is (not (:fn_false actual))))

(testing "The `:active?` entry must NOT leak out of the profiles and into the project"
(is (nil? (:active? actual))))))