v_b
v_b

Reputation: 640

Conditionally running tasks in grunt if some files are changed

I'm new to Grunt, and from what I understood up till now, Grunt has the "watch" task, which continuously checks files for modifications, and each time modification happens, runs corresponding tasks.

What I'm looking for would be a kind of discrete version of this - a task, that would run other tasks, if and only if some files were changed since the last build.

Seems to be a natural thing to ask for, but I couldn't find this. Is it just me, or is this really an issue?

Configuration file should look like this:

grunt.initConfig({
  foo: {
    files: "foo/*"
    // some task
  },
  bar: {
    files: "bar/*"
    // some other task
  },
  ifModified: {
    foo: {
      files: "foo/*",
      tasks: ['foo']
    },
    bar: {
      files: 'bar/*',
      tasks: ['bar', 'foo']
    }
  }
});

grunt.registerTask('default', ['bar', 'foo']);

Running grunt should always execute tasks 'bar', 'foo', while running grunt ifModified should execute any tasks only if some of the files were actually changed since the previous build.

Upvotes: 2

Views: 6234

Answers (3)

redben
redben

Reputation: 5686

What you need might be grunt-newer :

The newer task will configure another task to run with src files that are a) newer than the dest files or b) newer than the last successful run (if there are no dest files). See below for examples and more detail.

https://github.com/tschaub/grunt-newer

Upvotes: 0

v_b
v_b

Reputation: 640

Made my own task for that. It turned out to be not hard, here is the code:

build/tasks/if-modified.js:

var fs = require('fs');
var crypto = require('crypto');

module.exports = function (grunt) {

    grunt.registerMultiTask('if-modified', 'Conditionally running tasks if files are changed.', function () {

        var options = this.options({});

        grunt.verbose.writeflags(options, 'Options');

        var hashes = {};
        if (grunt.file.exists(options.hashFile)) {
            try {
                hashes = grunt.file.readJSON(options.hashFile);
            }
            catch (err) {
                grunt.log.warn(err);
            }
        }
        grunt.verbose.writeflags(hashes, 'Hashes');

        var md5 = crypto.createHash('md5');

        this.files.forEach(function (f) {
            f.src.forEach(function (filepath) {
                var stats = fs.statSync(filepath);
                md5.update(JSON.stringify({
                    filepath: filepath,
                    isFile: stats.isFile(),
                    size: stats.size,
                    ctime: stats.ctime,
                    mtime: stats.mtime
                }));
            });
        });

        var hash = md5.digest('hex');
        grunt.verbose.writeln('Hash: ' + hash);

        if (hash != hashes[this.target]) {
            grunt.log.writeln('Something changed, executing tasks: ' + JSON.stringify(options.tasks));

            grunt.task.run(options.tasks);

            hashes[this.target] = hash;
            grunt.file.write(options.hashFile, JSON.stringify(hashes));
        }
        else
            grunt.log.writeln('Nothing changed.');

    });
};

Gruntfile.js:

grunt.initConfig({
  foo: {
    src: ["foo/**/*"],
    dest: "foo-dest"
    // some task
  },
  bar: {
    src: ["bar/**/*", "foo-dest"]
    // some other task
  },
  'if-modified': {
    options: {
      hashFile: 'build/hashes.json'
    },
    foo: {
      src: ['foo/**/*', 'Gruntfile.js', 'package.json'],
      options: {tasks: ['foo']}
    },
    bar: {
      src: ['bar/**/*', "foo-dest", 'Gruntfile.js', 'package.json'],
      options: {tasks: ['bar']}
    }
  }
});

grunt.loadTasks('build/tasks'); // if-modified.js in this dir

grunt.registerTask('default', ['foo', 'bar']);

run:

grunt if-modified

Upvotes: 4

krampstudio
krampstudio

Reputation: 3611

You could create a task that runs conditionally other tasks, from https://github.com/gruntjs/grunt/wiki/Creating-tasks :

grunt.registerTask('foo', 'My "foo" task.', function() {
  // Enqueue "bar" and "baz" tasks, to run after "foo" finishes, in-order.
  grunt.task.run('bar', 'baz');
  // Or:
  grunt.task.run(['bar', 'baz']);
});

Upvotes: 0

Related Questions