xsc / lein-ancient

Check your Projects for outdated Dependencies
MIT License
561 stars 33 forks source link

Upgrading when invalid version numbers are present in project map #37

Closed johnwalker closed 10 years ago

johnwalker commented 10 years ago

I wondered if it would be possible to upgrade dependencies when invalid or non-existent dependencies are specified.

Right now, this is the behavior when you add non-existent ones:

:dependencies [[org.clojure/core.async "0.0.1"]]
(Could not find artifact org.clojure:core.async:jar:0.0.1 in central (https://repo1.maven.org/maven2/))
(Could not find artifact org.clojure:core.async:jar:0.0.1 in clojars (https://clojars.org/repo/))

Or invalid ones:

:dependencies [[org.clojure/core.async ""]]
java.lang.IllegalArgumentException: Bad artifact coordinates org.clojure:core.async:jar:, expected format is :[:[:]]:

Ideally, ancient would be able to upgrade to the latest valid one.

xsc commented 10 years ago

@johnwalker Good idea. Ideally, a vector without the version (i.e. [org.clojure/core.async]) could be processed as well - you didn't give a use case but I think this is useful when starting a new project, adding dependencies without versions, then running lein-ancient to have a valid project.clj.

Thanks!

johnwalker commented 10 years ago

Hehe, you read my mind :) :+1:

Thanks for ancient, it's my favorite plugin.

xsc commented 10 years ago

@johnwalker Can you try the latest SNAPSHOT?

[lein-ancient "0.6.0-SNAPSHOT"]
johnwalker commented 10 years ago

For dependencies with version string "", it finds the latest version but doesn't perform a replacement.

If you pass the dep as [org.clojure/core.async] (with nothing following the symbol) then ancient returns with upgrade-failed.

For dependencies with version nil, there's a stacktrace (but thats a weird case to handle)

xsc commented 10 years ago

Well, core.async is a special case since it currently only offers qualified versions (e.g. ...-alpha). You have to supply :allow-qualified on the command line to upgrade that.

$ cat project.clj
(defproject test-project "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [pandect ""]
                 [org.clojure/core.async]])
$ lein ancient upgrade :allow-qualified
[pandect "0.3.4"] is available but we use ""
[org.clojure/core.async "0.1.319.0-6b1aca-alpha"] is available but we use ""
2 artifacts were upgraded.

Running Test Task ["test"] ...
(Retrieving org/clojure/core.async/0.1.319.0-6b1aca-alpha/core.async-0.1.319.0-6b1aca-alpha.pom from central)
(Retrieving org/clojure/core.async/0.1.319.0-6b1aca-alpha/core.async-0.1.319.0-6b1aca-alpha.jar from central)

lein test user

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
$ cat project.clj
(defproject test-project "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [pandect "0.3.4"]
                 [org.clojure/core.async "0.1.319.0-6b1aca-alpha"]])

Note that you should probably use :interactive with :allow-qualified to prevent unwanted upgrades.

johnwalker commented 10 years ago

Hmm. With this same style example:

$ cat project.clj
(defproject baz "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [pandect ""]
                 [org.clojure/core.async]])

I'm getting:

$ lein ancient upgrade :allow-qualified
[pandect "0.3.4"] is available but we use ""
java.lang.NullPointerException: null
 at version_clj.split$eval4437$fn__4438.invoke (split.clj:60)
    version_clj.split$eval4418$fn__4419$G__4409__4426.invoke (split.clj:53)
    version_clj.split$first_split_at_point.invoke (split.clj:107)
    version_clj.split$version__GT_seq.invoke (split.clj:133)
    version_clj.split$version__GT_seq.invoke (split.clj:128)
    ancient_clj.core$artifact_map.invoke (core.clj:19)
    leiningen.ancient.utils.projects$create_artifact_map.invoke (projects.clj:37)
    leiningen.ancient.utils.projects$create_artifact_maps$fn__4685.invoke (projects.clj:51)
    clojure.core$map_indexed$mapi__6395$fn__6396.invoke (core.clj:6690)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$concat$fn__3955.invoke (core.clj:685)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$concat$cat__3957$fn__3958.invoke (core.clj:694)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$filter$fn__4264.invoke (core.clj:2595)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$map$fn__4245.invoke (core.clj:2551)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$filter$fn__4264.invoke (core.clj:2595)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$filter$fn__4264.invoke (core.clj:2595)
    clojure.lang.LazySeq.sval (LazySeq.java:40)
    clojure.lang.LazySeq.seq (LazySeq.java:49)
    clojure.lang.Cons.next (Cons.java:39)
    clojure.lang.RT.next (RT.java:598)
    clojure.core$next.invoke (core.clj:64)
    clojure.core.protocols/fn (protocols.clj:146)
    clojure.core.protocols$fn__6057$G__6052__6066.invoke (protocols.clj:19)
    clojure.core.protocols$seq_reduce.invoke (protocols.clj:31)
    clojure.core.protocols/fn (protocols.clj:48)
    clojure.core.protocols$fn__6031$G__6026__6044.invoke (protocols.clj:13)
    clojure.core$reduce.invoke (core.clj:6289)
    leiningen.ancient.upgrade$upgrade_artifact_map_BANG_.invoke (upgrade.clj:129)
    leiningen.ancient.upgrade$upgrade_artifact_file_BANG_.invoke (upgrade.clj:153)
    clojure.lang.AFn.applyToHelper (AFn.java:186)
    clojure.lang.AFn.applyTo (AFn.java:144)
    clojure.core$apply.invoke (core.clj:630)
    clojure.core$partial$fn__4234.doInvoke (core.clj:2474)
    clojure.lang.RestFn.invoke (RestFn.java:436)
    leiningen.ancient.upgrade$with_tests$fn__6101.invoke (upgrade.clj:213)
    leiningen.ancient.upgrade$with_backup$fn__6093.invoke (upgrade.clj:196)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.AFn.applyTo (AFn.java:144)
    clojure.core$apply.invoke (core.clj:624)
    leiningen.ancient.upgrade$with_abort$fn__6108.doInvoke (upgrade.clj:223)
    clojure.lang.RestFn.invoke (RestFn.java:436)
    leiningen.ancient.upgrade$upgrade_file_BANG_.invoke (upgrade.clj:250)
    leiningen.ancient.upgrade$upgrade_directory_BANG_.invoke (upgrade.clj:262)
    leiningen.ancient.upgrade$upgrade_path_BANG_.invoke (upgrade.clj:270)
    leiningen.ancient.upgrade$run_upgrade_task_BANG_.invoke (upgrade.clj:280)
    leiningen.ancient$ancient.doInvoke (ancient.clj:62)
    clojure.lang.RestFn.invoke (RestFn.java:439)
    clojure.lang.Var.invoke (Var.java:388)
    clojure.lang.AFn.applyToHelper (AFn.java:160)
    clojure.lang.Var.applyTo (Var.java:700)
    clojure.core$apply.invoke (core.clj:626)
    leiningen.core.main$partial_task$fn__5914.doInvoke (main.clj:240)
    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.invoke (core.clj:626)
    leiningen.core.main$apply_task.invoke (main.clj:290)
    leiningen.core.main$resolve_and_apply.invoke (main.clj:296)
    leiningen.core.main$_main$fn__5979.invoke (main.clj:366)
    leiningen.core.main$_main.doInvoke (main.clj:353)
    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.invoke (core.clj:624)
    clojure.main$main_opt.invoke (main.clj:315)
    clojure.main$main.doInvoke (main.clj:420)
    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)
