grunt icon indicating copy to clipboard operation
grunt copied to clipboard

Multitask option to set the default behavior when a subtask is not specified

Open dylang opened this issue 11 years ago • 27 comments

Sometimes it's dangerous or undesired to run all of a multitask's subtasks by default.

Proposal: an option to set the default behavior when a subtask is not specified instead of iterating through all the subtasks.

Default deploy to use dev when a subtask is not specified:

deploy: {
  options: { 
    default: 'dev'
  },
  dev: { .. },
  qa: { .. },
  prod: { .. }
}
$ grunt deploy  # only runs deploy:dev instead of all of them

Example: Don't run any bump subtasks if the subtask is not specified.

bump: {
  options: { 
    default: false
  },
  major: { .. },
  minor: { .. },
  patch: { .. },
  build: { .. }
}
$ grunt bump  # returns an error that the bump subtask was not specified

dylang avatar Jun 12 '13 21:06 dylang

:+1:

This would also be helpful when you have a bunch of subtasks for clean and running them all at once is an uncommon scenario.

NickHeiner avatar Jun 12 '13 22:06 NickHeiner

:+1:

kiurchv avatar Jun 29 '13 23:06 kiurchv

I have a case where I'd like to have some additional watch targets for specific deployment targets, but I'd like the generic watch task not to run any of them, and an option like the one described by @dylang would be very useful.

neagle avatar Oct 02 '13 15:10 neagle

+1

softwaredoug avatar Oct 16 '13 19:10 softwaredoug

I agree, I actually thought this was the default behavior when I first started using grunt, it just made sense. options is a grunt reserved keyword now, maybe defaults could be as well?

grunt.initConfig({
  myMultiTask: {
    options: {
      clean: true,
      dir: ""
    },
    defaults: {
      clean: false.
      dir: "foo"
    },
    bamboo: {
      dir: "bar"
    },
    jenkins: {
      dir: "baz"
    }
  }
});

Invoke:

$ grunt myMultiTask
-- Running myMultiTask...
$ grunt myMultiTask:bamboo
-- Running myMultiTask (subtask bamboo)...
$

@cowboy: Does this make any sense or are there perhaps any plans on a different approach that enables one to run this without having to define a new task that runs a specific subtask?

peol avatar Oct 18 '13 08:10 peol

:+1:

Having the exact same situation with clean as described by @NickHeiner.

rsternagel avatar Oct 18 '13 09:10 rsternagel

+1 for default subtask

rinatio avatar Dec 12 '13 00:12 rinatio

:+1:

jethrolarson avatar Jan 04 '14 01:01 jethrolarson

I don't like the idea of a named option, because it will 1. override any existing task's defaults option and 2. be meaningless once options are merged down to the target level.

This problem could be solved with another reserved key next to the options key, like default.

Another possible solution: Right now, properties starting with _ are ignored in multi-tasks and are invalid targets. Maybe I could change that behavior to make them non-iterable, but still valid.

Here's a sample Gruntfile:

module.exports = function(grunt) {
  grunt.initConfig({
    foo: {
      a: {},
      b: {},
      _c: {},
    }
  });

  grunt.registerMultiTask('foo', function() {
    console.log(this.nameArgs);
  });
};

And some sample CLI output.

$ grunt foo
Running "foo:a" (foo) task
foo:a

Running "foo:b" (foo) task
foo:b

Done, without errors.

$ grunt foo:_c
Running "foo:_c" (foo) task
foo:_c

Done, without errors.

Note that in the current Grunt, running grunt foo:_c will error. This would be a bit of a change.

cowboy avatar Jan 22 '14 20:01 cowboy

I love the underscore option. It maps to the way partials work in Sass, in a way.

Nate Eagle

On Jan 22, 2014, at 3:12 PM, Ben Alman [email protected] wrote:

I don't like the idea of a named option, because it will 1. override any existing task's defaults option and 2. be meaningless once options are merged down to the target level.

