Heroku 上的 Clojure + Clojurescript 应用程序:部署应用程序时未安装 npm 包的依赖项
Clojure + Clojurescript app on Heroku: dependencies of npm packages not getting installed when deploying app
我正在使用 Clojure 后端和 Clojurescript 前端以及 React 和 Reagent 编写一个 Web 应用程序。我正在尝试将其托管在 Heroku 上。
当我在本地 运行 lein ubjerar
时,它构建良好并且应用程序运行。
然而,当我尝试通过 git push heroku master
将应用程序部署到 Heroku 时,我总是以这样的错误告终:
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)
尽管 object-assign
被列为 react
npm 包的依赖项,但我已经将其列为 :npm-deps
中的一项 project.clj
。我已经验证,当我在本地构建时,包安装正常(即在这种情况下 node_modules/object-assign
存在)。
当我在 :npm-deps
中手动添加 object-assign
作为显式依赖项时,我得到了同样的错误,但抱怨 different 缺少依赖项应该自动安装(并且在本地)。当我添加 new 缺少的依赖项时,它会抱怨另一个。当我使用新的显式依赖项在本地构建时,lein uberjar
工作但抱怨现在存在版本冲突,因为隐式依赖项通常固定在不同的版本。
知道如何解决这个问题吗?
编辑 这是我的 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 {}})
编辑 2 我最终放弃了 git push heroku master
部署方法,转而使用 Docker。我在下面的回答中详细说明了这个过程。
你的项目是基于某种框架(比如 Luminus)吗?你可以 post 你的 project.clj
文件吗?该信息将帮助其他人了解正在发生的事情。
根据您的项目配置,运行 lein uberjar
可能会做不同的事情。最常见的是有一个 Clojure 后端,其中 uberjar
任务将 Clojure 和 Java 编译成字节码并将结果打包到 JAR 文件中。也有可能你有一些别名,它添加了从 NPM 下载依赖项和将 ClojureScript 编译到资产中的步骤,这些资产也被打包为 JAR 的一部分,但如果不查看 project.clj
文件就很难分辨。
我最终放弃了 Git-based Heroku 部署,而是对其进行了重新设计以使用 Docker。现在它可以工作了,但唯一的缺点是我现在每次要部署时都必须在本地构建一个新的 Docker 映像,这比远程 git push heroku master
后跟 lein uberjar
慢.我可以将此步骤移至 CI 服务器或稍后的其他服务器,并通过 Github 挂钩进行构建和部署,为我提供类似级别的便利。
使用 Docker 的简单性给我留下了深刻的印象。我使用的 Dockerfile
非常简单:
FROM openjdk:8-alpine
COPY target/uberjar/getfluentspanish.jar /getfluentspanish/app.jar
EXPOSE 3000
CMD ["java", "-jar", "/getfluentspanish/app.jar"]
我希望这可以帮助任何试图将 full-stack Clojure & Clojurescript 应用程序部署到 Heroku 的人。
我遇到了类似的问题。在我的例子中,有问题的包是 heroku-buildpack 在编译 shadow-cljs 之前 'pruned'。参见 heroku document。
我添加了以下环境变量并解决了问题。
NPM_CONFIG_PRODUCTION=false
YARN_PRODUCTION=false
我正在使用 Clojure 后端和 Clojurescript 前端以及 React 和 Reagent 编写一个 Web 应用程序。我正在尝试将其托管在 Heroku 上。
当我在本地 运行 lein ubjerar
时,它构建良好并且应用程序运行。
然而,当我尝试通过 git push heroku master
将应用程序部署到 Heroku 时,我总是以这样的错误告终:
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)
尽管 object-assign
被列为 react
npm 包的依赖项,但我已经将其列为 :npm-deps
中的一项 project.clj
。我已经验证,当我在本地构建时,包安装正常(即在这种情况下 node_modules/object-assign
存在)。
当我在 :npm-deps
中手动添加 object-assign
作为显式依赖项时,我得到了同样的错误,但抱怨 different 缺少依赖项应该自动安装(并且在本地)。当我添加 new 缺少的依赖项时,它会抱怨另一个。当我使用新的显式依赖项在本地构建时,lein uberjar
工作但抱怨现在存在版本冲突,因为隐式依赖项通常固定在不同的版本。
知道如何解决这个问题吗?
编辑 这是我的 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 {}})
编辑 2 我最终放弃了 git push heroku master
部署方法,转而使用 Docker。我在下面的回答中详细说明了这个过程。
你的项目是基于某种框架(比如 Luminus)吗?你可以 post 你的 project.clj
文件吗?该信息将帮助其他人了解正在发生的事情。
根据您的项目配置,运行 lein uberjar
可能会做不同的事情。最常见的是有一个 Clojure 后端,其中 uberjar
任务将 Clojure 和 Java 编译成字节码并将结果打包到 JAR 文件中。也有可能你有一些别名,它添加了从 NPM 下载依赖项和将 ClojureScript 编译到资产中的步骤,这些资产也被打包为 JAR 的一部分,但如果不查看 project.clj
文件就很难分辨。
我最终放弃了 Git-based Heroku 部署,而是对其进行了重新设计以使用 Docker。现在它可以工作了,但唯一的缺点是我现在每次要部署时都必须在本地构建一个新的 Docker 映像,这比远程 git push heroku master
后跟 lein uberjar
慢.我可以将此步骤移至 CI 服务器或稍后的其他服务器,并通过 Github 挂钩进行构建和部署,为我提供类似级别的便利。
使用 Docker 的简单性给我留下了深刻的印象。我使用的 Dockerfile
非常简单:
FROM openjdk:8-alpine
COPY target/uberjar/getfluentspanish.jar /getfluentspanish/app.jar
EXPOSE 3000
CMD ["java", "-jar", "/getfluentspanish/app.jar"]
我希望这可以帮助任何试图将 full-stack Clojure & Clojurescript 应用程序部署到 Heroku 的人。
我遇到了类似的问题。在我的例子中,有问题的包是 heroku-buildpack 在编译 shadow-cljs 之前 'pruned'。参见 heroku document。
我添加了以下环境变量并解决了问题。
NPM_CONFIG_PRODUCTION=false
YARN_PRODUCTION=false