grunt icon indicating copy to clipboard operation
grunt copied to clipboard

Run specific task(s) with force option

Open explunit opened this issue 12 years ago • 43 comments

I see that several plug-ins have added a force option, for example: https://github.com/gruntjs/grunt-contrib-jshint#force https://github.com/gruntjs/grunt-contrib-clean#force

But it would be useful to have a general-purpose way of doing this at the grunt level. Here's what I'm currently doing for a workaround:

grunt.registerTask('usetheforce_on',
 'force the force option on if needed', 
 function() {
  if ( !grunt.option( 'force' ) ) {
    grunt.config.set('usetheforce_set', true);
    grunt.option( 'force', true );
  }
});
grunt.registerTask('usetheforce_restore', 
  'turn force option off if we have previously set it', 
  function() {
  if ( grunt.config.get('usetheforce_set') ) {
    grunt.option( 'force', false );
  }
});
grunt.registerTask( 'myspecialsequence',  [
  'usetheforce_on', 
  'task_that_might_fail_and_we_do_not_care', 
  'another_task', 
  'usetheforce_restore', 
  'qunit', 
  'task_that_should_not_run_after_failed_unit_tests'
] );

Is there a better workaround? If not, would you consider adding a feature for this?

explunit avatar Jun 06 '13 18:06 explunit

I second this request. Also, when a task iterates over files, it would be nice to have an option to execute it on the next file when one fails.

ampgcat avatar Jul 04 '13 05:07 ampgcat

Sounds great. I might have an alias for other tasks, some of which are allowed to fail without failing the mother task.

Something like this would be good for me. There probably is a better syntax though.

grunt.registerTask('test', ['jshint --force', 'karma:test']); 

The karma task would have force off unless I run the mother task with --force

grunt test --force

mokkabonna avatar Aug 20 '13 09:08 mokkabonna

+1

btw, @explunit a bit of an improvement to the hack while we wait for this to land:

var previous_force_state = grunt.option("force");

grunt.registerTask("force",function(set){
    if (set === "on") {
        grunt.option("force",true);
    }
    else if (set === "off") {
        grunt.option("force",false);
    }
    else if (set === "restore") {
        grunt.option("force",previous_force_state);
    }
});

// .....

grunt.registerTask("foobar",[
    "task1",
    "task2",
    "force:on",     // temporarily turn on --force
    "task3",        // will run with --force in effect
    "force:restore",// restore previous --force state
    "task4"
]);

getify avatar Oct 30 '13 03:10 getify

+1

behrang avatar Dec 12 '13 09:12 behrang

Proposed solutions don't work in grunt-contrib--watch. Although this may be handled in that task. EDIT: It is not inherited by watch task, but it is possible to add it to the watch target's tasks list.

behrang avatar Dec 12 '13 09:12 behrang

There is also https://npmjs.org/package/grunt-continue.

Bartvds avatar Dec 12 '13 10:12 Bartvds

May be force also should be a task-level option like files, then we can configure it with some task, not only a global setting:

grunt.initConfig({
  concat: {
    foo: {
      files: {
        'dest/a.js': ['src/aa.js', 'src/aaa.js'],
        'dest/a1.js': ['src/aa1.js', 'src/aaa1.js'],
      },
      force: true,
    },
    bar: {
      files: {
        'dest/b.js': ['src/bb.js', 'src/bbb.js'],
        'dest/b1.js': ['src/bb1.js', 'src/bbb1.js'],
      },
      force: false,
    },
  },
});

yuanyan avatar Dec 12 '13 14:12 yuanyan