This problem could be solved with another reserved key next to the optionskey, like default.

Another possible solution: Right now, properties starting with _ are ignored in multi-tasks and are invalid targets. Maybe I could change that behavior to make them non-iterable, but still valid.

Here's a sample Gruntfile:

module.exports = function(grunt) { grunt.initConfig({ foo: { a: {}, b: {}, _c: {}, } });

grunt.registerMultiTask('foo', function() { console.log(this.nameArgs); });};

And some CLI output.

$ grunt foo Running "foo:a" (foo) task foo:a

Running "foo:b" (foo) task foo:b

Done, without errors.

$ grunt foo:_c Running "foo:_c" (foo) task foo:_c

Done, without errors.

It's just a thought, for now at least.

— Reply to this email directly or view it on GitHubhttps://github.com/gruntjs/grunt/issues/815#issuecomment-33062465 .

neagle avatar Jan 22 '14 21:01 neagle

@cowboy how would _ options help with default behavior? If you run grunt foo is it still running all subtasks?

rinatio avatar Jan 23 '14 05:01 rinatio

I like using "default" as an extra keyword. Though that could break code in the wild I guess.

The next question is whether the value should be a string that references the default task or just be the task object itself. I suppose it could be both dependant on the type the user uses.

jethrolarson avatar Jan 23 '14 07:01 jethrolarson

+1 for default target.

I'm using a build task that has several targets, named 'package1' to 'packageN'. Each package target builds a list of modules, many of which are built into multiple packages.

Running the build task without a target would build a lot of the modules more than once. Thats why im using an additional 'all' target that builds every module.

Being able to flag this target would be exactly what i need.

The underscore option is fine, but in my situation it would lead to the use of a lot of underscore target names (_packageX) - which seems awkward.

codepunkt avatar Apr 25 '14 09:04 codepunkt

:+1: A default target is needed and makes sense.

mustafa0x avatar May 05 '14 17:05 mustafa0x

+1 Why is this not a thing yet? :)

Here's a dirty hack until something more official comes along. Create a file (tasks/multitask-default.js) and call grunt.loadTasks('tasks') in your Gruntfile.js. Use this for the contents of the file:


/**
 * Enable default multitask support. Define your default task by adding another task
 * with the empty string ('') as its name and the name of the default task as its value.
 *
 * Example: (publish is a multi-task)
 *     // In your Gruntfile.js
 *     grunt.initConfig({
 *         publish: {
 *             options: {
 *                // some options
 *             },
 *             '': 'dev',
 *             dev: {
 *                 // definition
 *             },
 *             release: {
 *                 // definition
 *             }
 *         }
 *     });
 *
 *     // Running "grunt publish" will only invoke publish:dev
 *
 */
module.exports = function (grunt) {
    var originalRunAllTargets = grunt.task.runAllTargets;
    grunt.task.runAllTargets = function(taskname, args) {

        var targets = grunt.config.getRaw(taskname) || {},
            target;

        function isValidMultiTaskTarget(name) {
            return !/^_|^options$/.test(name);
        }

        target = targets[''];
        if (typeof target === 'undefined') {
            originalRunAllTargets(taskname, args);
        } else {
            if (! isValidMultiTaskTarget(target)) {
                throw new Error('Invalid default target "' + target + '" specified.');
            }
            grunt.task.run([taskname, target].concat(args || []).join(':'));
        }
    };
};

jcwilson avatar May 17 '14 00:05 jcwilson

:+1: How about naming it defaultTarget. I would even prefer to change the current behavior and make running all targets explicit:

grunt.initConfig({
  myMultiTask: {
    defaultTarget: 'jenkins',
    options: {
      clean: true,
      dir: ""
    },
    bamboo: {
      dir: "bar"
    },
    jenkins: {
      dir: "baz"
    }
  }
});
  • myMultiTask:bamboo runs the bamboo target
  • myMultiTask runs the default target (jenkins). If no defaultTarget is defined, myMultiTask will raise an error
  • myMultiTask:* runs bamboo and jenkins

