tools-deps

pez 2023-05-14T08:26:32.269489Z

What’s the best way to get 1.12.0-alpha3 installed in an apt-get based Docker container? This doesn’t work:

root@da127afd0308:/app# curl -O 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   243    0   243    0     0    371      0 --:--:-- --:--:-- --:--:--   371
root@da127afd0308:/app# tail -3 linux-install-1.12.0-alpha3.sh 
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>QHDZR8BX0JE4XQS0</RequestId><HostId>6kINnasq/o/x2gOdNFw0J3HxPPk1Owa081QnyKtTj9yPEHsOB6G/5o2HbcX5iBlYQ07TZ/AkFBo=</HostId></Error>root@da127afd0308:/app# 

Alex Miller (Clojure team) 2023-05-14T11:26:29.259709Z

There is no 1.12 version of the Clojure CLI. You need the latest version of the CLI (1.11.1.1273)

Alex Miller (Clojure team) 2023-05-14T11:27:39.086339Z

Any version of the CLI can use any version of Clojure - that depends on the Clojure version in your deps.edn

pez 2023-05-14T12:03:02.123349Z

Thanks! Can I assume that any stable Clojure release has a corresponding CLI?

Alex Miller (Clojure team) 2023-05-14T12:20:55.563979Z

That’s not a meaningful question

Alex Miller (Clojure team) 2023-05-14T12:21:22.057809Z

So, no

Alex Miller (Clojure team) 2023-05-14T12:25:09.278599Z

The CLI version is actually meaningfully only the last part. The first 3 parts indicate the Clojure version it was built with and the Clojure version you’ll get from the root deps.edn if you don’t say otherwise

pez 2023-05-14T12:25:27.578129Z

Haha, thanks. Is there a way I can programmatically figure out the latest CLI download path?

Alex Miller (Clojure team) 2023-05-14T12:25:38.169259Z

Yes

Alex Miller (Clojure team) 2023-05-14T12:27:36.192289Z

There is a file https://download.clojure.org/install/stable.properties

Alex Miller (Clojure team) 2023-05-14T12:28:41.868769Z

That has the version and the sha of the download

Alex Miller (Clojure team) 2023-05-14T12:29:48.921909Z

Also if you use the url at the top but without the version, that’s always the latest

Alex Miller (Clojure team) 2023-05-14T12:30:30.781669Z

https://download.clojure.org/install/linux-install.sh

pez 2023-05-14T12:34:29.255469Z

Awesome! Exactly what I need.

pez 2023-05-14T15:14:19.663799Z

Is there a way to tell clojure to only consider the classpath that is specified in the local deps.edn file? So, nothing from user deps.edn and nothing that is not specified (like no implicit clojure dependency)?

Alex Miller (Clojure team) 2023-05-14T15:16:49.366849Z

-Srepro will omit the user deps.edn

Alex Miller (Clojure team) 2023-05-14T15:17:44.656519Z

You always need a Clojure dep, the default is in the root deps.edn, but overrides by project deps.edn

Alex Miller (Clojure team) 2023-05-14T15:18:13.209229Z

But maybe it’s more useful to tell me what you’re actually trying to do

pez 2023-05-14T15:21:55.509249Z

I am trying to feed ClojureCLR a classpath based on a deps.edn file. This is what I have now:

#!/usr/bin/env bb

