Problem Summary
I recently tried to deploy my local application to Heroku. It is built using flex and the React / Redux interface. After working with Heroku subtleties (procfiles, where he reads package.json, etc.), I can get a backend to show (for example: the flask-admin section works the same as my database) m is still not can reach part of the interface (reaction) of my site. There are no errors that I can detect in Heroku logs, and in the local version my application works fine when starting my python server and NPM start in a static directory.
Any idea why the external interface will not display or how to get it?
Logs:
I deleted some confidential information from the details, but here is what heroku logs --tail gives me when I try to update the application.
2018-02-11T01:18:01.000000+00:00 app[api]: Build succeeded 2018-02-11T01:21:12.305017+00:00 heroku[web.1]: Starting process with command `gunicorn main:app` 2018-02-11T01:21:16.374150+00:00 heroku[web.1]: State changed from starting to up 2018-02-11T01:21:15.948707+00:00 app[web.1]: [2018-02-11 01:21:15 +0000] [4] [INFO] Starting gunicorn 19.6.0 2018-02-11T01:21:15.949430+00:00 app[web.1]: [2018-02-11 01:21:15 +0000] [4] [INFO] Listening at: http://0.0.0.0:29162 2018-02-11T01:21:19.278810+00:00 heroku[router]: at=info method=GET path="/" host=removed.herokuapp.com request_id=bd74ea4c-3e3a-403b-8850-198b7dec20e2 fwd="104.152.1.62" dyno=web.1 connect=1ms service=2146ms status=200 bytes=2895 protocol=https 2018-02-11T01:21:19.650759+00:00 heroku[router]: at=info method=GET path="/dist/bundle.css" host=removed.herokuapp.com request_id=48183249-fb12-4c7a-9a53-2a57ab58d89b fwd="104.152.1.62" dyno=web.1 connect=0ms service=3ms status=200 bytes=2895 protocol=https 2018-02-11T01:21:19.816113+00:00 heroku[router]: at=info method=GET path="/dist/bundle.js" host=removed.herokuapp.com request_id=1c8b258b-4187-4df6-af35-784e62fb97e5 fwd="104.152.1.62" dyno=web.1 connect=1ms service=3ms status=200 bytes=2895 protocol=https
When I look at the source code, I see the code from my index.html file (in the static folder, which is correct), and this particular line is highlighted in red, which makes me think that it does not have bundle.js? <script src="/dist/bundle.js"></script>
When I try to visit the front end, none of them are displayed. I'm really not sure where to look next.
Initial Ideas:
Does my server.js file really start with Heroku? I have a "start" script in my .json package, but if procfile runs Python ... does it really start? How to run it without putting it in postinstall or post-build?
Is webpack something messing around. My production website is slightly different from my development, so maybe during build time: is production getting spoiled? However, this does not explain why the assembly always succeeds.
Is my server.js or webpack forcing formatting in index.html and therefore it cannot parse correctly? Perhaps that is why I see SyntaxError: expected expression, got '< bundle.js:1 in the console?
Update: I received a note from Heroku Support that informed me that Heroku speakers were not designed to support applications with multiple processes, such as mine. As a result, they feel that I will need to make some significant changes so that all HTTP requests are forwarded through one dyno. Perhaps this is my problem?
Here are the files that can help debug:
Server.js
const http = require('http'); const express = require('express'); const httpProxy = require('http-proxy'); const path = require('path'); const apiPort = process.env.PORT || 8081; const proxy = httpProxy.createProxyServer({}); const app = express(); app.use(require('morgan')('short')); (function initWebpack() { const webpack = require('webpack'); const webpackConfig = require('./webpack/common.config'); const compiler = webpack(webpackConfig); app.use(require('webpack-dev-middleware')(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath, })); app.use(require('webpack-hot-middleware')(compiler, { log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000, })); app.use(express.static(path.join(__dirname, '/'))); }()); app.all(/^\/api\/(.*)/, (req, res) => { proxy.web(req, res, { target: `http://0.0.0.0:${apiPort}` }); }); app.get(/.*/, (req, res) => { res.sendFile(path.join(__dirname, '/index.html')); }); const server = http.createServer(app); server.listen(process.env.PORT || 8080, () => { const address = server.address(); console.log('Listening on: %j', address); console.log(' -> that probably means: http://0.0.0.0:%d', address.port); });
File structure
ROOT βββ/application β βββ models.py β βββ app.py βββ/static β βββ/bin β βββ/dist β β βββbundle.js β βββ/node_modules β βββ/src β β βββ/actions β β βββ/components β β β βββ/examplecomponenthere β β β β βββindex.js (for example component) β β βββ/constants β β βββ/containers β β βββ/reducers β β βββ/store β β βββ/webpack β βββindex.html β βββpackage.json (the true one) β βββserver.js βββ/tests βββconfig.py βββindex.py βββmain.py βββpackage.json (one to help heroku start) βββprocfile βββrequirements.txt. βββsetup.py βββtests.py
Package.json at the root This file exists because I am running a multi-line build. Heroku doesn't seem to recognize package.json in statics until I use this to drag it in there.
{ "name": "rmmd", "version": "0.0.1", "engines": { "node": "6.11.1", "npm": "3.10.10" }, "scripts": { "start": "node static/bin/server.js", "heroku-postbuild": "cd static && npm install && npm run build:production" } }
Package.json in static
{ "name": "redux-easy-boilerplate", "version": "1.3.3", "description": "", "scripts": { "clean": "rimraf dist", "build": "webpack --progress --verbose --colors --display-error-details --config webpack/common.config.js", "build:production": "npm run clean && npm run build", "lint": "eslint src", "start": "node bin/server.js", "test": "karma start" }, "repository": { "type": "git", "url": "" }, "keywords": [ "react", "reactjs", "boilerplate", "redux", "hot", "reload", "hmr", "live", "edit", "webpack" ], "author": "https://github.com/anorudes, https://github.com/keske", "license": "MIT", "devDependencies": { "webpack-dev-middleware": "^1.5.0", "webpack-dev-server": "^1.14.1", "webpack-hot-middleware": "^2.6.0", }, "dependencies": { "ant-design-pro": "^0.3.1", "antd": "^3.0.0", "lodash": "^4.17.4", "prop-types": "^15.6.0", "react-bootstrap": "^0.31.0", "redux-devtools-extension": "^2.13.2", "autoprefixer": "6.5.3", "axios": "^0.15.3", "babel-core": "^6.4.5", "babel-eslint": "^7.1.1", "babel-loader": "^6.2.1", "babel-plugin-import": "^1.2.1", "babel-plugin-react-transform": "^2.0.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-polyfill": "^6.3.14", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", "babel-preset-react-hmre": "^1.0.1", "babel-preset-stage-0": "^6.3.13", "bootstrap": "^3.3.5", "bootstrap-loader": "^1.2.0-beta.1", "bootstrap-sass": "^3.3.6", "bootstrap-webpack": "0.0.5", "classnames": "^2.2.3", "css-loader": "^0.26.4", "csswring": "^5.1.0", "deep-equal": "^1.0.1", "eslint": "^3.4.0", "eslint-config-airbnb": "13.0.0", "eslint-plugin-import": "^2.2.0", "eslint-plugin-jsx-a11y": "^3.0.1", "eslint-plugin-react": "^6.1.2", "expect": "^1.13.4", "exports-loader": "^0.6.2", "expose-loader": "^0.7.1", "express": "^4.13.4", "express-open-in-editor": "^1.1.0", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "gapi": "0.0.3", "history": "^4.4.1", "http-proxy": "^1.12.0", "imports-loader": "^0.6.5", "jasmine-core": "^2.4.1", "jquery": "^3.1.0", "jwt-decode": "^2.1.0", "karma": "^1.2.0", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", "karma-webpack": "^1.7.0", "less": "^2.7.2", "less-loader": "^2.2.3", "lodash": "^4.5.1", "material-ui": "^0.16.4", "mocha": "^3.0.2", "morgan": "^1.6.1", "node-sass": "^3.4.2", "postcss-import": "^9.0.0", "postcss-loader": "^1.1.1", "q": "^1.4.1", "qs": "^6.1.0", "rc-datepicker": "^4.0.1", "react": "^15.3.1", "react-addons-css-transition-group": "^15.3.1", "react-bootstrap": "^0.31.0", "react-calendar-component": "^1.0.0", "react-date-picker": "^5.3.28", "react-datepicker": "^0.37.0", "react-document-meta": "^2.0.0-rc2", "react-dom": "^15.1.0", "react-forms": "^2.0.0-beta33", "react-hot-loader": "^1.3.0", "react-loading-order-with-animation": "^1.0.0", "react-onclickoutside": "^5.3.3", "react-redux": "^4.3.0", "react-router": "3.0.0", "react-router-redux": "^4.0.0", "react-tap-event-plugin": "^2.0.1", "react-transform-hmr": "^1.0.1", "redux": "^3.2.1", "redux-form": "^6.0.1", "redux-logger": "2.7.4", "redux-thunk": "^2.1.0", "resolve-url-loader": "^1.4.3", "rimraf": "^2.5.0", "sass-loader": "^4.0.0", "style-loader": "^0.13.0", "url-loader": "^0.5.7", "webpack": "^1.12.11", "webpack-merge": "^1.0.2", "yargs": "^6.5.0" } }
Webpack prod
const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { devtool: 'source-map', entry: ['bootstrap-loader/extractStyles', './src/index'], output: { publicPath: '/dist/', }, module: { loaders: [ { test: /\.scss$/, loader: 'style!css!postcss-loader!sass', }, ], }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"', }, __DEVELOPMENT__: false, }), new ExtractTextPlugin('bundle.css'), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, }), ], };
General web package
const path = require('path'); const autoprefixer = require('autoprefixer'); const postcssImport = require('postcss-import'); const merge = require('webpack-merge'); const development = require('./dev.config'); const production = require('./prod.config'); require('babel-polyfill').default; const TARGET = process.env.npm_lifecycle_event; const PATHS = { app: path.join(__dirname, '../src'), build: path.join(__dirname, '../dist'), }; process.env.BABEL_ENV = TARGET; const common = { entry: [ PATHS.app, ], output: { path: PATHS.build, filename: 'bundle.js', }, resolve: { extensions: ['', '.jsx', '.js', '.json', '.scss'], modulesDirectories: ['node_modules', PATHS.app], }, module: { loaders: [{ test: /bootstrap-sass\/assets\/javascripts\//, loader: 'imports?jQuery=jquery', }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff', }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff2', }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream', }, { test: /\.otf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-otf', }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file', }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml', }, { test: /\.js$/, loaders: ['babel-loader'], exclude: /node_modules/, }, { test: /\.css$/, include: /node_modules/, loaders: ['style-loader', 'css-loader'], }, { test: /\.png$/, loader: 'file?name=[name].[ext]', }, { test: /\.jpg$/, loader: 'file?name=[name].[ext]', }], }, postcss: (webpack) => ( [ autoprefixer({ browsers: ['last 2 versions'], }) ] ), }; if (TARGET === 'start' || !TARGET) { module.exports = merge(development, common); } if (TARGET === 'build' || !TARGET) { module.exports = merge(production, common); }
PROCFILE
web: gunicorn main:app
Buildpacks in Heroku
heroku buildpacks:set heroku/python heroku buildpacks:add heroku/nodejs