How to Deploy an Angular 2/Rails 5 App to Heroku
Initializing the App
The first step will be to initialize a new Rails 5 project. I’m calling minerails_5_angular_2_deployment_example
.For the front-end we’ll use angular2-seed. Clone the repo into a subdirectory called
client
. (The name client
, in this case, is important. If you call it something else, the deployment won’t work.)We’re just interested in the angular2-seed code. If we keep it as a repository inside our project’s repository, there will be problems. Kill
client/.git
.Now install the Node packages.
client/dist/prod
.
If we want Rails to be able to serve something out of there, we’ll have
to tell Rails about it somehow. We can do this by simply symlinking public/
to client/dist/prod/
. Rails will look for public/index.html
and find client/dist/prod/index.html
and we’ll be in business.localhost:3000
you should see “Howdy! Here’s a list of awesome computer scientists…”Creating the Heroku App
The first step is somewhat self-explanatory:heroku/ruby
buildpack. We also need a Node buildpack
because in order to build our front-end app we need to run a Gulp
command and in order to run Gulp commands we need to have Node packages
installed.We can tell Heroku about our two buildpacks like this:
The reason we’re using my
https://github.com/jasonswett/heroku-buildpack-nodejs
buildpack instead of the Heroku version is that I needed to modify the buildpack to look for package.json
inside of the client
directory instead of at the project root.We need to do one last thing before we can push our code. Modify
client/package.json
to look like this:{
"name": "angular2-seed",
"version": "0.0.0",
"description": "Seed for Angular 2 apps",
"repository": {
"url": "https://github.com/mgechev/angular2-seed"
},
"scripts": {
"build.dev": "gulp build.dev --color",
"build.dev.watch": "gulp build.dev.watch --color",
"build.e2e": "gulp build.e2e --color",
"build.prod": "gulp build.prod --color",
"build.test": "gulp build.test --color",
"build.test.watch": "gulp build.test.watch --color",
"docs": "npm run gulp -- build.docs --color && npm run gulp -- serve.docs --color",
"e2e": "protractor",
"e2e.live": "protractor --elementExplorer",
"gulp": "gulp",
"karma": "karma",
"karma.start": "karma start",
"postinstall": "typings install && gulp check.versions && npm prune && gulp build.prod",
"reinstall": "npm cache clean && npm install",
"serve.coverage": "remap-istanbul -b src/ -i coverage/coverage-final.json -o coverage -t html && npm run gulp -- serve.coverage --color",
"serve.dev": "gulp serve.dev --color",
"serve.e2e": "gulp serve.e2e --color",
"serve.prod": "gulp serve.prod --color",
"start": "gulp serve.dev --color",
"tasks.list": "gulp --tasks-simple --color",
"test": "gulp test --color",
"webdriver-start": "webdriver-manager start",
"webdriver-update": "webdriver-manager update"
},
"author": "Minko Gechev <mgechev>",
"license": "MIT",
"devDependencies": {
},
"dependencies": {
"angular2": "2.0.0-beta.15",
"es6-module-loader": "^0.17.8",
"es6-promise": "^3.1.2",
"es6-shim": "0.35.0",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.2",
"systemjs": "~0.19.25",
"zone.js": "^0.6.10",
"async": "^1.4.2",
"autoprefixer": "^6.3.3",
"browser-sync": "^2.11.2",
"chalk": "^1.1.3",
"codelyzer": "0.0.12",
"colorguard": "^1.1.1",
"connect": "^3.4.1",
"connect-history-api-fallback": "^1.1.0",
"connect-livereload": "^0.5.3",
"cssnano": "^3.5.2",
"doiuse": "^2.3.0",
"event-stream": "^3.3.2",
"express": "~4.13.1",
"express-history-api-fallback": "^2.0.0",
"extend": "^3.0.0",
"gulp": "^3.9.1",
"gulp-cached": "^1.1.0",
"gulp-concat": "^2.6.0",
"gulp-filter": "^4.0.0",
"gulp-inject": "^4.0.0",
"gulp-inline-ng2-template": "^1.1.2",
"gulp-load-plugins": "^1.2.0",
"gulp-plumber": "~1.1.0",
"gulp-postcss": "^6.1.0",
"gulp-shell": "~0.5.2",
"gulp-sourcemaps": "git+https://github.com/floridoo/gulp-sourcemaps.git#master",
"gulp-template": "^3.1.0",
"gulp-tslint": "^4.3.3",
"gulp-typedoc": "^1.2.1",
"gulp-typescript": "~2.12.1",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"gulp-watch": "^4.3.5",
"is-ci": "^1.0.8",
"isstream": "^0.1.2",
"jasmine-core": "~2.4.1",
"jasmine-spec-reporter": "^2.4.0",
"karma": "~0.13.22",
"karma-chrome-launcher": "~0.2.2",
"karma-coverage": "^0.5.5",
"karma-ie-launcher": "^0.2.0",
"karma-jasmine": "~0.3.8",
"karma-mocha-reporter": "^2.0.0",
"karma-phantomjs-launcher": "^1.0.0",
"merge-stream": "^1.0.0",
"open": "0.0.5",
"phantomjs-prebuilt": "^2.1.4",
"postcss-reporter": "^1.3.3",
"protractor": "^3.0.0",
"remap-istanbul": "git+https://github.com/SitePen/remap-istanbul.git#master",
"rimraf": "^2.5.2",
"run-sequence": "^1.1.0",
"semver": "^5.1.0",
"serve-static": "^1.10.2",
"slash": "~1.0.0",
"stream-series": "^0.1.1",
"stylelint": "^5.3.0",
"stylelint-config-standard": "^5.0.0",
"systemjs-builder": "^0.15.14",
"tiny-lr": "^0.2.1",
"traceur": "^0.0.91",
"ts-node": "^0.7.1",
"tslint": "^3.7.0-dev.2",
"tslint-stylish": "2.1.0-beta",
"typedoc": "^0.3.12",
"typescript": "~1.8.10",
"typings": "^0.7.12",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"yargs": "^4.2.0"
}
}
gulp build.prod
to the postinstall
script. This will force Heroku to do a build as part of the deployment process. Second, we move everything from devDependencies
to dependencies
. Since it’s a production environment, Node won’t pick up the devDependencies
, but we need those.
By the way, why not just build locally, commit the generated code, and push that? You could do that and it would work. The reason I didn’t want to is that it’s not a good idea to commit build artifacts to version control. You end up with a bunch of “changed” files every time you do a build, which not only doesn’t make sense but serves as a distraction. I’ve worked on projects before that commit build artifacts to version control and it has been painful.
After all our changes are committed we can do a push:
Now open the app in Heroku.
Comments
Post a Comment