Issue was opened 7 months ago, and the pull request (https://github.com/yaymukund/grunt-simple-mocha/pull/36) addresses it. Has this been considered by the core contributors?

shellscape avatar Jan 15 '14 22:01 shellscape

Currently set milestone target is 0.4.3. Which is the next release, so we shall look at this.

vladikoff avatar Jan 16 '14 05:01 vladikoff

Outstanding. Thanks for getting eyes on this @vladikoff

shellscape avatar Jan 16 '14 16:01 shellscape

We have looked at this and discussed some syntax options. Something like ['jshint --force',... is not applicable. There are a lot of task running cases to look into, we shall revisit this again in 0.4.4. If anyone has more suggestions on how this force option may be defined. For example, should it part of the alias task definition? or anywhere else? Let us know.

vladikoff avatar Jan 22 '14 20:01 vladikoff

@vladikoff

Something like ['jshint --force',... is not applicable

Can you elaborate on why that's not an option?

Something like ['jshint:force', ...] could create unfortunate overlap with task parameters, I understand. But --force seems unique enough (non-param syntax) to easily be able to be parsed out ahead of time. Can you help us understand why it's not applicable or feasible?

What about the inverse, like ['force:jshint', 'bar', 'force:bar'], where force was treated like a pre-defined virtual task that does something similar to what I showed in my snippet above, perhaps like:

grunt.registerTask("force",function(task){
   var previous_force_state = grunt.option("force");

   grunt.option("force",true);
   grunt.runTask(task); // ??? is that how you run tasks?
   grunt.option("force",previous_force_state)
});

That way, wherever you have the ability to specify a task, if you want to force it, you just say "force: ..." to force it, or "..." to not force it. Would that work?

getify avatar Jan 23 '14 01:01 getify

@getify unfortunately, task names can be any arbitrary string. By giving the substring --force a special meaning it could cause compatibility issues. Technically. Unlikely, though.

Either way, it feels more like a band-aid than a real solution, because I can think of scenarios where the --force solution would be inadequate or where a more expressive solution might be desired.

For example, what if you have tasks a, b and c and you want b to not run if a fails, but you want c to run, regardless of the success of a or b.

I dunno.

cowboy avatar Jan 23 '14 15:01 cowboy

For example, what if you have tasks a, b and c and you want b to not run if a fails, but you want c to run, regardless of the success of a or b.

To expand on that, we were talking about syntax solutions for this: grunt.registerTask("build",['a','b','c']); - if 'a' fails then 'b', 'c' will not run grunt.registerTask("build",['a->b,c']); - if 'a' fails then 'b' doesn't run, 'c' will still run (see how the arg is a single string)

vladikoff avatar Jan 23 '14 16:01 vladikoff

oh please don't add a custom set of syntax where none is needed. there's already a convention in Javascript which could be used for that kind of notation:

grunt.registerTask("build",['a','b','c']); - if 'a' fails then 'b', 'c' will not run grunt.registerTask("build",[['a','b'],'c']); - if 'a' fails then 'b' doesn't run, 'c' will still run

['a', 'b'] is clearly a grouping, doesn't require any understanding of special characters or syntax, and follows the same ordering convention that's been in grunt, and unless it's used internally (that notation isn't in any documentation I could find) it should be open for use.

That would only require the introduction of a config option to force, or not to force. Or what to force, specifically. If you wanted to get nuts:

- force all (default)
- force none
- force only grouped tasks
- force only ungrouped tasks

shellscape avatar Jan 23 '14 17:01 shellscape

+1 I agree with @shellscape on the nesting thing. And I still think ["force:a","b"],"c",["d","force:e","f"] serves the important use-cases. That example would say:

  1. for the first group, "a" can succeed or fail, but "b" will go. if "b" fails, the whole sequence fails.
  2. "c" must succeed
  3. for the second group, "d" must succeed, "e" can succeed or fail, and "f" must succeed.

what use cases would that be missing?

getify avatar Jan 23 '14 17:01 getify

@getify that misses 1 use case: if 'a' fails, do we even want to try running 'b', same with e/f

I think I prefer @shellscape's idea (disclaimer: @shellscape is a coworker). Anything in a sub-group is essentially, a sub-grunt that succeeds or fails atomically, and doesn't affect the parent group.

kmdavis avatar Jan 23 '14 18:01 kmdavis

I like the prepending format, but what about using something like ! instead? ["a","!b", "c"], although that may cause some confusions with the different meaning in globbing patterns.

nschonni avatar Jan 23 '14 18:01 nschonni

@shellscape could you explain all four "force" options? By "default" do you mean the current behavior?

cowboy avatar Jan 23 '14 20:01 cowboy

Yeah "(default)" got put in the wrong place. Thanks for catching that. By no means set in stone, but off the cuff was thinking:

- force all - all tasks are forced, all continue regardless of result. - force none (default) - current behavior. - force only grouped tasks - only grouped tasks would be forced. ungrouped tasks would run with the current behavior - force only ungrouped tasks - only tasks which were not grouped would be forced. would allow for an inverse setup eg. grunt.registerTask("build",[''c', ['a','b']]); where task c would only be forced.

This was just off the cuff, to add to the discussion. The primary concern was logical grouping over syntax annotations.

shellscape avatar Jan 23 '14 21:01 shellscape

@kmdavis

that misses 1 use case: if 'a' fails, do we even want to try running 'b', same with e/f

If you want b to only run if a succeeds, then:

[["a","b"],"c"... or even just ["a","b","c"...

If you want the group ["a","b"] to be itself optional (aka forced), then no, my suggestion doesn't handle that. But what's actually a real (not just theoretical) example of such a use case? If the group failed, it would seem logical that the whole chain fails. I can only see in theory the need for "optional (aka forced) groups". Seems like it'd be over-architecting to solve that "optional-group" use-case unless there was a concrete example of such.

getify avatar Jan 23 '14 22:01 getify

@getify ok... real use case (please sign NDA here, here, and here, and submit in triplicate...)

In our legacy system (that we're converting over to grunt) we have some tasks that have Soft dependencies. A specific example would be linting. Linting is very nice, and we scream loudly if you fail linting... but we don't actually Stop anything that has a soft dependency on linting. We have a couple other things that are soft dependencies, like code coverage, complexity analysis, generating docs, etc. All things that are non-Essential.

Other things ARE essential. Like syntax checking, compilation, specs, etc. Sometimes, the soft deps are interspersed with the hard deps.

Example: When we run our spec task, it also runs npm install, compilation, docs, linting, syntax checking. Using @shellscape's proposed syntax, that'd look something like this:

grunt.registerTask('spec', [
  'check-syntax',
  [
    'jshint',
    'gilt-special-linting',
    'complexity',
    'groc'
  ],
  'compile',
  'npm-install',
  'karma'
]);

kmdavis avatar Jan 27 '14 16:01 kmdavis

@kmdavis

wouldn't this be the same thing (unless I'm missing something)?

grunt.registerTask('spec', [
  'check-syntax',
  'force:jshint',
  'force:gilt-special-linting',
  'force:complexity',
  'force:groc'
  'compile',
  'npm-install',
  'karma'
]);

To put it more succinctly, I am having trouble imagining a scenario (in the real world) like:

  1. some required tasks
  2. a group of tasks:
    • some which are required
    • some which are optional
    • if any of the required items in the group fails, the group as a whole is optional
  3. more required tasks (that happen even if 2 as a group fails)

getify avatar Jan 27 '14 18:01 getify

@getify yes, essentially they're two ways to say the same thing. I was going with the array-grouping notation because of the proposal for additional custom syntax and concerns earlier in the thread about using 'force' as a keyword in any sense. (eg. imagine someone silly writing a starwars grunt task named 'force' -> 'force:use')

We're all in agreement about the gist of the need and seemingly how it should be roughly notated. What we need now is weigh in from the people who make the decisions (@cowboy, @vladikoff) on what sits best with them. I'm good with either array grouping or making 'force:' a reserved word. I believe we are monkey-patching my proposal soonish here, and will likely submit a pull request to add to the conversation.

shellscape avatar Jan 27 '14 19:01 shellscape

@shellscape agreed, for the most part.

We're inventing new "syntax" to patch a gap in functionality, not inventing a new syntax to cover every possible niche use-case. IMO the former dictates a minimal syntax for covering demonstrable/common use-cases, not a sugared syntax that is broadly capable.

That's the only reason why I was urging a simpler option than the one your proposed, to see if the simpler option (usually better) covered enough of the real-world stuff and restrained itself on the theoretically "nice" stuff which won't really get much bang for the buck.

getify avatar Jan 27 '14 19:01 getify

I'm definitely not going to build-in a force: prefix, but you should be able to create a custom task that does just that. I encourage you to create it and give it a try.

I am, however, very interested in a more general solution, like what @shellscape suggests, as long as we can't poke any big holes in it.

cowboy avatar Jan 28 '14 00:01 cowboy

:+1: to a grunt --force option, in addition to something like the inverse grouping example grunt.registerTask("build",[''c', ['a','b']]) @shellscape mentioned; where grouped tasks would be considered dependent on each other, and so should not be 'forced'. Unless you really, really wanted to force those tasks anyway, in which case I suppose a grunt --force=all would do the trick.

JGarrido avatar Feb 01 '14 22:02 JGarrido

Whatever the solution here, I'd like you to consider a related, pretty important aspect: The exit code. Currently, setting the force option on tasks that support it will cause grunt to exit with a zero status code, even when one or more forced tasks had errors.

My usecase is pretty simple: Run all tasks, but exit with non-zero status code if anything failed. That way I'd get the most out of Travis builds, where a trailing comma can be printed as an error in the jshint task and causes the build to fail, but it would still run the unit tests.

jzaefferer avatar May 14 '14 17:05 jzaefferer

fwiw we've been using the solution that @kmdavis outlined for four months now and it's been easy as all hell to use and grok, and haven't run into any negative side effects given heavy use of the construct.

shellscape avatar May 14 '14 18:05 shellscape

I agree with @jzaefferer, forcing the task should still make the whole process exit with code different than 0.

mgol avatar May 15 '14 11:05 mgol