Coder Social home page Coder Social logo

grunt-shell-spawn's Introduction

grunt-shell-spawn Build Status Build status

A fork of sindresorhus's grunt-shell with support for background processes.

(e.g.: start a compass watch in the background)

This plugin lets you:

  • Run processes synchronously or asynchronously.
  • Process stdout and stderr using functions.
  • Run a function when an asynchronous process ends that gets the exit code.
  • Kill an asynchronous process.

Requirements

  • node.js 4.x or later
  • grunt 0.4 or later

Install

npm install grunt-shell-spawn --save-dev

Once the plugin has been installed, it may be enabled inside your Gruntfile with:

grunt.loadNpmTasks('grunt-shell-spawn');

Examples

Simple task:

Let's take for example launching a compass watch in background:

shell: {
    command: 'compass watch',
    options: {
        async: true
    }
}

Multitask:

shell: {
    compassWatch: {
        command: 'compass watch',
        options: {
            async: true,
            execOptions: {
                cwd: './src/www/'
           }
       }
    },
    coffeeCompile: {
        command: 'coffee -b -c -o /out /src',
        options: {
            async: false,
            execOptions: {
                cwd: './src/www/'
            }
        }
    },
    options: {
        stdout: true,
        stderr: true,
        failOnError: true
    }
}

Custom callbacks:

Works in synchronous or asynchronous mode.

    asyncWithCallbacks: {
        command: 'sleep 3 & echo HELLO & sleep 1 & echo WORLD & sleep 2',
        options: {
            async: true,
            stdout: function(data) { /* ... */ },
            stderr: function(data) { /* ... */ },
            callback: function(exitCode, stdOutStr, stdErrStr, done) { 
                done();
            }
        }
    }, 

Killing an async process

Stop a running async task with the :kill task argument.

    server: {
        command: 'redis-server',
        options: {
            async: true,
        }
    },

grunt shell:server shell:somethingElse shell:server:kill

The process will be killed with a SIGKILL.

Please note that processes that are not killed will continue running even after grunt finishes, unless explicitly terminated using :kill. This means it is required to use :kill to clean up any processes you started, unless you want them to continue running in the background.

Release History

  • 2019-05-26 v0.4.1 Updated dependencies
  • 2019-01-29 v0.4.0 Added CI on Travis, AppVeyor; updated node.js engine dependency to >=4
  • 2019-01-26 v0.3.12 Removed dependency on exec-sync to resolve security advisory
  • 2015-01-07 v0.3.1 Fix the :kill task on UNIX and Windows
  • 2013-04-06 v0.1.3 Last version with support for grunt 0.3.x

License

MIT License (c) Sindre Sorhus

grunt-shell-spawn's People

Contributors

benaghaeipour avatar cri5ti avatar dandv avatar dependabot-preview[bot] avatar dependabot-support avatar dignifiedquire avatar hurrymaplelad avatar jansegre avatar jeking3 avatar jonhoo avatar landau avatar lukas-shawford avatar mboudreau avatar mgs255 avatar sindresorhus avatar suisho avatar yannickcr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

grunt-shell-spawn's Issues

Cannot install execSync v1.0.2 error

I get the following error when I tried

npm install grunt-shell-spawn --save-dev

[email protected] install d:\cq\cq-bhg\ui\node_modules\grunt-shell-spawn\node_modules\execSync
node install.js

