Gulp and commit hooks (gilp)

Rodrigo Lopez
August 28, 2017

This is part of Code quality enforcement series.

When creating a validation pipeline for your staged files, you don't have too many choices and usually end up with a huge bash script with countless functions for every linter, rule, or standard you want to automate.

This post presents gilp, a gulp plugin which takes care of installing pre-commit, pre-push, and commit-msg hooks and running gulp tasks.

Validation pipeline

As gulp provides gulp.src, gilp provides gilp.srcFromStaged, a similar function that filters out unstaged files and returns a stream of vinyl File objects. Gilp also provides gilp.srcFromCommit which takes a revision hash.

This example includes gulp-eslint for javascript validation.

gilp.hook('pre-commit', ['check-eslint'], () =>
  gilp.srcFromStaged(['**/*'.js])
  .pipe(eslint())
  .pipe(eslint.format())
  .pipe(eslint.failAfterError())

gilp.hook behaves similarly to gulp.task, but it gets registered to ./git/hooks and wraps the original gulp.task.

Now let's take a look at a minimal working example.

Example

We usually first separate our checks in different functions.

const filter = require('gulp-filter');
const combiner = require('stream-combiner2').obj;
const print = require('gulp-print');
const flake8 = require('gulp-flake8');
const gulpIsort = require('gulp-isort');
const checkGrep = require('gulp-check-grep');

function py() {
  const src = filter([
    '**/*.py',
  ], {restore: true});
  return combiner(
    src,
    print(),
    flake8('.flake8'),
    flake8.failOnError(),
    gulpIsort(),
    gulpIsort.failOnError(),
    checkGrep(/print\+$/gm, {pass: (line, n, f) => line.endswith('# noqa')}),
    checkGrep.failOnError(),
    src.restore
  );
}

This uses stream-combiner2 to make our py function behave like a single-stream pipeline and gulp-filter to get only python files.

const filter = require('gulp-filter');
const combiner = require('stream-combiner2').obj;
const print = require('gulp-print');
const lintFilepath = require('gulp-lint-filepath');

function html() {
  const src = filter(['**/*.html'], {restore: true});
  return combiner(
    src,
    print(),
    lintFilepath({'file-name': [/(^|\/)[a-z0-9_]+\.html$/]}),
    lintFilepath.reporter(),
    src.restore
  );
}

This is more or less the same using a gulp file name validator for html files.

const gilp = require('gilp');

gilp.hook('pre-commit', () =>
  gilp.srcFromStaged(['**/*'])
  .pipe(py())
  .pipe(html())
);

And here's the gilp hook defined, running gulp gilp-install will put those tasks into the pre-commit hooks file.

"Gulp and commit hooks (gilp)" by Rodrigo Lopez is licensed under CC BY SA. Source code examples are licensed under MIT.

Photo by Tommy Lisbin on Unsplash.

Categorized under research & learning.

Join our team

If you're passionate about building quality software and our values resonate with you, get in touch with us!