Using Bower + Grunt in existing python projects


by
07 Jan
January 7, 2014

When I set out to integrate Bower and Grunt into our current Python projects I wasn’t looking to upset the balance, but instead improve the process for front-end development. Integrating these tools allows us to lighten our repository by moving third party dependencies to Bower, along with giving us easier access to build tools like Require.js and Sass. Ultimately these tools give us the ability to deploy optimized assets without the need to check them into git and manage changes to built files across branches.

File Structure and setup

With our current python file structure, we have a nice setup for integrating the new tools. There are a number of files that need to be added to utilize the new tools.

Bower

Setting up Bower is simple.

$ bower init

Answer the prompts to generate your bower.json.

After you have a bower.json you will need to create a .bowerrc to tell Bower where to install components.

In your favorite editor open .bowerrc and add the following.


This tells Bower to install its components into a bower_components folder under the subdirectory AppName. If you didn’t do this, your site would not have access to the installed components because they are outside the web server root.

Now you are free to install components.

$ bower install --save jquery

Grunt

Once you have bower you can start installing components, but you need to be able to build them. Enter Grunt.

Grunt is a task runner written in Javascript for Node.js. Grunt will allow us to write some tasks to do things like compile javascript with r.js, compile Sass with node-sass, start the development server, and actively watch files for changes.

We first need to create a package.json to outline what node packages we need.

We will be using a few grunt plugins to handle creating tasks for us.

  • node-sass – Implements Node bindings for libsass.
  • grunt-contrib-watch – tasks for watching files and executing tasks when changes occur.
  • grunt-sass – tasks for building Sass with node-sass.
  • grunt-contrib-requirejs – tasks for compiling javascript with requirejs and r.js.
  • load-grunt-tasks – automatically load grunt tasks from installed plugins.
  • time-grunt – display execution summary on end.

Once we have our package.json created and dependencies declared we can install those dependancies with one command.

$ npm install

After our dependancies are installed we can write our Gruntfile.

While there is a lot going on in this Gruntfile.js, there are a few key items to explain.

Running the server and building sass

We have a custom Grunt task called ‘runApp’ that will kick off our python server using the ‘run_appname.sh’ bash script.

The task starts by spawning a child process using Node’s built in child process tasks, and runs the ‘run_appname.sh’ script. We don’t need to pass any arguments to the script, but we do set the cwd (current working directory) with the options argument. Additionally we log out all events from the child process, stdout, stderr, data, and close.

To run the server in tandem with the watch task we have a combined task called ‘server’ that will first start the python server and second start the watch to rebuild JS and CSS files when they change.

Building and deploying

Since we are using javascript and Sass compilers there will inevitably come a time when we need to build those for deployment. With Grunt we have a task to do just that.

$ grunt build

This task is almost never necessary for development – ‘watch’ and ‘server’ should be used then. The ‘build’ task is mostly intended to be used on deployment. After the latest has been pulled from git ‘$ grunt build’ should be run to compile the latest css and javascript from source. This allows us to not only deliver small/optimized files, but now there is no need to commit compiled files to source control.

While this simplifies things a little it also adds some overhead to our server stack. In addition to needing Python we also need Node and NPM.

The typical deployment workflow would be as follows.

  1. fetch the latest from origin
  2. pull the latest code from the master branch
  3. activate the virtualenv and update/install any python packages
  4. install or update node packages
  5. install or update bower components
  6. build the latest css and js using grunt
  7. restart the service/application using supervisord

This is a simple deploy workflow, there could be more complex steps involved like tagging new and old releases, etc…

Python Configuration

Using Require.js and r.js to build our javascript for production means we will have two different files for development and production. On build r.js creates ‘main-built.js’ for production, we need a way to tell our views what environment version to use.

To configure the views to use either development or production versions of our files we use a ‘Config’ python class. Imported from ‘/AppName/common/config.py’ as ‘CONFIG’.

This sets up our configuration variables, of which we have a ‘js’ object.

The ‘CONFIG.js’ object contains two properties.

  • ‘main’ – this is the name of the main javascript file (main for un-built, and main-built for built)
  • ‘loader’ – the loader script to use (require.js, almond.js, etc AMD loader…)

The config.py values can be overridden for local development via ‘/AppName/common/local_config.py’.

The local config is never checked into version control, allowing everyone to configure the app for their environment without conflict.

View setup

In our route callback we return the ‘CONFIG.js’ data to the views as the ‘js’ property, along with any other view data.

In our view we output the correct loader and main file using the ‘js.loader’ and ‘js.main’ properties respectively.

Final thoughts

As we begin to use these tools on more projects I suspect more changes/improvements will happen. This first attempt was to simply integrate tools we are accustomed to using on other simpler projects into more advanced projects that utilize server languages. As we improve this integration I would like to see us attempt to follow this process with other languages like PHP, and frameworks like Magento.

Tags: , , , , , , ,
© Copyright 2017 Findaway. All rights reserved.