Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grunt watch - detecting success and failure of tasks

Tags:

gruntjs

watch

UPDATE

I'm currently using a solution similar to that described here for error notifications, and the 'Current workaround' below (without modifying the grunt force option) for success notifications.

ORIGINAL QUESTION

I'm having trouble determining when a sub-task run by the grunt-contrib-watch has finished (successfully or not).

Specifically, I'm using grunt-contrib-coffee and grunt watch to compile my CoffeeScript files as they change. The compilation is working fine.

What I would like to do is notify myself of the status of the compilation. Here's what I've tried (all code in CS):

Current workaround

From SO Question (How can I make a Grunt task fail if one of its sub tasks fail?)

What I don't like: setting and restoring a global option seems clunky, especially since it's happening in different tasks / event handlers. Also, I have to delete the destination file every time.

Without setting the global option, I can notify a successful compilation, which is great, but I would like to notify a failed one as well.

grunt.initConfig
  watch: 
    options: nospawn: true
    coffee: 
      files: '<%= coffee.dev.cwd %>/<%= coffee.dev.src %>'
      options: 
        events: ['changed', 'added']
  coffee:
    dev: 
      expand: true
      cwd: 'app'
      src: '**/*.coffee'
      dest: 'public'
      ext: '.js'

grunt.registerTask 'completedCompile', (srcFilePath, destFilePath) ->
  grunt.option 'force', false
  if grunt.file.exists( destFilePath )
    # notify success
  else 
    # notify failure

grunt.event.on 'watch', (action, filepath) ->
  if grunt.file.isMatch grunt.config('watch.coffee.files'), filepath
    filepath = # compose source filepath from config options (omitted)
    dest     = # compose destination filepath from config options (omitted)

    if grunt.file.exists( dest )
      grunt.file.delete dest   # delete the destination file so we can tell in 'completedCompile' whether or not 'coffee:dev' was successful

    grunt.option 'force', true   # needed so 'completedCompile' runs even if 'coffee:dev' fails
    grunt.config 'coffee.dev.src', filepath   # compile just the one file, not all watched files
    grunt.task.run 'coffee:dev'
    grunt.task.run 'completedCompile:'+filepath+':'+dest   # call 'completedCompile' task with args

Another option (so slow)

As suggested by another SO Question (Gruntfile getting error codes from programs serially), I used grunt.util.spawn.

This worked, but it was pretty slow (several seconds every time the CS file is saved).

grunt.event.on 'watch', (action, filepath) ->
  if grunt.file.isMatch grunt.config('watch.coffee.files'), filepath
    filepath = # compose source filepath from config options (omitted)
    dest     = # compose destination filepath from config options (omitted)

    if grunt.file.exists( dest )
      grunt.file.delete dest   # delete the destination file so we can tell in 'completedCompile' whether or not 'coffee:dev' was successful

    grunt.util.spawn {
      grunt: true # use grunt to spawn
      args: ['coffee:dev']
      options: { stdio: 'inherit' } # print to same stdout
    }, -> # coffee:dev finished
      if grunt.file.exists( dest )
        # notify success
      else 
        # notify error

Other attempts

I tried a slew of things.

  • grunt.fail.errorcount (when used in the 'completedCompile' task) is non-zero if previous compilations have failed. (Is it safe to manually reset this to zero? If yes, I wouldn't have to delete the dest file every time.) Even so, this requires setting the global option 'force' to true.
  • Anything involving specifying the 'watch.coffee.tasks' options in grunt.initConfig doesn't work because the 'coffee:dev' task is run after the 'watch' event handler has completed.
  • grunt.task.current always refers to the 'watch' task, of course



If you've made it this far, thanks for reading :).

like image 366
mnoble01 Avatar asked Jun 27 '13 19:06

mnoble01


1 Answers

I've also been having the same issue, trying to figure out when a watch sub task is complete.

Part of the problem it seem's is that Watch by default will spawn a new Grunt process to run the sub task. So your main Grunt process won't know about the tasks finishing. You can set 'nospawn' but this doesn't help much as watch doesn't expose the sub task itself.

The nearest I could get to this was using Grunt.util.hooker (inspired by Grunt Notify) to react when the Grunt fail 'report' method is called.

grunt.util.hooker.hook(grunt.fail, 'report', function(){});

However this contains no information on the actual task completed, which would be helpful if you wanted to do something based on specific sub tasks within your watch task.

Looking at the Grunt Watch github there seems to be some traction to implement a complete/fail event: https://github.com/gruntjs/grunt-contrib-watch/issues/131

like image 155
k0nG Avatar answered Oct 13 '22 05:10

k0nG