mar10 avatar Jun 19 '14 21:06 mar10

I like the * designation to make it explicit that you want to run all of the tasks, too.

jcwilson avatar Jun 19 '14 23:06 jcwilson

+1

I would like to write independent grunt-exec tasks, where only the specified subtask runs by default, similar to how a traditional Makefile behaves.

mcandre avatar Jul 30 '14 14:07 mcandre

+1 for default subtask.

vgrinko avatar Oct 10 '14 23:10 vgrinko

:+1: This would be a great feature.

jawshooah avatar Jan 30 '15 17:01 jawshooah

Hi, I have the following scenario:

My app is built for multiple clients, so I'm setting up grunt to handle commands like: grunt scsslint --client=company1 grunt scsslint --client=company2

Gruntfile.js

module.exports = function (grunt) {

    var client = grunt.option('client') || 'company1';
    var scsslint = {
        options: {
            config: 'scss-lint-config.yml',
            reporterOutput: 'scss-lint-report.xml'
        },
        core: ['core/**/*.scss']
    };
    scsslint[client] = [client + '/**/*.scss'];

   grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readJSON(client + '-config.json'),
        scsslint: scsslint,

grunt scsslint --client=company1 should be the same as running grunt scsslint:company1. However, grunt scsslint --client=company1 run all sub-tasks. grunt scsslint:company1 works just fine.

Any ideas to accomplish what I want?

JobaDiniz avatar Feb 20 '15 18:02 JobaDiniz

+1 for the defaultTarget +1 for myMultiTask:* to all the targets explicitly

grunt.initConfig({
  myMultiTask: {
    defaultTarget: 'jenkins',
    options: {
      clean: true,
      dir: ""
    },
    bamboo: {
      dir: "bar"
    },
    jenkins: {
      dir: "baz"
    }
  }
});

myMultiTask:bamboo runs the bamboo target myMultiTask runs the default target (jenkins). If defaultTarget is not provided / defined, myMultiTask will raise an error myMultiTask:* runs bamboo and jenkins

sasikanth avatar May 29 '15 04:05 sasikanth

:+1: Would solve some problems we are having really nicely

MichaelSitter avatar Jun 02 '15 18:06 MichaelSitter

+1 for defaultTarget

Especially in the case of multiple watch targets this would be really helpful.

Henni avatar Jun 03 '15 11:06 Henni

It's easy to overcome this limitation by monkeypatching:

module.exports = (grunt) ->
  grunt.registerMultiTask 'test', 'Docs...', ->
    ...

  decoratedFn = grunt.task._tasks['test'].fn
  decorator = (target) ->
    args = grunt.util.toArray(arguments)

    if not target
      args = ['all'].concat(args.slice(1)) # running just 'grunt test'
    else if not grunt.config(['test', target])
      args = ['arbitrary', target].concat(args.slice(1)) # running arbitrary test, e.g. 'grunt:test/my-specific-test-file.coffee'

    @nameArgs = "test:#{args[0]}"
    decoratedFn.apply(@, args)
  grunt.task._tasks['test'].fn = decorator

Config:

  test:
      options:
        reporter: 'spec'
        timeout: 120000

      all:
        options: ...

      arbitrary:
        options: ...

      unit:
        src: 'test/*-test.coffee'
        options:
          timeout: 30000

Now I'm able to do:

grunt test:path/to/my/test.coffee # unknown target, assuming path to file, target 'arbitrary'
grunt test:unit # specific target 'unit'
grunt test # target 'all', thus the same as 'grunt test:all'

honzajavorek avatar Jun 04 '15 07:06 honzajavorek

+1 This would be a useful feature.

titocr avatar Jul 12 '16 22:07 titocr

How come this is still not a thing?

JoseFaeti avatar Aug 16 '18 08:08 JoseFaeti