Reputation: 1166
I am writing a web application with the backend in Clojure and the frontend in Clojurescript with React and Reagent. I am trying to host it on Heroku.
When I run lein ubjerar
locally, it builds fine and the app works.
However, when I try to deploy the app to Heroku via git push heroku master
, I always end up with an error like this:
remote: Preparing npm packages
remote: Installing npm packages
remote: npm packages successfully installed
remote: Running shadow-cljs...
remote: [:app] Compiling ...
remote: The required JS dependency "object-assign" is not available, it was required by "node_modules/react/cjs/react.production.min.js".
remote:
remote: Searched in:/tmp/build_c09494ebe081fa0581db343dc809fb45/node_modules
remote:
remote: You probably need to run:
remote: npm install object-assign
remote:
remote: See: https://shadow-cljs.github.io/docs/UsersGuide.html#npm-install
remote:
remote: ! Failed to build.
remote: ! Push rejected, failed to compile Clojure (Leiningen 2) app.
remote:
remote: ! Push failed
remote: Verifying deploy...
remote:
remote: ! Push rejected to getfluentspanish.
remote:
To https://git.heroku.com/getfluentspanish.git
! [remote rejected] master -> master (pre-receive hook declined)
This happens in spite of the fact that object-assign
is listed as a dependency of the react
npm package, which I already have listed as an item in :npm-deps
in my project.clj
. I have verified that when I build locally, the package installs fine (i.e. in this case node_modules/object-assign
exists).
When I manually add object-assign
as an explicit dependency in :npm-deps
, I get the same error, but complaining about a different missing dependency that should get installed automatically (and does locally). When I add the new missing dependency, it complains about another. When I build locally with the new explicit dependencies, lein uberjar
works but complains that there are now version conflicts because the implicit dependencies are often fixed at a different version.
Any idea how I can fix this?
EDIT Here is my project.clj
(defproject getfluentspanish "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:dependencies [[cheshire "5.8.1"]
[cljs-ajax "0.8.0"]
[clojure.java-time "0.3.2"]
[com.cognitect/transit-clj "0.8.313"]
[com.google.javascript/closure-compiler-unshaded "v20190528" :scope "provided"]
[cprop "0.1.13"]
[day8.re-frame/http-fx "0.1.6"]
[funcool/struct "1.4.0"]
[luminus-immutant "0.2.5"]
[luminus-transit "0.1.1"]
[markdown-clj "1.10.0"]
[metosin/muuntaja "0.6.4"]
[metosin/reitit "0.3.7"]
[metosin/ring-http-response "0.9.1"]
[mount "0.1.16"]
[nrepl "0.6.0"]
[org.clojure/clojure "1.10.1"]
[org.clojure/clojurescript "1.10.520" :scope "provided"]
[org.clojure/google-closure-library "0.0-20190213-2033d5d9" :scope "provided"]
[org.clojure/tools.cli "0.4.2"]
[org.clojure/tools.logging "0.4.1"]
[org.webjars.npm/bulma "0.7.5"]
[org.webjars.npm/material-icons "0.3.0"]
[org.webjars/webjars-locator "0.36"]
[org.webjars/webjars-locator-jboss-vfs "0.1.0"]
[re-frame "0.10.6"]
[reagent "0.8.1"]
[ring-webjars "0.2.0"]
[ring/ring-core "1.7.1"]
[ring/ring-defaults "0.3.2"]
[selmer "1.12.12"]
[thheller/shadow-cljs "2.8.39" :scope "provided"]]
:min-lein-version "2.0.0"
:source-paths ["src/clj" "src/cljs" "src/cljc"]
:test-paths ["test/clj"]
:resource-paths ["resources" "target/cljsbuild"]
:target-path "target/%s/"
:main ^:skip-aot getfluentspanish.core
:plugins [[lein-shadow "0.1.3"]
[lein-immutant "2.1.0"]
[lein-sassc "0.10.4"]
[lein-auto "0.1.2"]]
:sassc
[{:src "resources/scss/screen.scss"
:output-to "resources/public/css/screen.css"
:style "nested"
:import-path "resources/scss"}]
:auto
{"sassc" {:file-pattern #"\.(scss|sass)$" :paths ["resources/scss"]}}
:hooks [leiningen.sassc]
:clean-targets ^{:protect false}
[:target-path "target/cljsbuild"]
:shadow-cljs
{:nrepl {:port 7002}
:builds
{:app
{:target :browser
:output-dir "target/cljsbuild/public/js"
:asset-path "/js"
:modules {:app {:entries [getfluentspanish.app]}}
:devtools {:watch-dir "resources/public"}}
:test
{:target :node-test
:output-to "target/test/test.js"
:autorun true}}}
:npm-deps [[core-js "^2.6.9"]
[shadow-cljs "2.8.31"]
[create-react-class "15.6.3"]
[react "16.8.6"]
[react-dom "16.8.6"]
[react-beautiful-dnd "11.0.4"]
[react-dnd "7.6.0"]
[react-dnd-html5-backend "7.6.0"]
[react-dnd-touch-backend "0.8.3"]
[react-dnd-multi-backend "3.2.1"]]
:profiles
{:uberjar {:omit-source true
:prep-tasks ["compile" ["shadow" "release" "app"]]
:aot :all
:uberjar-name "getfluentspanish.jar"
:source-paths ["env/prod/clj" "env/prod/cljs"]
:resource-paths ["env/prod/resources"]}
:dev [:project/dev :profiles/dev]
:test [:project/dev :project/test :profiles/test]
:project/dev {:jvm-opts ["-Dconf=dev-config.edn"]
:dependencies [[binaryage/devtools "0.9.10"]
[cider/piggieback "0.4.1"]
[expound "0.7.2"]
[pjstadig/humane-test-output "0.9.0"]
[prone "1.6.3"]
[re-frisk "0.5.4.1"]
[ring/ring-devel "1.7.1"]
[ring/ring-mock "0.4.0"]]
:plugins [[com.jakemccrary/lein-test-refresh "0.24.1"]]
:source-paths ["env/dev/clj" "env/dev/cljs" "test/cljs"]
:resource-paths ["env/dev/resources"]
:repl-options {:init-ns user}
:injections [(require 'pjstadig.humane-test-output)
(pjstadig.humane-test-output/activate!)]}
:project/test {:jvm-opts ["-Dconf=test-config.edn"]
:resource-paths ["env/test/resources"]}
:profiles/dev {}
:profiles/test {}})
EDIT 2 I ended up giving up on the git push heroku master
deployment approach and switched to Docker. I detailed the process in an answer below.
Upvotes: 2
Views: 775
Reputation: 160
I had the similar problem. In my case, the problematic package was 'pruned' by heroku-buildpack before compiling shadow-cljs. See heroku document.
I added following env variables and solved the problem.
NPM_CONFIG_PRODUCTION=false
YARN_PRODUCTION=false
Upvotes: 0
Reputation: 1166
I ended up giving up on Git-based Heroku deployment and instead reworked it to use Docker instead. Now it works, but the only downside is that I now have to locally build a new Docker image every time I want to deploy, which is slower than git push heroku master
followed by lein uberjar
on the remote. I can move this step to a CI server or something later on though and do builds and deploys via Github hooks, giving me a similar level of convenience.
I was actually really impressed with the simplicity of using Docker. The Dockerfile
I'm using is exceptionally trivial:
FROM openjdk:8-alpine
COPY target/uberjar/getfluentspanish.jar /getfluentspanish/app.jar
EXPOSE 3000
CMD ["java", "-jar", "/getfluentspanish/app.jar"]
I hope this helps anyone who is trying to use trying to deploy a full-stack Clojure & Clojurescript app to Heroku.
Upvotes: 1
Reputation: 3346
Is your project based on some framework (like Luminus)? Can you post your project.clj
file? That info would help others to understand what is going on.
Depending on your project configuration, running lein uberjar
could be doing different things. It's most common to have a Clojure backend where the uberjar
task does the compilation of Clojure and Java into bytecode and packaging the results into a JAR file. It's also possible that you have some alias that adds the steps to download dependencies from NPM and compilation of ClojureScript into assets that also are packaged as part of the JAR, but it's hard to tell without looking at the project.clj
file.
Upvotes: 0