Developing on Staxmanade

How to block git commit if webpack bundle has changed

(Comments)

I sometimes write little web app utilities that are often statically hosted. But to use some of the new ES features means I need to drop in a build process that creates a bundle before the code gets checked in.

There are two parts to this.

  1. The raw source code. The ES6+7+next and all the nifty new js features I want to leverage today.
  2. The bundled es5 output.

In some of these projects it means I also check in the bundled code into git. A common use case is to be able to use github's pages feature to host this content (err bundled output) as well as the raw source.

The problem we can run into is if you make a change to the source, commit and push - nothing happened... Because the bundled code didn't get re-bundled, the github hosted pages page doesn't pull the latest changes in.

I'm a fan of using git pre-commit hooks to catch you early in the development life cycle to on things like test errors (or in this case a bundle issues).

So I came up with an example that allows me to make code changes and catch myself from committing when the raw source has changed, but the bundle did not reflect that.

So what is this thing?

The gist is it's a .js script that runs a set of actions and tests for the bundle.js currently vs what's about to be committed. Failing to commit if the current bundle doesn't match what the previous bundle is... Meaning, when we run our build (webpack in this case) if the bundle.js didn't change, we can commit.

This ensures that whatever bundle.js is committed is tied to the code-change the original source. Avoiding "fixing" something in the source and it not actually getting deployed because the bundle is out of date.

First get a pre-commit tool

There are some good options in the npm/node world for pre-commit hooks. Check out husky or pre-commit. However you get your precommit hook setup - great...

In my case I used husky and here are the relevant bits to my package.json.

{
  ...

  "devDependencies": {
+    "husky": "^0.13.4"
  }
  "scripts": {
+    "precommit": "node ./pre-commit-build.js"
  }
}

The pre-commit-build.js script I used

The below is the short, but complete pre-commit script I use to enforce this workflow.

var crypto = require('crypto');
var fs = require('fs');
var exec = require('child_process').exec;

var bundleFileName = './dist/bundle.js';

var getShaOfBundle = function () {
    var distFile = fs.readFileSync(bundleFileName);
    var sha = crypto.createHash('sha1').update(distFile).digest("hex");
    return sha;
}

// make sure we only bundle/build what is staged to get a proper
// view of what will be committed
exec('git stash --keep-index');

// Get a snapshot of the original bundle
var beforeSha = getShaOfBundle();

// run our build
exec('./node_modules/.bin/webpack');

// snapshot the bundle after the build
var afterSha = getShaOfBundle();

// reset anything that was stashed
exec('git stash pop');

if (beforeSha !== afterSha) {
    throw new Error("Need to bundle before committing");
}

Now whenever I make a change to the raw source code - this pre-commit script makes sure that the dist/bundle.js is correctly mapped to the raw source.

Happy committing!

Comments