kittoframework / kitto

Kitto is a framework for interactive dashboards written in Elixir
http://kitto.io/dashboards/sample
MIT License
956 stars 58 forks source link

Heroku deployment not serving assets #77

Closed cforcey closed 7 years ago

cforcey commented 7 years ago

On a fresh install of kitto 0.3.1 using the installer, the heroku application properly serves the dashboard HTML but fails to find the assets in public/assets/ such as application.js and widgets.js. The precompile steps are all happening as expected, the files are viewable on the heroku file system, but the route is returning a 404 instead of picking up the public/assets serve static assets directive. There was a discussion on Gitter which I have saved off in this Gist (https://gist.github.com/cforcey/18dbd233c959984e6cff33faa04b58bd) which confirms fresh application installs pushed to Heroku have this issue.

Environment

±  |master ✓| → heroku run mix hex.info
Running mix hex.info on ⬢ voltmeter... up, run.3225 (Free)
Hex:    0.14.1
Elixir: 1.3.4
OTP:    19.1

Built with: Elixir 1.3.4 and OTP 18.3.4.4
±  |master ✓| → heroku run node -v
Running node -v on ⬢ voltmeter... up, run.8619 (Free)
v4.3.1
±  |master ✓| → heroku buildpacks
=== voltmeter Buildpack URLs
1. https://github.com/HashNuke/heroku-buildpack-elixir.git
2. heroku/nodejs

Current behavior

Heroku application shows dashboard HTML but fails to load assets. Logs:

2016-12-22T14:35:00.330075+00:00 heroku[web.1]: Starting process with command `mix kitto.server`
2016-12-22T14:35:03.171874+00:00 app[web.1]: 14:35:03.171 [info] Starting Kitto server, listening on 0.0.0.0:55531
2016-12-22T14:35:03.972705+00:00 heroku[web.1]: State changed from starting to up
2016-12-22T14:35:32.378744+00:00 app[web.1]: 14:35:32.378 [info] GET /dashboards/sample
2016-12-22T14:35:32.811838+00:00 heroku[router]: at=info method=GET path="/assets/widgets.js" host=voltmeter.herokuapp.com request_id=e9cd306a-1021-4f51-9f9f-2df08fdb144d fwd="73.218.169.138" dyno=web.1 connect=4ms service=14ms status=404 bytes=1279
2016-12-22T14:35:32.679364+00:00 heroku[router]: at=info method=GET path="/dashboards/sample" host=voltmeter.herokuapp.com request_id=a3a430ed-7ce2-4bb0-963e-f1cb30a8da0b fwd="73.218.169.138" dyno=web.1 connect=0ms service=363ms status=200 bytes=2437
2016-12-22T14:35:32.677326+00:00 app[web.1]: 14:35:32.676 [info] Sent 200 in 298ms
2016-12-22T14:35:32.736512+00:00 app[web.1]: 14:35:32.736 [info] GET /assets/application.js
2016-12-22T14:35:32.746774+00:00 app[web.1]: 14:35:32.746 [info] Sent 404 in 10ms
2016-12-22T14:35:32.806198+00:00 app[web.1]: 14:35:32.805 [info] GET /assets/widgets.js
2016-12-22T14:35:32.809887+00:00 app[web.1]: 14:35:32.809 [info] Sent 404 in 3ms

screen shot 2016-12-22 at 10 07 19 am

Sample applications:

Expected behavior

In kitto 0.2.x assets loaded on heroku as expected. Discussion on chat focused on this code but no solution was apparent: kittoframework/kitto@bd84aa0

cforcey commented 7 years ago

Full heroku deploy logs if it helps: https://gist.github.com/cforcey/0ebf3e68158e29b92335390cc2679b67

As always, I appreciate the time you took on Gitter (before coffee!) and the effort that has gone into this update and tool in general. And again, no rush on this issue for me as I am deploying this on vacation time as an elixir on heroku spike. Thanks!

cforcey commented 7 years ago

With @Zorbash's help, confirmed that production mode runs fine on a Mac OS X local install:

|ruby-2.1.3@cf-es-rails| Karmapa in ~/Dev/voltmeter
±  |master ✓| → MIX_ENV=prod mix compile
Compiling 1 file (.ex)
Generated voltmeter app

|ruby-2.1.3@cf-es-rails| Karmapa in ~/Dev/voltmeter
±  |master ✓| → npm run build

> voltmeter@0.0.1 build /Users/cforcey/Dev/voltmeter
> webpack --config webpack.config.js

Hash: 69d4d761121617469b74
Version: webpack 1.14.0
Time: 26517ms
                          Asset     Size  Chunks             Chunk Names
  fonts/fontawesome-webfont.eot   166 kB          [emitted]
fonts/fontawesome-webfont.woff2  77.2 kB          [emitted]
 fonts/fontawesome-webfont.woff    98 kB          [emitted]
  fonts/fontawesome-webfont.ttf   166 kB          [emitted]
 images/fontawesome-webfont.svg   444 kB          [emitted]
                 application.js   179 kB       0  [emitted]  application
                     widgets.js   518 kB       1  [emitted]  widgets
              application.js.gz    54 kB          [emitted]
                  widgets.js.gz   146 kB          [emitted]
   [0] multi widgets 112 bytes {1} [built]
    + 206 hidden modules

|ruby-2.1.3@cf-es-rails| Karmapa in ~/Dev/voltmeter
±  |master ✗| → MIX_ENV=prod PORT=4000 mix kitto.server
10:23:39.181 [info] Starting Kitto server, listening on 0.0.0.0:4000
10:23:44.172 [info] GET /dashboards/sample
10:23:44.396 [info] Sent 200 in 223ms
10:23:44.412 [info] GET /assets/application.js
10:23:44.415 [info] GET /assets/widgets.js
10:23:44.428 [info] Sent 200 in 15ms
10:23:44.428 [info] Sent 200 in 12ms
10:23:44.938 [info] GET /events
10:23:44.938 [info] Chunked 200 in 70µs
10:23:45.098 [info] GET /assets/fonts/fontawesome-webfont.woff2
10:23:45.098 [info] Sent 200 in 712µs

Local config:

±  |master ✗| → mix hex.info
Hex:    0.14.1
Elixir: 1.3.4
OTP:    19.1

Built with: Elixir 1.3.4 and OTP 18.3.4.4
±  |master ✗| → node -v
v6.6.0

Thanks @Zorbash for testing that path which puts the focus on heroku environment specifically and not the handling of static assets in general in prod.

davejlong commented 7 years ago

I have a theory that the following scenario is happening:

Plug.Static sets up the request to serve the static asset. The route for GET assets/*asset is picking up on the request and adding a 404 error to the connection, which triggers Plug.ErrorHandler to replace the response contents with the 404 template.

Am currently testing out the theory, but it only seems to occur on Heroku...

cforcey commented 7 years ago

Thanks @davejlong for testing that theory.

This is almost certainly irrelevant but there is what looks like a Heroku specific issue with an optional dependency node-zopfli on initial install or when always_rebuild=true is set in elixir_buildpack.config. The log is here from the error during a deploy:

https://gist.github.com/cforcey/10913ba1d05c50a1a261a203aa3c90a1

I do NOT think this is relevant as this is an optional compression library but I am looking for any differences with the heroku platform build.

cforcey commented 7 years ago

Also tested with node 6.6.0 on heroku which was only difference between prod on local and heroku environments but as expected build worked and behavior stayed the same with no assets being served on heroku.

"engines": {
    "node": "6.6.0",
    "npm": "4.0.5"
  },

Just trying to eliminate differences between working local production and non-working heroku production.

cforcey commented 7 years ago

Not sure if this is interesting -- we form the Asset path with Kitto.root, but on heroku this is set to "/app". Here is an iex session on heroku:

|ruby-2.1.3@cf-es-rails| Karmapa in ~/Dev/voltmeter
±  |master ✗| → heroku run iex -S mix kitto.server
Running iex -S mix kitto.server on ⬢ voltmeter... up, run.7286 (Free)
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]

16:33:53.758 [info] Starting Kitto server, listening on 0.0.0.0:8329
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Mix.env
:prod
iex(2)> Mix.env == :prod
true
iex(3)> Application.get_env(:kitto, :serve_assets?, true)
true
iex(7)> Kitto.root
"/app"
iex(8)> Path.join [Kitto.root, "public", "assets"]
"/app/public/assets"
cforcey commented 7 years ago

Documenting that this is the correct asset path as far as heroku is concerned:

|ruby-2.1.3@cf-es-rails| Karmapa in ~/Dev/voltmeter
±  |master ✗| → heroku run "cd public/assets && pwd && ls -al"
Running cd public/assets && pwd && ls -al on ⬢ voltmeter... up, run.8920 (Free)
/app/public/assets
total 904
drwx------ 4 u14057 dyno   4096 Dec 22 16:54 .
drwx------ 3 u14057 dyno   4096 Dec 22 16:51 ..
-rw------- 1 u14057 dyno 178666 Dec 22 16:54 application.js
-rw------- 1 u14057 dyno  54020 Dec 22 16:54 application.js.gz
-rw------- 1 u14057 dyno    318 Dec 22 16:51 favicon.ico
drwx------ 2 u14057 dyno   4096 Dec 22 16:54 fonts
drwx------ 2 u14057 dyno   4096 Dec 22 16:54 images
-rw------- 1 u14057 dyno 518410 Dec 22 16:54 widgets.js
-rw------- 1 u14057 dyno 146148 Dec 22 16:54 widgets.js.gz
cforcey commented 7 years ago

In Gitter chat session with @davejlong and @Zorbash, issue was narrowed down to this commit which swapped the old relative address that was better for heroku — plug Plug.Static, at: "assets", gzip: true, from: Path.join ["public", “assets”] for an absolute path which despite being well formed as /app/public/assets was not valid for serving a static file on heroku (works great locally): kittoframework/kitto@7ec1c22.

Trick is to support both umbrella app case and heroku with relative paths. @Zorbash is working on a solution to cover all cases.