amperity / lein-monolith

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

:monolith/inherit breaks composite profiles when run from repo root #29

Closed MatthewDarling closed 3 years ago

MatthewDarling commented 7 years ago

Steps to reproduce

  1. Clone and switch to the minimal reproduction case on my fork
  2. cd lein-monolith/example
  3. CONFIG_JAR_PATH=/dev/null lein monolith each install. This will crash when installing app-a.
  4. cd apps/app-a
  5. CONFIG_JAR_PATH=/dev/null lein monolith each install. This will succeed.
  6. cd ../../libs/lib-a
  7. CONFIG_JAR_PATH=/dev/null lein monolith each install. This will succeed.

Expected behaviour

install for app-a should succeed regardless of where the command is executed.

Actual behaviour

When the command is run from the same directory as the parent project.clj, it will reliably crash with a huge stacktrace:

Click to show stacktrace ```clojure Applying to example/app-a Resume with: lein monolith each :start example/app-a install Completed example/app-a (3/3) in 0:00.024 java.lang.NullPointerException: null at leiningen.core.project$absolutize.invokeStatic (project.clj:425) leiningen.core.project$absolutize.invoke (project.clj:424) clojure.core$partial$fn__4759.invoke (core.clj:2515) clojure.core$map$fn__4785.invoke (core.clj:2644) clojure.lang.LazySeq.sval (LazySeq.java:40) clojure.lang.LazySeq.seq (LazySeq.java:49) clojure.lang.LazySeq.withMeta (LazySeq.java:34) clojure.lang.LazySeq.withMeta (LazySeq.java:17) clojure.core$with_meta__4375.invokeStatic (core.clj:218) clojure.core/with_meta (core.clj:217) leiningen.core.project$with_meta_STAR_.invokeStatic (project.clj:110) leiningen.core.project$with_meta_STAR_.invoke (project.clj:105) leiningen.core.project$absolutize_path$fn__6177.invoke (project.clj:434) clojure.core$update.invokeStatic (core.clj:5960) clojure.core$update.invoke (core.clj:5952) leiningen.core.project$absolutize_path.invokeStatic (project.clj:434) leiningen.core.project$absolutize_path.invoke (project.clj:429) clojure.core.protocols$iter_reduce.invokeStatic (protocols.clj:49) clojure.core.protocols$fn__6744.invokeStatic (protocols.clj:75) clojure.core.protocols/fn (protocols.clj:75) clojure.core.protocols$fn__6684$G__6679__6697.invoke (protocols.clj:13) clojure.core$reduce.invokeStatic (core.clj:6545) clojure.core$reduce.invoke (core.clj:6527) leiningen.core.project$absolutize_paths.invokeStatic (project.clj:440) leiningen.core.project$absolutize_paths.invoke (project.clj:439) leiningen.core.project$init_profiles.invokeStatic (project.clj:836) leiningen.core.project$init_profiles.doInvoke (project.clj:814) clojure.lang.RestFn.invoke (RestFn.java:425) leiningen.core.project$init_project.invokeStatic (project.clj:900) leiningen.core.project$init_project.invoke (project.clj:892) lein_monolith.task.each$apply_subproject_task.invokeStatic (each.clj:197) lein_monolith.task.each$apply_subproject_task.invoke (each.clj:187) lein_monolith.task.each$run_task_BANG_.invokeStatic (each.clj:246) lein_monolith.task.each$run_task_BANG_.invoke (each.clj:230) clojure.core$partial$fn__4759.invoke (core.clj:2515) clojure.core$comp$fn__4727.invoke (core.clj:2460) clojure.core$mapv$fn__6953.invoke (core.clj:6627) clojure.core.protocols$fn__6755.invokeStatic (protocols.clj:167) clojure.core.protocols/fn (protocols.clj:124) clojure.core.protocols$fn__6710$G__6705__6719.invoke (protocols.clj:19) clojure.core.protocols$seq_reduce.invokeStatic (protocols.clj:31) clojure.core.protocols$fn__6738.invokeStatic (protocols.clj:75) clojure.core.protocols/fn (protocols.clj:75) clojure.core.protocols$fn__6684$G__6679__6697.invoke (protocols.clj:13) clojure.core$reduce.invokeStatic (core.clj:6545) clojure.core$mapv.invokeStatic (core.clj:6618) clojure.core$mapv.invoke (core.clj:6618) lein_monolith.task.each$run_linear_BANG_.invokeStatic (each.clj:273) lein_monolith.task.each$run_linear_BANG_.invoke (each.clj:269) lein_monolith.task.each$run_tasks.invokeStatic (each.clj:326) lein_monolith.task.each$run_tasks.invoke (each.clj:305) leiningen.monolith$each.invokeStatic (monolith.clj:160) leiningen.monolith$each.invoke (monolith.clj:111) leiningen.monolith$monolith.invokeStatic (monolith.clj:197) leiningen.monolith$monolith.doInvoke (monolith.clj:185) clojure.lang.RestFn.invoke (RestFn.java:442) clojure.lang.Var.invoke (Var.java:388) clojure.lang.AFn.applyToHelper (AFn.java:160) clojure.lang.Var.applyTo (Var.java:700) clojure.core$apply.invokeStatic (core.clj:648) clojure.core$apply.invoke (core.clj:641) leiningen.core.main$partial_task$fn__5932.doInvoke (main.clj:272) clojure.lang.RestFn.applyTo (RestFn.java:139) clojure.lang.AFunction$1.doInvoke (AFunction.java:29) clojure.lang.RestFn.applyTo (RestFn.java:137) clojure.core$apply.invokeStatic (core.clj:648) clojure.core$apply.invoke (core.clj:641) leiningen.core.main$apply_task.invokeStatic (main.clj:322) leiningen.core.main$apply_task.invoke (main.clj:308) leiningen.core.main$resolve_and_apply.invokeStatic (main.clj:328) leiningen.core.main$resolve_and_apply.invoke (main.clj:324) leiningen.core.main$_main$fn__5998.invoke (main.clj:401) leiningen.core.main$_main.invokeStatic (main.clj:394) leiningen.core.main$_main.doInvoke (main.clj:391) clojure.lang.RestFn.invoke (RestFn.java:436) clojure.lang.Var.invoke (Var.java:388) clojure.lang.AFn.applyToHelper (AFn.java:160) clojure.lang.Var.applyTo (Var.java:700) clojure.core$apply.invokeStatic (core.clj:646) clojure.main$main_opt.invokeStatic (main.clj:314) clojure.main$main_opt.invoke (main.clj:310) clojure.main$main.invokeStatic (main.clj:421) clojure.main$main.doInvoke (main.clj:384) clojure.lang.RestFn.invoke (RestFn.java:482) clojure.lang.Var.invoke (Var.java:401) clojure.lang.AFn.applyToHelper (AFn.java:171) clojure.lang.Var.applyTo (Var.java:700) clojure.main.main (main.java:37) ```


