Hi can I get some help making an uberjar that can be run with java -jar target/app.jar ?
What I've done so far:
Cloned the latest electric v3 starter app.
Added #?(:clj (:gen-class)) to the top of src-prod/prod.cljc
Modified src-build/build.clj to compile before uber - see Tobias comments below
(let [jar-name (or (some-> jar-name str)
(format "electricfiddle-%s.jar" version))
aliases [:prod]
basis (b/create-basis {:project "deps.edn" :aliases aliases})] ; Tobias added line
;; Tobias added b/compile-clj form below
(b/compile-clj {:basis basis
:ns-compile '[prod]
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file (str "target/" jar-name)
:basis basis ; Tobias changed line
:main 'prod}) ; Tobias edited line
(log/info jar-name))
Compiled app with clj -X:build:prod uberjar :build/jar-name "app.jar"
Run app with java -jar target/app.jar
Then I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: hyperfiddle/electric3$Apply (wrong name: hyperfiddle/electric3$apply)
My java version is openjdk 21.0.6 2025-01-21 LTS
Thanks in advance for your help!Could the error be due to the compiler trying to compile hyperfiddle.electric3/apply and hyperfiddle.electric3/Apply (both defined in ns hyperfiddle.electric3) to the same file name hyperfiddle/electric3$Apply.class on a case-insensitive file system? Weirdly I get the same error though even if I try compiling on a newly-created case-sensitive volume on my mac.
hard to debug this from my phone, did you confirm that the unmodified starter app repo dockerfile works? i believe we deploy it and therefore it should work
Not sure about Docker but the regular way of building and running work just fine, i.e. leave prod.cljc and build.clj unchanged and run the following
clj -X:build:prod uberjar :build/jar-name "app.jar"
java -cp target/app.jar clojure.main -m prod
BTW there's no urgency this can wait until after the weekendThe only reason I want to run it as java -jar target/app.jar is that's just a lot easier to set up with my hosting provider as they have a managed java image they keep up to date
A workaround in case anyone else runs into this problem is to compile the app inside of a Docker container using the Dockerfile that comes with the starter app. Here's a one-liner script to compile and then copy the app.jar out of the container:
VERSION=$(git describe --tags --long --always --dirty) \
&& docker build --build-arg VERSION=$VERSION -t electric3-starter-app-builder . \
&& docker create --name extract-electric electric3-starter-app-builder \
&& rm -rf target && mkdir -p target \
&& docker cp extract-electric:/app/target/app.jar target/app.jar \
&& docker rm extract-electric \
&& echo "✅ Built target/app.jar with version: $VERSION"
I think the reason this works is because the file system inside the docker container is case sensitive.