Continuous Integration with grunt, node and yeoman angular-fullstack

In case you use the excellent angular-fullstack generator, have build your app and now are ready to deploy you will most likely face certain obstacles:

  • How to build with grunt and overcome certain bugs in the Gruntfile.js not yet fixed by the angular-fullstack team?
  • How to push to server and what Web server to use?
  • How to make node run forever?
  • How to continuously integrate?

Fix angular-fullstack

Given that you are using the Gruntfile.js provided by angular fullstack we will edit it and fix the bug I discovered. Change to your project root and type:

$ sudo emacs Gruntfile.js

Fix Font-Awesome Bug

Insert these lines in order to fix Font Awesome and the app.css bug:

In addition to above changes, go into your `app.css` and change all lines like this; instead of the bower_components directory: src: url('../assets/fonts/fontawesome-webfont.eot?v=4.1.0'); ## Fig Google Webfont Bug The Build ignores Google font paths. THus go to the top of your bootstrap file, and look for any google web font imports, e.g. at the top of the file: **/bootstrap_components/bootswatch-dist/css/bootstrap.css** We see a @import url("//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,400italic"); Minify tasks inside of our grunt task runner will effectively destroy this (at least from my troubleshooting findings I would call it that). In order to fix this bug, we have to hard code the font into the index.html. Yes its ugly,but its a quick fix, so stop complaining. Put this in the top of your index.html file, right after the meta tags and outside the yeoman template comments: .... ## Fix imagemin Bug Now you might hit another bug, especially when using a Mac and grunt to build the project: `Warning: Running "imagemin:dist" (imagemin) task` In order to workaround this bug you usually would: $ brew install libpng then rebuild the imagemin grunt plugin with `$ npm install grunt-contrib-imagemin` Now, when building with `grunt build:dist` you are still getting the error about imagemin, try getting the latest MacOSX binary from the git repo with: git clone https://github.com/imagemin/pngquant-bin.git sudo cp pngquant-bin/vendor/osx/pngquant /usr/local/bin/ sudo chmod +x /usr/local/bin/pngquant Now rebuild imagemin with npm and the error message should be gone. Further troubleshooting can be found on [GruntJS/GitHub](http://cur.lv/u7o4k). # Install nginx and proxy forward to node.js How-to do this on Linux is described by Digital Ocean, I couldn't write the tutorial any better, so here is the link: [Digital Oceans How-To](http://cur.lv/u7o3z). On a Mac simply do: $ ALL_PROXY=socks5://proxy:port brew install nginx You can leave the proxy part out in case you are lucky and not behind a Nazi wall. ## Start/Stop nginx on a Mac $ sudo nginx $ sudo nginx -s stop ## Edit nginx.conf on a Mac $ sudo emacs /usr/local/etc/nginx/nginx.conf # Routing requests to static content In case you are using HTML5 mode in angular like so: $locationProvider.html5Mode(true); because you have decided to get rid of the ugly `/#/` angular URL routing, you will run into problems with Express.js (at the lastest when deploying). Suddenly there is a need to re-wire requests to static content that are controlled by angular.js routing. Therefore we add in our `router.js` the appropiate re-routings as so: If you know a better solution, I would be delighted to hear about it in the comments section. # Build Project locally $ grunt build:dist When you are seeing no more errors, you can proceed to starting up with **node.js**. # Start with node.js $ export PORT=9000; export NODE_ENV=production; node dist/server/app.js Work through the 404's by following the advise given in above chapter "Routing requests to static content" until they are gone completely. Make sure to edit the `server/app.js`inside the dist directory. There might be one final error message from **node.js** Warning: connect.session() MemoryStore is not designed for a production environment, as it will leak memory, and will not scale past a single process. To get rid of that error, node.js needs to change internal workings. I have no solution for that so far. Comments welcome. You then should end up with a complete, clean build, which is ready to deploy. This is what you should see after a successfull grunt build: grunt-build-img # Deploy ## Pushing dist to server There is no need to check out the whole project and build on the production server; at least usually. So there are multiple approaches on how to deploy, either you could use sftp, rsync, git or maybe bamboo. ## Deploy with git Make sure you don't have dist inside your `.gitignore`, commit and push your project to your remote repo, then check it out on your server in a seperate folder. Then copy the contents of the dist folder to your `/var/www/project`. Make sure to remove any remainders of an old build before copying over. In essence this would look like this: $ sudo rm -rf /var/www/project/{client,public} $ cp -r /var/www/project.checkedout/dist/* /var/www/project/ $ cd /var/www/project/; sudo npm install && bower install This could go in a script or maybe there is a better solution, which as always I would like to know about in the comments. ## Git Sparse Checkout *Using git sparse checkout* $ git init $ cd $ git remote add -f origin $ git config core.sparseCheckout true $ echo "some/dir/" >> .git/info/sparse-checkout $ echo "another/sub/tree" >> .git/info/sparse-checkout ## Putting it all together From above deployment snips I created a bash script, but I am thinking of using rsync for deployment in future, git is definitely not the deployment method I would recommend. #!/bin/bash set -e cd /home/user/projekt git config core.sparseCheckout true git pull origin master rm -rf /var/www/project/{client,public} sudo cp -r /home/user/project-checkout/project/dist/* /var/www/project/ cd /var/www/project/ sudo npm install `set -e` ensure the script to stop if something throws an error. Run above script after `chmod 777 deploy-project.sh` with `./deploy-project.sh`. Also be aware that using sudo within a bash script is bad practice. Also see: https://gist.github.com/isaacs/579814 # Continous Integration ## GitHub Flow

  • Anything in the master branch is deployable
  • To work on something new, create a descriptively named branch off of master (ie: new-oauth2-scopes)
  • Commit to that branch locally and regularly push your work to the same named branch on the server
  • When you need feedback or help, or you think the branch is ready for merging, open a pull request
  • After someone else has reviewed and signed off on the feature, you can merge it into master
  • Once it is merged and pushed to ‘master’, you can and should deploy immediately