The crash only happens when three specific things are combined:

  1. You're running the lein monolith each install command where the parent project.clj is
  2. You have a composite profile that gets activated during the install task
  3. The subproject with the composite profiles inherits something from the parent. If nothing is inherited the command will succeed.

If you comment out the :uberjar profile, it will work fine. It also works if you manually merge the two profiles, avoiding a composite profile entirely.

The profile also has to be activated - in that second branch, the command works fine even though the :dev profile is still a composite profile.

greglook commented 7 years ago

Haven't forgotten about this, but due to scheduling it may be a few weeks before I have time to look into it.

thevermeer commented 6 years ago

@greglook - this issue persists. Might I politely inquire as to the feasibility of fixing this issue?

greglook commented 3 years ago

Well, a few weeks turned into three years, but I finally dug into this! This still fails on modern Leiningen versions, with a slightly different error though.

It turns out to be a really gnarly interaction deep inside Leiningen that I still don't fully understand, but for some reason supplying multiple profiles to init-project adds a number of metadata profiles with :aliases nil entries https://github.com/technomancy/leiningen/blob/94a1b2e6c9cd2028400c2e8abb4797d3f4116ef1/leiningen-core/src/leiningen/core/project.clj#L1019-L1022 This in turn causes the meta-merge call in apply-profiles to try merging {:aliases nil} into the :uberjar profile: https://github.com/technomancy/leiningen/blob/94a1b2e6c9cd2028400c2e8abb4797d3f4116ef1/leiningen-core/src/leiningen/core/project.clj#L621 When :uberjar is a map, this works okay because the nil is a no-op. When it's a composite profile though, this tries to merge the map into the vector, which winds up coercing it into a sequence. I think I have an approach that will fix it, but needs testing.

MatthewDarling commented 3 years ago

Oh, cool! I checked out the PR, pretty neat that moving the middleware helped to reduce the "special case" of having to inject profiles.

In terms of it needing testing, is that something you'd like help with? Or were you able to confirm the fix with the old example I had put together?

greglook commented 3 years ago

I was using the example branch you provided to test out locally, so thanks for that! I'm trying to tackle a bunch of the outstanding issues, then I'll publish a snapshot version - any additional testing you can throw at it would certainly be helpful.