title: Grunt hangs when executed from a node script
body: <p>I am trying to wrap the execution of a grunt task inside another node script (for build simple integrations) and am having a problem getting all the tasks to run. Here's my very simple test script:</p>
  
  <pre><code>'use strict';
  var path = require('path');
  var g = require('grunt');
  g.tasks();
  </code></pre>
  
  <p>And here is the output:</p>
  
  <pre><code>-&gt; % node test.js
      Running "jshint:all" (jshint) task
      &gt;&gt; 50 files lint free.
  
      Running "clean:server" (clean) task
  
      Running "concurrent:test" (concurrent) task
  </code></pre>
  
  <p>The process is stuck on the concurrent tasks, and pegs the cpu at 100%. It seems to do this for any concurrent task. If it skips the :test task it fails at another one later on with compass and a few other things.</p>
  
  <p>test.js is in the same directory as my Gruntfile.js. If I run just 'grunt' it complete the process fine.</p>
  
  <p>Gruntfile.js: Pretty stock yeoman Gruntfile</p>
  
  <pre><code>// Generated on 2013-07-29 using generator-angular 0.3.1
  'use strict';
  var LIVERELOAD_PORT = 35729;
  var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
  var mountFolder = function(connect, dir) {
  return connect.static(require('path').resolve(dir));
  };
  
  // # Globbing
  // for performance reasons we're only matching one level down:
  // 'test/spec/{,*/}*.js'
  // use this if you want to recursively match all subfolders:
  // 'test/spec/**/*.js'
  
  module.exports = function(grunt) {
  // load all grunt tasks
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
  
  // configurable paths
  var yeomanConfig = {
      app: 'app',
      dist: 'dist'
  };
  
  try {
      yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
  } catch (e) {
  }
  
  grunt.initConfig({
      yeoman: yeomanConfig,
      watch: {
          compass: {
              files: ['&lt;%= yeoman.app %&gt;/styles/{,*/}*.{scss,sass}'],
              tasks: ['compass:server']
          },
          livereload: {
              options: {
                  livereload: LIVERELOAD_PORT
              },
              files: [
                  '&lt;%= yeoman.app %&gt;/{,*/}*.html',
                  '{.tmp,&lt;%= yeoman.app %&gt;}/styles/{,*/}*.css',
                  '{.tmp,&lt;%= yeoman.app %&gt;}/scripts/{,*/}*.js',
                  '&lt;%= yeoman.app %&gt;/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
              ]
          }
      },
      connect: {
          options: {
              port: 9000,
              // Change this to '0.0.0.0' to access the server from outside.
              hostname: 'localhost'
          },
          livereload: {
              options: {
                  middleware: function(connect) {
                      return [
                          lrSnippet,
                          mountFolder(connect, '.tmp'),
                          mountFolder(connect, yeomanConfig.app)
                      ];
                  }
              }
          },
          test: {
              options: {
                  middleware: function(connect) {
                      return [
                          mountFolder(connect, '.tmp'),
                          mountFolder(connect, 'test')
                      ];
                  }
              }
          },
          dist: {
              options: {
                  middleware: function(connect) {
                      return [
                          mountFolder(connect, yeomanConfig.dist)
                      ];
                  }
              }
          }
      },
      open: {
          server: {
              url: 'http://localhost:&lt;%= connect.options.port %&gt;'
          }
      },
      clean: {
          dist: {
              files: [{
                      dot: true,
                      src: [
                          '.tmp',
                          '&lt;%= yeoman.dist %&gt;/*',
                          '!&lt;%= yeoman.dist %&gt;/.git*'
                      ]
                  }]
          },
          server: '.tmp'
      },
      jshint: {
          options: {
              jshintrc: '.jshintrc'
          },
          all: [
              'Gruntfile.js',
              '&lt;%= yeoman.app %&gt;/scripts/{,*/}*.js',
              '!&lt;%= yeoman.app %&gt;/scripts/bootstrap.min.js'
          ]
      },
      compass: {
          options: {
              sassDir: '&lt;%= yeoman.app %&gt;/styles',
              cssDir: '.tmp/styles',
              generatedImagesDir: '.tmp/images/generated',
              imagesDir: '&lt;%= yeoman.app %&gt;/images',
              javascriptsDir: '&lt;%= yeoman.app %&gt;/scripts',
              fontsDir: '&lt;%= yeoman.app %&gt;/styles/fonts',
              importPath: '&lt;%= yeoman.app %&gt;/bower_components',
              httpImagesPath: '/images',
              httpGeneratedImagesPath: '/images/generated',
              httpFontsPath: '/styles/fonts',
              relativeAssets: false
          },
          dist: {},
          server: {
              options: {
                  debugInfo: true
              }
          }
      },
      // not used since Uglify task does concat,
      // but still available if needed
      /*concat: {
       dist: {}
       },*/
      rev: {
          dist: {
              files: {
                  src: [
                      '&lt;%= yeoman.dist %&gt;/scripts/{,*/}*.js',
                      '&lt;%= yeoman.dist %&gt;/styles/{,*/}*.css',
                      '&lt;%= yeoman.dist %&gt;/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                      '&lt;%= yeoman.dist %&gt;/styles/fonts/*'
                  ]
              }
          }
      },
      useminPrepare: {
          html: '&lt;%= yeoman.app %&gt;/index.html',
          options: {
              dest: '&lt;%= yeoman.dist %&gt;'
          }
      },
      usemin: {
          html: ['&lt;%= yeoman.dist %&gt;/{,*/}*.html', '&lt;%= yeoman.dist %&gt;/views/**/*.html'],
          css: ['&lt;%= yeoman.dist %&gt;/styles/{,*/}*.css'],
          options: {
              dirs: ['&lt;%= yeoman.dist %&gt;']
          }
      },
      imagemin: {
          dist: {
              files: [{
                      expand: true,
                      cwd: '&lt;%= yeoman.app %&gt;/images',
                      src: '{,*/}*.{png,jpg,jpeg}',
                      dest: '&lt;%= yeoman.dist %&gt;/images'
                  }]
          }
      },
      svgmin: {
          dist: {
              files: [{
                      expand: true,
                      cwd: '&lt;%= yeoman.app %&gt;/images',
                      src: '{,*/}*.svg',
                      dest: '&lt;%= yeoman.dist %&gt;/images'
                  }]
          }
      },
      cssmin: {
          // By default, your `index.html` &lt;!-- Usemin Block --&gt; will take care of
          // minification. This option is pre-configured if you do not wish to use
          // Usemin blocks.
          // dist: {
          //   files: {
          //     '&lt;%= yeoman.dist %&gt;/styles/main.css': [
          //       '.tmp/styles/{,*/}*.css',
          //       '&lt;%= yeoman.app %&gt;/styles/{,*/}*.css'
          //     ]
          //   }
          // }
      },
      htmlmin: {
          dist: {
              options: {
                  /*removeCommentsFromCDATA: true,
                   // https://github.com/yeoman/grunt-usemin/issues/44
                   //collapseWhitespace: true,
                   collapseBooleanAttributes: true,
                   removeAttributeQuotes: true,
                   removeRedundantAttributes: true,
                   useShortDoctype: true,
                   removeEmptyAttributes: true,
                   removeOptionalTags: true*/
              },
              files: [{
                      expand: true,
                      cwd: '&lt;%= yeoman.app %&gt;',
                      src: ['*.html', 'views/**/*.html'],
                      dest: '&lt;%= yeoman.dist %&gt;'
                  }]
          }
      },
      // Put files not handled in other tasks here
      copy: {
          dist: {
              files: [{
                      expand: true,
                      dot: true,
                      cwd: '&lt;%= yeoman.app %&gt;',
                      dest: '&lt;%= yeoman.dist %&gt;',
                      src: [
                          '*.{ico,png,txt}',
                          '.htaccess',
                          'bower_components/**/*',
                          'images/{,*/}*.{gif,webp}',
                          'styles/fonts/*',
                          'fonts/*'
                      ]
                  }, {
                      expand: true,
                      cwd: '.tmp/images',
                      dest: '&lt;%= yeoman.dist %&gt;/images',
                      src: [
                          'generated/*'
                      ]
                  }]
          }
      },
      concurrent: {
          server: [
              'compass:server'
          ],
          test: [
              'compass'
          ],
          dist: [
              'compass:dist',
              'imagemin',
              'svgmin',
              'htmlmin'
          ]
      },
      karma: {
          unit: {
              configFile: 'karma.conf.js',
              singleRun: true
          },
      cdnify: {
          dist: {
              html: ['&lt;%= yeoman.dist %&gt;/*.html']
          }
      },
      ngmin: {
          dist: {
              files: [{
                      expand: true,
                      cwd: '&lt;%= yeoman.dist %&gt;/scripts',
                      src: '*.js',
                      dest: '&lt;%= yeoman.dist %&gt;/scripts'
                  }]
          }
      },
      uglify: {
          dist: {
              files: {
                  '&lt;%= yeoman.dist %&gt;/scripts/scripts.js': [
                      '&lt;%= yeoman.dist %&gt;/scripts/scripts.js'
                  ]
              }
          }
      }
  });
  
  grunt.registerTask('server', function(target) {
      if (target === 'dist') {
          return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
      }
  
      grunt.task.run([
          'clean:server',
          'concurrent:server',
          'connect:livereload',
          'watch'
      ]);
  });
  
  grunt.registerTask('test', [
      'clean:server',
      'concurrent:test',
      'connect:test',
      'karma'
  ]);
  
  grunt.registerTask('build', [
      'clean:dist',
      'useminPrepare',
      'concurrent:dist',
      'concat',
      'copy',
      'ngmin',
      'uglify',
      'rev',
      'usemin'
  ]);
  grunt.registerTask('default', [
      'jshint',
      'test',
      'build'
  ]);
  };
  </code></pre>
  
  <p><strong>Workaround:</strong> If I move the tasks in the concurrent groupings to be listed separately under the registerTask the process works fine.</p>