[execsync v1.0.2] Attempting to compile native extensions.
{ [Error: spawn ENOENT] code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn' }
[execSync v1.0.2]
Native code compile failed!!
Will try to use win32 extension.
[email protected] node_modules\grunt-shell-spawn
└── [email protected] ([email protected])

I'm running

$ node --version
v0.10.35

Any idea why?

Thanks

Can't run a task twice

Config:

shell: {
    ls: {
        options: {
            async: false,
            stdout: true
        },
        command: 'ls'
    }
}
grunt.registerTask('ls', ['shell:ls', 'shell:ls']);

Then run:

grunt ls

Result with errors:

Running "shell:ls" (shell) task
Gruntfile.js

Running "shell:ls" (shell) task
Fatal error: Process :ls already started.

Repalce not found

Any idea what this error means?

shell : {
    test : {
        options : {
            async: true
        },
        cmd: 'java -version'
    }
}

Here is the error:
Cannot call method 'replace' of undefined

Cannot find module './build/Release/shell' with v0.3.1

After updating to v0.3.1, I'm getting the following error when running the shell task:

Cannot find module './build/Release/shell'

Here's the respective configuration from my Grunfile.js:

        shell: {
            xvfb: {
                command: 'Xvfb :99 -ac -screen 0 1600x1200x24',
                options: {
                    async: true
                }
            }
        }

Do I need to update my configuration when using v0.3.1? Reverting to v0.3.0 fixes the issue immediately.

Not working in a loop

For some reason, when I call this task from within a loop, it is not behaving synchronously.

    shell: {
      jekyll: {
        command: 'jekyll build',
        options: {
          async: false,
        }
      }
    }
....
  grunt.registerMultiTask('build', 'Build all things', function(){
    // Build all locales
    var locales = this.data.things,
        child = null,
        temp = filePath = null,
        src = grunt.file.read('config.yml'),
        len = locales.length;

    while(len--){
      grunt.log.writeln('Building ' + things[len]);
      filePath = this.data.path + '/' + things[len] + '.yml';

      if(grunt.file.exists(filePath)){
        temp  = grunt.file.read(filePath);
        grunt.file.write('_config.yml', src + temp);

        if(grunt.file.exists("_config.yml")) console.log("Succesfully created _config.yml");

        grunt.task.run('compile');

        grunt.file.delete('_config.yml');
      }
    }
  });

Yet, all of the other stuff in the loop runs and then all of a sudden these child processes spawn:

➜  $ ✗ grunt
Running "build:all" (build) task
Building thing1
Succesfully created _config.yml
Building thing2
Succesfully created _config.yml
Building thing3
Succesfully created _config.yml
Building thing4
Succesfully created _config.yml

Running "shell:jekyll" (shell) task
>> Configuration file: none

Running "shell:jekyll" (shell) task
>> Configuration file: none

Running "shell:jekyll" (shell) task
>> Configuration file: none

Running "shell:jekyll" (shell) task
>> Configuration file: none

Done, without errors.

Any idea whether I'm doing something wrong or somehow something is becoming async along the way?

Thanks a ton

:kill does not actually kill the process

I noticed that the :kill target doesn't actually work. You can verify this using the existing tasks in this repo's Gruntfile. The simple test is to just run the killTask twice, and you'll notice that the second time it errors out because the port is already in use:

$ grunt killTask
...
Done, without errors.

$ grunt killTask
>> ....
>> Error: listen EADDRINUSE

As a side note, I also encountered "connection refused" errors because the shell:curl task sometimes runs before the node server was ready, so I just created a simple pause task:

pause: { command: 'sleep 3' },

And inserted in between shell:neverEndingTask and shell:curl:

 grunt.registerTask('killTask', ['shell:neverEndingTask', 'shell:pause', 'shell:curl', 'shell:neverEndingTask:kill']);

Now let's try to figure out what's going on with the original issue. Run grunt using --verbose on the predefined killTask:

$ grunt --verbose killTask

Observe the PID of the process being killed:

Running "shell:neverEndingTask:kill" (shell) task
Verifying property shell.neverEndingTask exists in config...OK
File: [no files]
Options: stdout, stderr, failOnError, canKill, async
Options: stdout, stderr, failOnError, canKill, async
Killing process for target: neverEndingTask (pid = 17825)

In this case, note that it's 17825.

On Linux, you can use the fuser command to find which processes are listening on a particular port. killTask launches an HTTP server that listens on port 1337, so let's see if it's still listening:

$ fuser 1337/tcp
1337/tcp:            17826

Indeed it is, and the associated PID is 17826. Examining that process using top shows that this is the node process that's running tests/server/server.js. Now let's look up its parent process:

$ ps -p 17826 -o ppid=
1160

The parent process PID is 1160 - let's examine that using top:

$ top -p 1160
PID USER      PR  NI  VIRT  RES  SHR S  %CPU %MEM    TIME+  COMMAND
1160 serg      20   0  5740 1884 1484 S   0.0  0.0   0:00.06 init

And based on that, that's the init process, which on Unix means that the node process that was supposed to be killed is now officially an orphaned process.

I looked into why this might be the case and found that rather than launching the command directly, we're actually launching a separate shell process and passing in the actual command as the command to execute within that shell:

tasks/shell.js#L52:

    if (process.platform === 'win32') {
        file = 'cmd.exe';
        args = ['/s', '/c', data.command.replace(/\//g, '\\') ];
        opts.windowsVerbatimArguments = true;
    } else {
        file = '/bin/sh';
        args = ['-c', data.command];
    }

Is there any reason we're doing that, instead of just using child_process.spawn on the actual command itself?

In any case, I think the assumption was made here that if we kill the parent shell process, that would automatically kill any child processes started on that shell. That is not the case. As explained in that post, on Unix, in order to kill all the associated child processes, we must kill the process group - and the way to do that, at least on the command line, is to pass in the parent PID as a negative value to the kill command:

$ kill -9 -17825

Instead of:

$ kill -9 17825

Now the question is how to do that with Node's child_process API. And hopefully in a platform independent manner, so this works on Windows also.

I have no idea.

What would be easier is if we just passed the original command to child_process.spawn - then the process PID would be the actual process PID, and no trickery would be required to kill it. But I imagine there's a reason we're wrapping the command in a shell, so I don't know if this is really an option.

In which case, should the :kill feature just be removed, since it's clearly broken?

cmd on win7 ENOENT

Hi following Problem: When i try to run java from your plugin i get following error.
grunt-shell did work but i needed to spawn the childs asyncronly

Verifying property shell.startExport exists in config...OK
File: [no files]
Options: stdout, stderr, failOnError, canKill, stopIfStarted=false, execOptions={"cwd":"./exporter"}
Options: stdout, stderr, failOnError, canKill, stopIfStarted=false, execOptions={"cwd":"./exporter"}
Command: cmd.exe
Args: /s, /c,                  java -classpath D:/.../products/.../lib/*                 -Dlogback.configurationFile=../configs/exporter/logback.xml                 -Dapp.config=../configs/exporter/full-admin-from-dev.json                 com....Launcher
Fatal error: spawn cmd.exe ENOENT

procs is not defined with version 0.3.4

Getting following error with latest version (v0.3.4)

/var/lib/jenkins/jobs/test-job/node_modules/grunt-shell-spawn/tasks/shell.js:152
        _.forEach(procs, function (proc, key, collection) {
                  ^
ReferenceError: procs is not defined
    at process.<anonymous> (/var/lib/jenkins/jobs/test-job/node_modules/grunt-shell-spawn/tasks/shell.js:152:19)
    at process.EventEmitter.emit (events.js:107:17)
    at process.exit (node.js:597:17)
    at tryToExit (/var/lib/jenkins/jobs/test-job/node_modules/grunt/node_modules/exit/lib/exit.js:17:15)
    at Object.exit (/var/lib/jenkins/jobs/test-job/node_modules/grunt/node_modules/exit/lib/exit.js:34:3)
    at Object.task.options.done (/var/lib/jenkins/jobs/test-job/node_modules/grunt/lib/grunt.js:154:14)
    at Task.<anonymous> (/var/lib/jenkins/jobs/test-job/node_modules/grunt/lib/util/task.js:280:25)
    at /var/lib/jenkins/jobs/test-job/node_modules/grunt/lib/util/task.js:227:11
    at process._tickCallback (node.js:343:11)
```code

Can "command" Be a Function?

In grunt-shell, there is an option to make the command be a function, rather than just a string, but this doesn't seem to be the case in grunt-shell-spawn:

grunt:shell task (using load-grunt-config):

module.exports = {

    // starts up MongoDB server/daemon
    mongod: {
        command: function(port, ip) {
            return 'mongod --bind_ip konneka.org --port ' + (process.env.MONGO_PORT ||   27017) + ' --dbpath C:/MongoDB/data/db';
        },
        options: {
            async: true, // makes this command asynchronous
            stdout: false, // does not print to the console
            stderr: true, // prints errors to the console
            failOnError: true, // fails this task when it encounters errors
            execOptions: {
                cwd: '.'
            }
        }
    }
};

Output:

$ grunt shell:mongod
Running "shell:mongod" (shell) task
Warning: Object function (port, ip) {
                        return 'mongod --bind_ip konneka.org --port ' + (process
.env.MONGO_PORT || 27017) + ' --dbpath C:/MongoDB/data/db';
                } has no method 'replace' Use --force to continue.

Aborted due to warnings.

I should note I'm using Windows with Git Bash.

The function/string dichotomy of the command is outlined in grunt-shell's README, but not grunt-shell-spawn's, which is partly why I'm posting. Is this a bug, or does grunt-shell-spawn not have this functionality? If the latter, are there any plans to include it? Thanks in advance.

Anyone interested in taking over the project?

I'm sorry everyone, but I haven't been able to check any issues / pull requests for a very long time.

Would anyone be interested in taking over this project and npm package ownership?
Maybe @SergKr, would you be interested? As I've seen you've had the most activity so far.

npm audio security report, sync-exec, dependency of grunt-shell-spawn

This can be resolved by following the instructions at https://www.npmjs.com/advisories/310

root@efc557466b90:/thrift/src/lib/js# npm audit

                       === npm audit security report ===


                                 Manual Review
             Some vulnerabilities require your attention to resolve

          Visit https://go.npm.me/audit-guide for additional guidance


  Moderate        Tmp files readable by other users

  Package         sync-exec

  Patched in      No patch available

  Dependency of   grunt-shell-spawn [dev]

  Path            grunt-shell-spawn > sync-exec

  More info       https://nodesecurity.io/advisories/310

found 1 moderate severity vulnerability in 2788 scanned packages
  1 vulnerability requires manual review. See the full report for details.

Option: Disable standard output buffering

My use case for grunt-shell-spawn is to pipe the output of a command to a file. To do this, I'd like to:

  • Create a writeable stream to the output file.
  • Write to it using the stdout callback.
  • Close the stream when callback is triggered.

However, this command produces >200KB of output, which is greater than BUFF_LENGTH: https://github.com/cri5ti/grunt-shell-spawn/blob/master/tasks/shell.js#L64

As a result, this task overruns the buffer, which causes a runtime error.

I would like an option to disable standard output buffering so that I can assign a callback to the callback property without receiving this runtime error.

I can write a PR for this usecase if need be; I believe all I'll need to do is perform an extra OR here: https://github.com/cri5ti/grunt-shell-spawn/blob/master/tasks/shell.js#L64, and add a conditional around these two lines: https://github.com/cri5ti/grunt-shell-spawn/blob/master/tasks/shell.js#L117

Forward-slash use on Windows

Some windows programs need their arguments supplied using a forward slash, /port:8000. I don't seem to be able to do this, as the forward-slashes are converted to backslashes. Thus, my arguments are never supplied. This seems to plague me when trying to run microsoft's development server, WebDev.WebServer40.exe.

Fatal error: No running process for target:...

Hi, I got the following grunt configuration:

        watch: {
            proxy: {
                files: ['**/*.js'],
                tasks: ['proxy:restart']
            }
        },


        shell: {
            proxy: {
                command: 'node proxy.js',
                options: {
                    async: true,
                    canKill: true
                }
            }
        }

    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-shell-spawn');

    grunt.registerTask('proxy:start', "starts the proxy", ['shell:proxy', 'watch:proxy'])
    grunt.registerTask('proxy:stop', "starts the proxy", ['shell:proxy:kill'])
    grunt.registerTask('proxy:restart', "starts the proxy", ['proxy:stop', 'proxy:start'])

Now the proxy starts just fine. However, when changing a file shell:proxy:kill does not find the proc. I did a little debugging: where here the procs hash is filled correctly, when trying to kill it later the procs hash is empty.

Any help is greatly appreciated :)

"execSync incompatible with installed nodejs" on 0.3.1

Version 0.3.1 cannot be used on Windows:

Running "shell:check-compass-installation" (shell) task
[Error: %1 is not a valid Win32 application.
...\gruntjs\node_modules\grunt-shell-spawn\node_modules\execSync\win32\v0.10\shell.node]
Warning: execSync incompatible with installed nodejs Use --force to continue.

Aborted due to warnings.
grunt process exited with code 3

The shell.node is not a valid Win32 application.
Works perfectly fine on 0.3.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.