Deploy via Grunt

This what I use, go into grunt and define:

Of course you need to install sshexec via npm as well.

Deploy to staging with hubot

Hubot does not support proxy servers and will not be supported by this tutorial. In case you are WFH, you can:

$ npm install -g yo generator-hubot
$ hubot depoy github to production

Then follow: Build by github: https://hubot.github.com/

Deploy to staging with Bamboo

  • grunt build. No bower install is needed because we check-in our bower_components. The grunt build step compiles, concatenates, and minifies code and assets. It also takes the unusual step of cache-busting the asset filenames and rewriting any references in view files to point to the new filenames.

  • The resulting artifact is saved in Bamboo

  • Tests, Reviews, Deploy

Control node.js with pm2

Usually you would start the app with node this way:

$ export NODE_ENV=production; export PORT=9000; node /var/www/adfraud/server/app.js

However this is far from ideal and you have no clue how much space, memory and cpu-time your app is eating up. Therefore let's employ pm2 to monitor the app:

Start Application

The first thing you will want to do is use the pm2 start command to run your application, hello.js, in the background:

pm2 start app.js --name="teapot"

This also adds your application to PM2's process list, which is outputted every time you start an application:

pm2

As you can see, PM2 automatically assigns an App name (based on the filename, without the .js extension) and a PM2 id. PM2 also maintains other information, such as the PID of the process, its current status, and memory usage.

Applications that are running under PM2 will be restarted automatically if the application crashes or is killed, but an additional step needs to be taken to get the application to launch on system startup (boot or reboot). Luckily, PM2 provides an easy way to do this, the startup subcommand.

The startup subcommand generates and configures a startup script to launch PM2 and its managed processes on server boots. You must also specify the platform you are running on, which is ubuntu, in our case:

pm2 startup ubuntu

You then should see:

[PM2] You have to run this command as root
[PM2] Execute the following command :
...

Follow the instructions and all will be fine.

pm2 sample commands

pm2 stop example
pm2 restart example
pm2 list
pm2 info example
pm2 monit

Finally you should be able to reach your project on your server from your browser and all should be well.

I hope you enjoyed this article - it was quite some work. Have a productive day!

Sources

Scott Chacon: GitHub Flow

Build and Deploy with Grunt, Bamboo, and Elastic Beanstalk