Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run grunt task asynchronously before another task

I have a gruntfile setup so that I can develop my local angularjs frontend while forwarding all api requests to a java middle tier hosted seperately on the network.

This works great except the location of the server changes every few days and I have to continuously update the gruntfile with the latest server location.

The latest server location can be found by a URL shortening service that forwards to the correct location, so I can fetch it with this grunt task/node.js code:

grunt.registerTask('setProxyHost', 'Pings the url shortener to get the latest test server', function() {
    request('http://urlshortener/devserver', function(error, response, body) {
        if (!error) {
            var loc = response.request.uri.href;
            if (loc.slice(0, 7) === 'http://') {
                proxyHost = loc.slice(7, loc.length - 1);
            }
        }
    });
});

Of course this is asynchonous, and when I run it, grunt has already setup the proxy by the time the request completes.

How can I run this nodejs request synchronously or block grunt until it completes? This is just for development so hacky solutions welcomed.

Thanks

EDIT:

Great answer Cory, that has mostly solved the problem as grunt now waits for the task to complete before continuing. One last issue is that I can't access that config from the initConfig for setting the proxy as initConfig runs first:

module.exports = function(grunt) {
[...]
    grunt.initConfig({
        connect: {
            proxies: [{
                host: grunt.config.get('proxyHost')

This post (Access Grunt config data within initConfig()) outlines the issue but I'm not sure how I could run the request synchronously outside of a task?

EDIT2 [solved]:

Corys answer + this post Programmatically pass arguments to grunt task? solved the issue for me.

module.exports = function(grunt) {
[...]
    grunt.initConfig({
        connect: {
           proxies: [{
                host: '<%= proxyHost %>',
like image 853
Aidan Avatar asked Dec 16 '14 17:12

Aidan


1 Answers

Instead of running the task synchronously, you can easily run the task asynchronously & share data between tasks through the grunt config object.


1. Run a task asynchronously

To run a task asynchronously, you must first inform Grunt that the task is going to be async by calling this.async() This async call returns a "done function" that you'll use to tell Grunt whether the task has passed or failed. You can complete the task by passing the handler true and fail it by passing it an error, or false.

Async Task:

module.exports = function(grunt) {
    grunt.registerTask('foo', 'description of foo', function() {
        var done = this.async();

        request('http://www...', function(err, resp, body) {
            if ( err ) {
                done(false); // fail task with `false` or Error objects
            } else {
                grunt.config.set('proxyHost', someValue);
                done(true); // pass task
            }
        });
    });
}


2. Share data between tasks

That grunt.config.set() bit (in the code above) is probably the easiest way to share values between tasks. Since all tasks share the same grunt config, you simply set a property on the config and then read it from the following tasks via grunt.config.get('property')

Following Task

module.exports = function(grunt) {
    grunt.registerTask('bar', 'description of bar', function() {
        // If proxy host is not defined, the task will die early.
        grunt.config.requires('proxyHost');
        var proxyHost = grunt.config.get('proxyHost');
        // ...
    });
}

The grunt.config.requires bit will inform grunt that the task has config dependencies. This is useful in scenarios where tasks go untouched for a long time (very common) and the intricacies are forgotten about. If you decide on changing the async task (rename the variable? dev_proxyHost, prod_proxyHost?) the following task will gracefully inform you that proxyHost could not be found.

Verifying property proxyHost exists in config...ERROR
>> Unable to process task. 
Warning: Required config property "proxyHost" missing. Use --force to continue.


3. Your code, async

grunt.registerTask('setProxyHost', 'Pings the url shortener to get the latest test server', function() {
    var done = this.async(),
        loc,
        proxyHost;

    request('http://urlshortener/devserver', function(error, response, body) {
        if (error) {
            done(error); // error out early
        }

        loc = response.request.uri.href;
        if (loc.slice(0, 7) === 'http://') {
            proxyHost = loc.slice(7, loc.length - 1);
            // set proxy host on grunt config, so that it's accessible from the other task
            grunt.config.set('proxyHost', proxyHost);
            done(true); // success
        }
        else {
            done(new Error("Unexpected HTTPS Error: The devserver urlshortener unexpectedly returned an HTTPS link! ("+loc+")"));
        }
    });
});

Then from your proxy task, you can retrieve this proxyHost value via the following

var proxyHost = grunt.config.get('proxyHost');
like image 193
Cory Danielson Avatar answered Oct 05 '22 19:10

Cory Danielson