可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is there a way to configure a sequence of tasks so that specific subsequent ones (I don't want --force on the whole batch) run even if one fails? For example, consider a case like this
- Create some temporary files
- Run some unit tests which involve those temporary files
- Clean up those temporary files
I can do this:
grunt.registerTask('testTheTemp', ['makeTempFiles', 'qunit', 'removeTempFiles']);
But if qunit fails then the removeTempFiles task never runs.
回答1:
Here's one workaround. It's not pretty, but it does solve the issue.
You create two extra tasks which you can wrap at the beginning/end of any sequence that you want to continue even over failure. The check for existing value of grunt.option('force')
is so that you do not overwrite any --force
passed from the command line.
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'
] );
I've also submitted a feature request for Grunt to support this natively.
回答2:
For posterity sake, this might be an improved hack while we wait for that PR from @explunit to land in grunt:
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"
]);
回答3:
Perhaps you can create an async grunt task and grunt.util.spawn your desired tasks serially. You can then write some conditional logic for the success/error codes. Something similar to the answer to this question
回答4:
Echoing Marius' comment, the grunt-force-task plugin now provides this functionality. Full details by following the link above, but in a nutshell this is what you need to achieve the desired effect
npm install grunt-force-task --save-dev
Then import it into your gruntfile
grunt.loadNpmTasks('grunt-force-task');
Finally, just add the force: prefix to the task(s) before the one you always want to run.
grunt.registerTask('testTemp', ['makeTempFiles', 'force:qunit', 'removeTempFiles']);
Now removeTempFiles will always run even if the test fails.
回答5:
The one issue with using the grunt-force-task plugin mentioned above is that the grunt process will now unconditionally exit with 0 (which means pass).
This is an issue if you want to use grunt in a CI (continuous integration) environment and fail the CI task based on whether your test/build (qunit
in the OP) passes or fails. I have worked around this issue by adding a new task that uses grunt's this.requires
function to test whether qunit
passed or failed:
grunt.registerTask('exitWithQunitStatus', function() {
this.requires(['qunit']);
return true;
})
grunt.registerTask('testTheTemp', ['makeTempFiles', 'force:qunit', 'removeTempFiles', 'exitWithQunitStatus']);
Now if qunit
fails grunt will exit with 3, which indicates Task Error
. Without the exitWithQunitStatus
, the grunt process will exit with 0.
this.requires
is documented here : http://gruntjs.com/api/inside-tasks#this.requires . Basically it will fail the current task unless all of the specified "required" tasks have already run and passed.