amperity / lein-monolith

Leiningen plugin for working with monorepos.
Other
214 stars 18 forks source link

Inherited profiles aren't activated #30

Closed MatthewDarling closed 4 years ago

MatthewDarling commented 7 years ago

Leiningen has five profiles it activates by default: [:base :system :user :provided :dev]

When any of those are defined, they get activated by default on all tasks. This makes them useful for sharing things across projects.

Steps to reproduce

  1. Clone and switch to the minimal reproduction case on my fork
  2. cd lein-monolith/example/libs/lib-a
  3. lein test2junit - this prints 'test2junit' is not a task. See 'lein help'.
  4. lein with-profile +provided test2junit - this again fails to find the plugin, but prints some extra debug info
  5. git checkout provided-profile-activated-by-default - on this branch, the :provided profile is directly in the lib-a/project.clj
  6. lein test2junit - this prints Using test2junit version: 1.2.5 Running Tests... and some other stuff

It's not specific to the :provided profile either, the same thing happens with :dev as well.

The profiles do seem to be inherited, but they aren't (can't be?) activated. After lein-monolith has been run, the profiles look nearly identical for both branches:

Monolith inherited profile

➜  lib-a git:(inherited-profiles-not-activated) ✗ lein pprint :profiles                                                                                                                                                               12:26pm
{:monolith/inherited
 {:test-selectors
  {:unit (complement :integration), :integration :integration},
  :env {:foo "bar"}},
 :monolith/leaky
 {:repositories
  [["central"
    {:url "https://repo1.maven.org/maven2/", :snapshots false}]
   ["clojars" {:url "https://clojars.org/repo/"}]],
  :profiles
  {:provided
   {:plugins ([test2junit/test2junit "1.2.5"]),
    :jvm-opts nil,
    :eval-in nil}}},
 :provided
 {:plugins ([test2junit/test2junit "1.2.5"]),
  :jvm-opts nil,
  :eval-in nil}}

Non-inherited profile

➜  lib-a git:(provided-profile-activated-by-default) ✗ lein pprint :profiles                                                                                                                                                          12:29pm
{:provided
 {:plugins
  ([test2junit/test2junit
    "1.2.5"
    :exclusions
    ([org.clojure/clojure])]),
  :jvm-opts nil,
  :eval-in nil},
 :monolith/inherited
 {:test-selectors
  {:unit (complement :integration), :integration :integration},
  :env {:foo "bar"}},
 :monolith/leaky
 {:repositories
  [["central"
    {:url "https://repo1.maven.org/maven2/", :snapshots false}]
   ["clojars" {:url "https://clojars.org/repo/"}]]}}

Expected behaviour

That the :provided profile would be inherited. Then when running tasks, it would be activated, loading the test2junit plugin and allowing lein test2junit to work.

Actual behaviour

The :provided profile is inherited, but doesn't get activated. It also can't be activated with a manual lein with-profiles +provided ... call.

MatthewDarling commented 7 years ago

This turned out not to be an especially important bug for us, because we were able to directly inherit :plugins instead. But there are probably other use cases for inheriting profiles where regular inheritance doesn't serve the same purpose.

greglook commented 7 years ago

It looks like you're trying to inject the :provided profile using another profile (the monolith inherited one). I'm not surprised that this doesn't work; profile expansion is probably done operating before the embedded profile shows up in the project map. You could try a similar approach without monolith at all, by sticking :provided into your :user profile, for example.

MatthewDarling commented 7 years ago

You could try a similar approach without monolith at all, by sticking :provided into your :user profile, for example.

Something like this?

:profiles {:user
           {:profiles
            {:provided
             {:plugins [[test2junit "1.2.5"]]}}}}

I'm assuming that doesn't work. Which, by analogy, is what is happening when inheriting via lein-monolith?


I find it a bit surprising though that lein ppprint :profiles shows the :provided profile at the top level. When run on the inherited-profiles-not-activated branch, there are three profiles present: :provided, :monolith/inherited, and :monolith/leaky.

Is there some sort of intermediate step where only the two monolith profiles would be present?

greglook commented 7 years ago

Which, by analogy, is what is happening when inheriting via lein-monolith?

Yes, lein-monolith's inheritance pattern works by injecting the virtual profiles containing the keys specified in the metaproject and making sure those profiles are activated in subprojects before running tasks (whether directly or via monolith each). Profiles work by merging themselves intelligently into the main project map, so it's not surprising that the embedded :provided profile shows up in the final project map. My assertion is that despite this, it's showing up after the activation step, which is why it doesn't seem to affect the project.

For dependencies and plugins specifically, you can use the other syntax for marking them provided, which is: [foo/bar "0.1.0" :scope "provided"] if I recall correctly.

MatthewDarling commented 7 years ago

Ahhh, this makes more sense. I thought lein-monolith had a manual merging step, rather than reusing the profiles logic.

Makes sense now why that wouldn't work. And there's no reasonable way to implement it, really.

Maybe there could be a warning/error if :profiles is in the inherits list. lein-monolith is internally implemented using profiles. This means that profiles can't be inherited. Try directly inheriting the contents of the profiles.