gruntjs / grunt

Grunt: The JavaScript Task Runner
http://gruntjs.com/
Other
12.27k stars 1.5k forks source link

Run specific task(s) with force option #810

Open explunit opened 11 years ago

explunit commented 11 years ago

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?

ampgcat commented 11 years ago

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.

mokkabonna commented 11 years ago

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
getify commented 10 years ago

+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"
]);
behrang commented 10 years ago

+1

behrang commented 10 years ago

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.

Bartvds commented 10 years ago

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

yuanyan commented 10 years ago

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,
    },
  },
});
shellscape commented 10 years ago

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?

vladikoff commented 10 years ago

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

shellscape commented 10 years ago

Outstanding. Thanks for getting eyes on this @vladikoff

vladikoff commented 10 years ago

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.

getify commented 10 years ago

@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?

cowboy commented 10 years ago

@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.

vladikoff commented 10 years ago

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)

shellscape commented 10 years ago

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
getify commented 10 years ago

+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?

kmdavis commented 10 years ago

@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.

nschonni commented 10 years ago

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.

cowboy commented 10 years ago

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

shellscape commented 10 years ago

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.

getify commented 10 years ago

@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.

kmdavis commented 10 years ago

@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'
]);
getify commented 10 years ago

@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)
shellscape commented 10 years ago

@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.

getify commented 10 years ago

@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.

cowboy commented 10 years ago

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.

JGarrido commented 10 years ago

:+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.

jzaefferer commented 10 years ago

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.

shellscape commented 10 years ago

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.

mgol commented 10 years ago

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

vladikoff commented 10 years ago

https://github.com/gruntjs/grunt/issues/1163

gausie commented 9 years ago

@shellscape what solution are you referring to there?

shellscape commented 9 years ago

@gausie this one https://github.com/gruntjs/grunt/issues/810#issuecomment-33385633

however, we've since moved onto gulp as it made more sense for our needs.

gausie commented 9 years ago

but isn't that comment just a recommendation for a syntax?

floriangosse commented 9 years ago

I have published a grunt task which enable the force mode for a specific task: grunt-force-task. It's easy to use and doesn't need any configuration. For example force:jshint.

At the moment I have one untested use case: Some tasks are executed parallel. But first it's works for me.

AndersDJohnson commented 9 years ago

@jzaefferer @mzgol Agreed, there needs to be an option continue build (with grouping or --force or whatever) but still carry through the non-zero exit code from intermediary steps so that the final exit code is also non-zero. This should be optional. Having it could solve the use case in #1114. Are there any other approaches to try/catch/finally error handling per-task in Grunt?

Taytay commented 9 years ago

Thanks @floriangosse. Your force-task works like a champ.

atav32 commented 9 years ago

@floriangosse +1 grunt-force-task

stefcameron commented 9 years ago

@floriangosse +1 grunt-force-task works great!!

ghost commented 8 years ago

@getify @floriangosse Another +1 for grunt-force-task ... worked as expected. Found it easier to understand than the grouped arrays. Seems more explicit in its use than having force implicitly called based on array structure.

jsr6720 commented 8 years ago

@getify thanks for the workaround. plus one with the others.

pombadev commented 7 years ago

+1

ghost commented 6 years ago

+1