(require '[clojure.string :as string])
(require '[babashka.process :refer [sh]])
(require '[babashka.fs :as fs])

(sh "clojure" "-P" "-A:dev")

(def original-classpath (string/trim (:out (sh "clojure" "-Spath" "-A:dev"))))

(def skip-deps ["org/clojure/clojure" 
                "org/clojure/core.specs.alpha"
                "org/clojure/spec.alpha"])
(def skipped-deps-re (re-pattern (str ".*/\\.m2/repository/("
                                      (string/join "|" skip-deps)
                                      ")/.*")))

(defn skip-path? [path]
  (re-matches skipped-deps-re path))

(defn process-classpath [classpath]
  (let [paths (string/split classpath #":")]
    (->> paths
         (remove skip-path?)
         (map (fn [path]
                (if (.endsWith path ".jar")
                  (let [deps-subdir (string/replace (fs/file-name path) #".jar$" "")
                        deps-dir (str "/app/dependencies/" deps-subdir)]
                    (sh "mkdir" "-p" deps-dir)
                    (sh "unzip" "-q" "-o" path "-d" deps-dir)
                    deps-dir)
                  path)))
         (string/join ":"))))

(when (= *file* (System/getProperty "babashka.file"))
  (-> original-classpath
      process-classpath
      println))

pez 2023-05-14T15:24:16.493089Z

I’ll add the -Srepro, even if it isn’t strictly needed because this is run in a Docker container that does not have a user deps.edn.

Alex Miller (Clojure team) 2023-05-14T15:24:32.761729Z

You can just call tools.deps directly

👀 1
Alex Miller (Clojure team) 2023-05-14T15:24:59.309079Z

Instead of shelling out to use the CLI which uses tools.deps

pez 2023-05-14T15:27:43.828439Z

Hmmm, not sure how. Babashka has tools.deps in there somewhere?

Alex Miller (Clojure team) 2023-05-14T15:28:02.306659Z

Call clojure.tools.deps/create-basis and pass it :root nil :user nil. That gives you a basis with a :classpath-roots, then call join-classpath

Alex Miller (Clojure team) 2023-05-14T15:28:26.024169Z

I think it does, but prob a question for the borkmeister

Alex Miller (Clojure team) 2023-05-14T15:29:04.358349Z

Alternately you can call the create-basis variant provided in the :deps alias

Alex Miller (Clojure team) 2023-05-14T15:29:48.198889Z

clj -X:deps create-basis :root nil :user nil

Alex Miller (Clojure team) 2023-05-14T15:30:02.965769Z

Something like that, on my phone

pez 2023-05-14T15:30:56.712109Z

I’ll experiment with this a bit.

Alex Miller (Clojure team) 2023-05-14T15:31:43.825029Z

Oh wait, that’s not there anymore but there is prob some path to doing it

Alex Miller (Clojure team) 2023-05-14T15:32:27.303929Z

But really, tools.deps is a library to make classpaths. Ideally you can just call it to do that

borkdude 2023-05-14T15:32:30.043719Z

@pez babashka's classpath is empty by default and if you use (babashka.deps/add-deps '{:deps {}}) it will only add those deps to the classpath, not even clojure itself

borkdude 2023-05-14T15:33:36.041039Z

You can retrieve the bb classpath with (babashka.classpath/get-classpath)

borkdude 2023-05-14T15:36:09.582989Z

This is done via the :classpath-overrides option similar to this:

clj -Sdeps '{:deps {} :aliases {:rm-clojure {:classpath-overrides {org.clojure/clojure nil}}}}' -A:rm-clojure -Spath

pez 2023-05-14T15:37:44.715159Z

Can I get add-deps to use my deps.edn file?

borkdude 2023-05-14T15:39:14.075539Z

@pez Yes: (add-deps (edn/read-string (slurp "my-deps.edn")))

pez 2023-05-14T15:45:19.155579Z

How would I tell it to use the :dev alias in there?

borkdude 2023-05-14T15:48:28.523189Z

@pez

(add-deps {:deps ..} {:aliases [:dev]})

pez 2023-05-14T15:51:32.274269Z

Awesome, works! Does it also download dependencies?

borkdude 2023-05-14T15:55:07.916719Z

yes

pez 2023-05-14T16:10:50.943259Z

This is great! Now I have this:

#!/usr/bin/env bb

(require '[clojure.string :as string])
(require '[clojure.edn :as edn])
(require '[babashka.process :refer [sh]])
(require '[babashka.deps :as deps])
(require '[babashka.classpath :as classpath])
(require '[babashka.fs :as fs])

(defn process-classpath [classpath]
  (let [paths (string/split classpath #":")]
    (->> paths
         (map (fn [path]
                (if (.endsWith path ".jar")
                  (let [deps-subdir (string/replace (fs/file-name path) #".jar$" "")
                        deps-dir (str "/app/dependencies/" deps-subdir)]
                    (sh "mkdir" "-p" deps-dir)
                    (sh "unzip" "-q" "-o" path "-d" deps-dir)
                    deps-dir)
                  path)))
         (string/join ":"))))

(when (= *file* (System/getProperty "babashka.file"))
  (deps/add-deps (edn/read-string (slurp "deps.edn")) {:aliases [:dev]})
  (-> (classpath/get-classpath)
      process-classpath
      println))
It does some work when running the script that I might want to have it do as part of building the container. Like downloading Clojure and stuff.
root@d278348ba283:/app# output=$(docker/cheap-deps.clj) 
Downloading: org/clojure/clojure/1.11.1/clojure-1.11.1.pom from central
Downloading: org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.pom from central
Downloading: org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.pom from central
Downloading: org/clojure/pom.contrib/1.1.0/pom.contrib-1.1.0.pom from central
Downloading: org/clojure/clr/tools.nrepl/0.1.0-alpha1/tools.nrepl-0.1.0-alpha1.pom from clojars
Downloading: org/clojure/clr/tools.reader/1.3.7/tools.reader-1.3.7.pom from clojars
Downloading: org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar from central
Downloading: org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar from central
Downloading: org/clojure/clojure/1.11.1/clojure-1.11.1.jar from central
Downloading: org/clojure/clr/tools.reader/1.3.7/tools.reader-1.3.7.jar from clojars
Downloading: org/clojure/clr/tools.nrepl/0.1.0-alpha1/tools.nrepl-0.1.0-alpha1.jar from clojars
root@d278348ba283:/app# echo $output
src:dev:/app/dependencies/tools.nrepl-0.1.0-alpha1:/app/dependencies/tools.reader-1.3.7
What’s short command line to tell Babashka to do this initial work?

borkdude 2023-05-14T16:12:25.482529Z

> What’s short command line to tell Babashka to do this initial work? I'm not sure what the question is

borkdude 2023-05-14T16:13:26.793299Z

btw instead of (sh "mkdir" "-p") , you could use (fs/create-dirs deps-dir) and for (sh "unzip") you could possibly use fs/unzip

🙏 1
borkdude 2023-05-14T16:16:39.829149Z

Also instead of #":" I recommend using fs/path-separator for better cross-platform compatibility

🙏 1
pez 2023-05-14T16:16:41.291439Z

The question. I want to populate the maven repo with the stuff that babashka needs at container build time. So that running my script doesn’t pay that overhead.

borkdude 2023-05-14T16:17:07.987639Z

bb already has babashka.classpath/split-classpath which can split the classpath for you in a cross-platform manner

🙏 1
borkdude 2023-05-14T16:18:53.217599Z

> I want to populate the maven repo with the stuff that babashka needs at container build time Do you mean the "installation" of deps.clj, i.e. downloading the tools jar?

pez 2023-05-14T16:23:18.831089Z

Yes, if that’s what causes all those downloads, then that’s what I mean. 😃

pez 2023-05-14T16:23:52.834309Z

The script now looks like

(defn process-classpath [classpath]
  (->> (classpath/split-classpath classpath)
       (map (fn [path]
              (if (.endsWith path ".jar")
                (let [deps-subdir (string/replace (fs/file-name path) #".jar$" "")
                      deps-dir (str "/app/dependencies/" deps-subdir)]
                  (fs/create-dirs deps-dir)
                  (fs/unzip path deps-dir {:replace-existing true})
                  deps-dir)
                path)))
       (string/join ":")))

(when (= *file* (System/getProperty "babashka.file"))
  (deps/add-deps (edn/read-string (slurp "deps.edn")) {:aliases [:dev]})
  (-> (classpath/get-classpath)
      process-classpath
      println))
And runs crazy fast compared to what I started with!

borkdude 2023-05-14T16:26:41.945569Z

> Yes, if that’s what causes all those downloads, then that’s what I mean. I don't know what you mean by "all those downloads", can you be more specific? If you download deps using this script, then it's expected that ... there are "all those downloads"?

borkdude 2023-05-14T16:27:22.833169Z

(.endsWith path ".jar") => (str/ends-with? path ".jar") would be my preference here

🙏 1
pez 2023-05-14T17:28:17.241939Z

Sorry for being unclear. Of those downloads there, only the last two are from my deps.edn (one directly, and one transiently). The rest is something triggered by my script.

borkdude 2023-05-14T17:32:53.331999Z

are you sure? e.g.:

Downloading: org/clojure/clr/tools.nrepl/0.1.0-alpha1/tools.nrepl-0.1.0-alpha1.pom from clojars
Downloading: org/clojure/clr/tools.reader/1.3.7/tools.reader-1.3.7.pom from clojars

borkdude 2023-05-14T17:33:50.579329Z

I'd do an -Stree or -X:deps tree to double check

pez 2023-05-14T18:28:42.490119Z

Those are the last two. My deps.edn looks like so:

{:paths ["src"]
 :aliases {:dev {:extra-paths ["dev"]
                 :extra-deps {org.clojure.clr/tools.nrepl {:mvn/version "0.1.0-alpha1"}}}}}

pez 2023-05-14T18:31:13.422229Z

# clojure -Stree 
org.clojure/clojure 1.11.1
  . org.clojure/spec.alpha 0.3.218
  . org.clojure/core.specs.alpha 0.2.62

borkdude 2023-05-14T18:52:24.869159Z

Well, those other deps aren't needed by bb so I think it's just the result of tools.deps downloading those as well

pez 2023-05-14T18:59:06.079159Z

Is there a way I can trigger the download of those dependencies without running my script?

pez 2023-05-14T19:02:35.256389Z

This isn’t enough:

RUN curl -sLO  \
    && chmod +x install \
    && ./install \
    && bb -e '(require (quote [babashka.deps :as deps]))'

borkdude 2023-05-14T19:03:39.386319Z

I think you could even prevent downloading those dependencies with a hack:

clj -Sdeps '{:deps {org.clojure/clojure {:local/root "src" :deps/manifest :deps} org.clojure/core.specs.alpha {:local/root "src" :deps/manifest :deps}}}}' -Spath
src:/private/tmp/src/src:/private/tmp/src/src
So by setting clojure + core.specs.alpha to a local (existing!) dir it won't download those

borkdude 2023-05-14T19:04:09.989009Z

but note that those libs will end up on the classpath, but combined with :classpath-overrides they will then go away again, I think

borkdude 2023-05-14T19:05:16.933629Z

You could force downloading those dependencies by just pre-fetching an arbitrary dependency with add-deps in the image build, probably

pez 2023-05-14T19:07:58.532359Z

I don’t have clj in the container any longer. Can I do this with Babashka?

borkdude 2023-05-14T19:08:08.382279Z

yes, with add-deps

borkdude 2023-05-14T19:08:19.746139Z

bb -e '(babashka.deps/add-deps ...)'

borkdude 2023-05-14T19:15:15.513749Z

$ bb -e '(babashka.deps/add-deps (quote {:mvn/local-repo "/tmp/src" :deps {medley/medley {:mvn/version "1.0.0"}}}))'
Downloading: org/clojure/clojure/1.11.1/clojure-1.11.1.pom from central
Downloading: org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.pom from central
Downloading: org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.pom from central
Downloading: org/clojure/pom.contrib/1.1.0/pom.contrib-1.1.0.pom from central
Downloading: medley/medley/1.0.0/medley-1.0.0.pom from clojars
Downloading: org/clojure/core.specs.alpha/0.2.62/core.specs.alpha-0.2.62.jar from central
Downloading: org/clojure/spec.alpha/0.3.218/spec.alpha-0.3.218.jar from central
Downloading: org/clojure/clojure/1.11.1/clojure-1.11.1.jar from central
Downloading: medley/medley/1.0.0/medley-1.0.0.jar from clojars

borkdude 2023-05-14T19:15:41.820439Z

so yeah, it seems to download clojure + spec etc even though they are cancelled out on the classpath with classpath-overrides 🤷

borkdude 2023-05-14T19:16:23.695089Z

$ bb -e '(babashka.deps/add-deps (quote {:mvn/local-repo "/tmp/src" :deps {medley/medley {:mvn/version "1.0.0"}}})) (prn (babashka.classpath/get-classpath))'
"/tmp/src/medley/medley/1.0.0/medley-1.0.0.jar"