Running Grunt tasks without grunt-cli

Grunt is a JavaScript task runner that helps automate common development tasks such as minifying and testing code. Traditionally, using Grunt is a two-step process. First you install Grunt’s command-line interface (GLI) globally using npm (npm install -g grunt-cli). Next, in any project where Grunt is to be used, you must locally install Grunt and any Grunt plugins you wish to include. The command-line interface is installed separately so as to allow multiple versions of Grunt to be installed on the same machine, and acts as a simple wrapper for executing tasks through the local Grunt module.

Once you’ve installed Grunt both locally and globally, and after configuring your Gruntfile, running a task is as simple as passing a task name to Grunt’s command-line interface. For example, if my Gruntfile defines a test task, I could run it by entering grunt test into my command-line. It may sometimes be necessary or handy to be able to trigger a task without the Grunt CLI, in which case you can use the following code snippet, replacing test with the task(s) of your choosing:

node -e "require('grunt').tasks(['test']);

There are a few common scenarios in which using the above syntax would be preferable to running tasks through Grunt directly. Specifically, this may be a requirement in cases where another developer or system does not have Grunt installed globally.

As an illustration, consider the scripts feature of npm. Often overlooked, the scripts section of a package.json allows a developer to define command-line scripts to be run via predefined keywords. For example, you could trigger a Grunt test task with the following configuration, to be called using npm test.

{
    "name": "my-package",
    "version": "1.0.0",
    "scripts": {
        "test": "node -e \"require('grunt').tasks(['test']);\""
    }
}

The advantage of using the command above in place of grunt test is that your test script can now be executed regardless of whether the global Grunt module is installed.

Removing global dependencies may also help in the case of continuous integration services. Personally, I use Travis CI to run unit tests against my GitHub project commits. Up until recently, I’ve used the before_script section of my .travis.yml configurations to install the global Grunt module. By running my tasks directly, I can safely omit this configuration section. Other services may not even grant you the option to configure global dependencies, so this may be your only option.

And it’s not just Grunt, either. Where possible, make an effort to eliminate the need for global dependencies in your npm scripts. Not every developer is going to be familiar with the tools that you choose to use, and removing such dependencies helps to avoid frustration caused by error messages. It may someday be possible to define global dependencies in a project’s package.json, but that’s not likely to be coming anytime soon.

6 thoughts on “Running Grunt tasks without grunt-cli

    1. Hi Andrew! I’ve been meaning to circle back to this post and add an edited remark, since I’m not sure this is the recommendation I’d still encourage folks to use. Instead, I might encourage people to install grunt-cli as a development dependency, which will add the grunt binary to the project’s local node_modules/.bin directory. Since npm scripts include this directory in the PATH automatically, you can simply run your grunt commands as you normally would from within an npm script, including any command-line arguments. This will work even if the user running the script does not have Grunt installed globally.

      Let me know if this helps.

      Like

      1. That doesn’t seem to work on gitlab CI

        Not sure why but even with grunt-cli as a dev dependency it;s not in the path

        $ echo $PATH
        /home/gitlab-runner/.rvm/gems/ruby-2.0.0-p648/bin:/home/gitlab-runner/.rvm/gems/ruby-2.0.0-p648@global/bin:/home/gitlab-runner/.rvm/rubies/ruby-2.0.0-p648/bin:/home/gitlab-runner/.rvm/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

        Like

      2. Doesn’t work locally

        Maybe I’m doing something wrong?

        “devDependencies”: {
        “grunt-cli”: “1.2.0”
        },

        andrew.murphy@laptop94b:~/code/supermodeller-v2$ npm install
        npm WARN package.json grunt-templater@0.0.4-1 ‘repositories’ (plural) Not supported.
        npm WARN package.json Please pick one as the ‘repository’ field
        npm WARN prefer global jison@0.4.13 should be installed with -g
        andrew.murphy@laptop94b:~/code/supermodeller-v2$ echo $PATH
        /home/andrew.murphy/.rvm/gems/ruby-1.9.3-p551/bin:/home/andrew.murphy/.rvm/gems/ruby-1.9.3-p551@global/bin:/home/andrew.murphy/.rvm/rubies/ruby-1.9.3-p551/bin:/home/andrew.murphy/.rvm/bin:/home/andrew.murphy/.nvm/v0.10.24/bin:/home/andrew.murphy/.dnx/runtimes/dnx-coreclr-linux-x64.1.0.0-rc1-update1/bin:/home/andrew.murphy/.dnx/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/andrew.murphy/VSCode-linux-x64/

        Like

    2. The dev dependencies are only added to the PATH in the context of running an npm script. So you’d either run grunt if executing in an npm script or, otherwise, the equivalent but more explicit ./node_modules/.bin/grunt

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s