Build automation is quite handy, such as in this scenario I ran into at work. A few months ago I had written a framework to kick-start a project, weeks/months pass and some issues at the tail-end of the project brought me back in to trouble-shoot. A few issues were identified with one of them being IE8-specific. It turns out that IE8 will throw mixed content warnings for paths that use relative URLs over SSL which means that all resource paths needed to be updated . That issue (among others) made me decide to add build automation to the project so that the team could work in our dev environment with relative resource paths and when needed automate the changing of all dev relative paths to absolute production paths.

A by-product of this was that the project needed to be reorganized and added to source control. Incidentally I had created a repo for this all those months ago but for some reason the new devs didn’t bother to continue with it…. (source control is your friend)

Ref 1, Ref 2

The tasks

These build tasks needed to be accomplished:

  • “Clean” the build folder prior to starting the build process
  • Copy all files to the build folder
  • Change all URL’s in relevant HTML files to the absolute URL’s of the target environment
  • Change all URL’s in all CSS files to the absolute URL’s of the target environment
  • Change the values of “dev mode” variables within the JS
  • Execute each step synchronously

Project Reorganization

The project needed to be reorganized so this is what I came up with:

  • my_project/src/
  • my_project/build/
  • my_project/.gitignore <<< will be used to tell git to ignore the "build" & "node_modules" folders
  • my_project/gulpfile.js <<< your gulp file goes here in your workspace root
  • my_project/package.json <<< contains your NPM dependencies

To accomplish the above – besides just creating directories – I inited the project for source control:

$ git init

Next I inited NPM and followed the prompts that created my package.json file:

$ npm init

Of course, Gulp needs to be installed:

$ npm install –save-dev gulp

Not all of the files that were just created need to be source-controlled – by creating a “.gitignore” file and adding the paths to be excluded Git will know which files to monitor and which ones to ingore:

  • /node_modules/
  • /build/

As you can see I don’t want Git to track the “node_modules” directory nor do I want it to track the build directory. For the later it doesn’t make any sense to track it since I can run a Gulp build command to build it on the fly.

Finally I did the initial commit:

$ git add .
$ git commit -m ‘initial commit’

“Clean” the build folder

The first thing we will need to do is to clear out the build folder to ensure that it contains only production-ready files. For this I used the del node module.

To install via your console (assuming that you have NPM installed) run this command:

$ npm install –save-dev gulp del

Next add this to your Gulp file:

// all gulp files start with this line
var gulp = require('gulp');

// and now a reference to the del module
var del = require('del');

Next create the Gulp task:

gulp.task('clean:build', function () {
  return del(['build/**/*']);
});

This task will empty the “build” folder – necessary so that you are assured that files that are no longer part of the build are removed. The name of this Gulp task is “clean:build”.

Copy all files to the build folder

This step will copy everything over to the build folder – while its true some of them still need to be edited for production the tasks that follow will take care of them. For the time being we need to get everything else so a wholesale copy accomplishes that goal.

To copy files into the build folder I used Gulp’s built-in file streaming and dest method. That task looked like this:

gulp.task('build-copy-all',function(){
   return gulp.src('./src/**/*')
      .pipe(gulp.dest('./build'));
});

Change URL’s

As I mentioned the target environment uses SSL and due to the requirement that we be compatible with IE8 we were required to use only absolute paths in our files in order to avoid IE8’s quirkiness. I chose to break this into three tasks, the first was to update the index.html.

First, install gulp-assetpaths:

$ npm install –save-dev gulp-assetpaths

Next create a reference to it in the gulpfile:

var assetpaths = require('gulp-assetpaths');

And then as before create the task:

gulp.task('build-html',function(){
   return gulp.src(['./src/*.html'])
      .pipe(assetpaths({
         newDomain: 'https://www.some-domain-here.com/content/some-path',
         oldDomain : 'https://test.some-test-domain.com',
         docRoot : '',
         filetypes : ['js','css'],
         templates: false
      }))
      .pipe(gulp.dest('./build/'));
});

This task looks for paths referencing javascript and CSS files and updates the file paths.

Next, I created an almost identical task to update our template HTML files:

gulp.task('build-templates',function(){
   return gulp.src(['./src/templates/*.html'])
      .pipe(assetpaths({
         newDomain: 'https://www.some-domain-here.com/content/some-path',
         oldDomain : 'https://test.some-test-domain.com',
         docRoot : '',
         filetypes : ['js','css'],
         templates: false
      }))
      .pipe(gulp.dest('./build/templates/'));
});

Next the CSS contains paths to fonts, images, etc so it too needs to be parsed to have the proper paths inserted. Note below that I specified which file types needed to have their paths inspected:

gulp.task('build-css',function(){
   return gulp.src(['./src/styles/*.css'])
      .pipe(assetpaths({
         newDomain: 'https://www.some-domain-here.com/content/some-path',
         oldDomain : 'https://test.some-test-domain.com',
         docRoot : '',
         filetypes : ['gif','jpg','png','eot','woff','ttf','svg'],
         templates: false
      }))
      .pipe(gulp.dest('./build/styles/'));
});

Change code

One of the developers created a “dev mode” flag within the index.html – actually two flags – which needed to be flipped in order for the project to run in “production mode”.

To do this I used gulp-replace. To install the plugin:

$ npm install –save-dev gulp-replace

Next, as always, create a reference to the plugin in your gulpfile:

var replace = require('gulp-replace');

And then create the task:

gulp.task('toggleDevMode', function(){
  gulp.src(['./build/index.html'])
    .pipe(replace('devMode = true', 'devMode = false'))
 	.pipe(replace('isScorm = false', 'isScorm = true'))
    .pipe(gulp.dest('./build'));
});

You can see that I am literally looking for the variable name and its value and then changing its value where the string on the left is replace by the string on the right. Also note that I’m not pulling from the “src” folder but instead am pulling from “build”. The reason for this is that if I do pull from “src” then I would be overwriting the edits that have already taken place to the index.html, so therefore I need to take the already edited file from build, update it and then overwrite the build version.

Execute Tasks Sequentially

In order to avoid race conditions I needed to run each task in a synchronous fashion. For that the handy run-sequence plugin.

$ npm install –save-dev run-sequence

var runSequence = require('run-sequence');

The task called “build”, shown below, calls each of my build tasks in the order indicated:

gulp.task('build', function() {
   runSequence('clean:build','build-copy-all','build-html','build-templates','build-css','toggleDevMode');
});

Running all of this in the console shows the tasks running and inspecting the contents of the build folder verifies that the build process is working.

$ gulp build

gullp-build-process