johnwalker commented 10 years ago

If I add an empty string, and answer yes to "Do you want to overwrite the existing backup file? [yes/no]" then the upgrade is successful (provided that the tests pass). But if I answer no, then the upgrade fails.

xsc commented 10 years ago

Can you paste your profiles.clj? Maybe something's interfering with lein-ancient.

johnwalker commented 10 years ago

Sure -

{:user {:plugins [[org.clojure/tools.trace "0.7.8"]
                  [cider/cider-nrepl "0.8.0-SNAPSHOT"]
                  [jonase/eastwood "0.1.4"]
                  [lein-kibit "0.0.8"]
                  [lein-plz "0.1.1"]
                  [lein-ancient "0.6.0-SNAPSHOT"]
                  [slamhound "1.5.5"]]
        :plz ["/home/john/.lein/plz/default.edn"
              "/home/john/.lein/plz/newest.edn"]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}}
 :signing {:gpg-key "6105CB25"}}
johnwalker commented 10 years ago

Huh. Somehow, it does fine when lein-plz relies on the same version of ancient-clj.

johnwalker commented 10 years ago

Well, I was unfamiliar with this property. This configuration will work:

{:user {:plugins [[org.clojure/tools.trace "0.7.8"]
                  [cider/cider-nrepl "0.8.0-SNAPSHOT"]
                  [jonase/eastwood "0.1.4"]
                  [lein-kibit "0.0.8"]
                  [lein-plz "0.1.1"]
                  [lein-ancient "0.6.0-SNAPSHOT"]
                  [slamhound "1.5.5"]]
        :plz ["/home/john/.lein/plz/default.edn"
              "/home/john/.lein/plz/newest.edn"]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}}
 :signing {:gpg-key "6105CB25"}}

while this will not:

{:user {:plugins [[org.clojure/tools.trace "0.7.8"]
                  [cider/cider-nrepl "0.8.0-SNAPSHOT"]
                  [jonase/eastwood "0.1.4"]
                  [lein-kibit "0.0.8"]
                  [lein-plz "0.1.1"]
                  [lein-ancient "0.6.0-SNAPSHOT"]
                  [slamhound "1.5.5"]]
        :plz ["/home/john/.lein/plz/default.edn"
              "/home/john/.lein/plz/newest.edn"]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}}
 :signing {:gpg-key "6105CB25"}}
johnwalker commented 10 years ago

Alright, @hypirion had an answer:

15:11 hyPiRion: oh crap, that issue. Well, the only thing for now is `lein deps :plugin-tree` which emits warnings, or [lein-plugin :exclusions [[shared/dependency]]]

I will recommend lein-plz users use the :exclusions property with ancient-clj and rewrite-clj when they also have lein-ancient. I think this feature is completed.

For completeness, this will work:

{:user {:plugins [[org.clojure/tools.trace "0.7.8"]
                  [cider/cider-nrepl "0.8.0-SNAPSHOT"]
                  [jonase/eastwood "0.1.4"]
                  [lein-kibit "0.0.8"]
                  [lein-plz "0.1.1" :exclusions [[rewrite-clj] [ancient-clj]]]
                  [lein-ancient "0.6.0-SNAPSHOT"]
                  [slamhound "1.5.5"]]
        :plz ["/home/john/.lein/plz/default.edn"
              "/home/john/.lein/plz/newest.edn"]
        :aliases {"slamhound" ["run" "-m" "slam.hound"]}}
 :signing {:gpg-key "6105CB25"}}
xsc commented 10 years ago

Thanks for digging into this. Leiningen should probably emit warnings automatically for plugins, though. Anyway, now I know about lein deps :plugin-tree which will be quite useful when troubleshooting plugin problems in future.

johnwalker commented 10 years ago

Hehe, still quite embarrassing that it came from mine. ;) Thanks!