Grunt notify: https://github.com/dylang/grunt-notify is great. However, it seems a bit limited. As far as I can tell all my messages need to be pre-generated. So the first question is how can I generate notifications?
Next, It seems that grunt notify triggers based on error or success of some task. I guess based on std in/out/err? The problem where this breaks down is if some task doesn't use these. grunt compass doesn't use stderr if there are compile errors. So how can I run grunt notify when it has an error? This then leads to the next question. How can I grab the console output or stderr from a grunt task?
First of all, use growl. It is easy and flexible to use. To install growl:
npm install growl --save-dev
Then you need to hook into the stderr/out stream of the process. This way you can create a notification each time a new message arrives at the stderr/out stream.
This is what I've created. I've made a CommonJs module which adds hooks to:
grunt.fail.warn(), grunt.fail.fatal()
grunt.log.warn(), grunt.log.error()
grunt.warn()
process.stderr.write()
process.stdout.write() (error lines)
(child) process.stderr.write()
(child) process.stdout.write() (error lines)
It works more or less, but it may need some tweaking.
tasks/lib/notify.js
(function (module) {
var grunt = require('grunt'),
growl = require('growl'),
Buffer = require('buffer').Buffer;
function notify(obj, title) {
if (obj) {
var message = Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj);
var msg = grunt.log.uncolor(message);
if (msg.length > 0) {
growl(msg, {
title: title,
image: 'Console'
});
}
}
}
// add a hook to grunt.fail.warn(), grunt.fail.fatal()
['warn', 'fatal'].forEach(function (level) {
grunt.util.hooker.hook(grunt.fail, level, function(obj) {
notify(obj);
});
});
// add a hook to grunt.log.warn(), grunt.log.error()
['warn', 'error'].forEach(function (level) {
grunt.util.hooker.hook(grunt.log, level, function(obj) {
notify(obj, level);
});
});
// add a hook to grunt.warn()
grunt.util.hooker.hook(grunt, 'warn', function(obj) {
notify(obj, 'warn');
});
// add a hook to process.stderr.write()
grunt.util.hooker.hook(process.stderr, 'write', function(obj) {
var messages = grunt.log.uncolor((Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj))).split('\n');
messages.forEach(function (message) {
notify(message, 'stderr');
});
});
// add a hook to process.stdout.write() (only error lines)
grunt.util.hooker.hook(process.stdout, 'write', function(obj) {
var messages = grunt.log.uncolor((Buffer.isBuffer(obj) ? obj.toString() : (obj.message || obj))).split('\n');
messages.forEach(function (message) {
if (message && message.indexOf('error ') > -1) {
notify(message, 'stdout');
}
});
});
// add a hook to child process stdout/stderr write() (only error lines)
grunt.util.hooker.hook(grunt.util, 'spawn', {
post: function(child) {
child.stderr.on('data', function (data) {
var messages = grunt.log.uncolor(data.toString()).split('\n');
messages.forEach(function (message) {
notify(message, 'stderr');
});
});
child.stdout.on('data', function (data) {
var messages = grunt.log.uncolor(data.toString()).split('\n');
messages.forEach(function (message) {
if (message && message.indexOf('error ') > -1) {
notify(message, 'stdout');
}
});
});
}
});
}) (module);
Then you need to include it in your Gruntfile.js with a require statement:
module.exports = function (grunt) {
grunt.initConfig({
...
});
require('./tasks/lib/notify');
...
};
PS I've placed the file in tasks/lib/notify.js, but feel free to place it somewhere else.
Related
I have the following code in my index.js file. This was my attempt to implement an external task worker for Camunda in node js after cloning this repo.
var Workers = require('camunda-worker-node');
var Backoff = require('camunda-worker-node/lib/backoff');
try {
console.log('In try ');
console.log(Backoff)
var engineEndPoint = process.env.ENGINE_URL || 'http://localhost:8080/engine-rest';
}
catch (e) {
console.log(e)
}
console.log("Out of try");
var uuid = require('uuid');
var workers = new Workers(engineEndPoint);
try {
Backoff(workers);
}
catch (e) {
console.log(e);
}
workers.subscribe('FightTribe', ['name'], function (context, callback) {
console.log("in worker subscribe");
if (Math.random() > 0.9) {
console.log('German Tribe Fighter, The Battle is lost.');
return callback(null, {
variables: {
legionStatus: "defeated"
}
});
}
console.log('German Tribe Fighter, The Battle is WON.');
callback(null, {
variables: {
legionStatus: 'Victorious'
}
})
});
My camunda process engine is up and running. On starting up index.js the process hangs at the function call Backoff(workers).
In an attempt to debug i placed console.log("in worker subscribe") in workers.subscribe, but the execution never reaches there.
I have attached my working directory. I am not sure what the issue is.
In ,winston when I tried logging by passing a mongoose query result as a metadata argument, winston just spit out like a thousand lines of log before the task quit.
So for a log like this :
tSchool.findById(bus.schoolid,function(err,school){
winston.info('loaded school',school);
});
Here's a small piece of whats get output :
return _next.apply(this, arguments);
}, remove=function wrappedPointCut() {
var args = [].slice.call(arguments);
var lastArg = args.pop();
var fn;
var originalStack = new Error().stack;
var $results;
if (lastArg && typeof lastArg !== 'function') {
args.push(lastArg);
} else {
fn = lastArg;
}
var promise = new Promise.ES6(function(resolve, reject) {
args.push(function(error) {
if (error) {
// gh-2633: since VersionError is very generic, take the
// stack trace of the original save() function call rather
// than the async trace
if (error instanceof VersionError) {
error.stack = originalStack;
}
_this.$__handleReject(error);
reject(error);
return;
}
// There may be multiple results and promise libs other than
// mpromise don't support passing multiple values to `resolve()`
$results = Array.prototype.slice.call(arguments, 1);
resolve.apply(promise, $results);
});
_this[newName].apply(_this, args);
});
if (fn) {
if (_this.constructor.$wrapCallback) {
fn = _this.constructor.$wrapCallback(fn);
}
return promise.then(
function() {
process.nextTick(function() {
fn.apply(null, [null].concat($results));
});
},
function(error) {
process.nextTick(function() {
fn(error);
});
});
}
return promise;
}
So I wanted to know a few things :
Why is passing a mongoose query result, which is supposed to be just a small json object, printing such gibberish?
Will this happen for other objects also - like the err objects in callbacks .etc?
How do I prevent this? Checking each and every log statement to ensure no query results are passed is not very practical.
Thanks in advance.
Update :
Issues #862, #474 and #914 are tracking/related to this problem, but there hasn't been much progress.
This issue has been fixed in pull request #977 of Winston. you can check out the details on the PR page.
I have an weird error and can't find the cause of it for the last few hours...
I have a meteor app, that scrapes some webpages for information and everything works fine as long as I use reuqest and cheerio for static pages, but now I have a dynamic site and I wanted to use phantomjs, casperjs and spookyjs for this one, but here I get some bug...
My code is as follows, I import the npm modules at the start:
if (Meteor.isServer) {
var cheerio = Meteor.npmRequire('cheerio');
var request = Meteor.npmRequire('request');
var phantomJS = Meteor.npmRequire('phantomjs');
var spooky = Meteor.npmRequire('spooky');
And sometime later I want to use spooky to scrape some webpage:
spooky.start("https://www.coursera.org/");
spooky.then( function () {
this.fill("form", {email: user, password: pass}, true);
});`
But as soon as I call the method I get the following error message:
20150224-21:16:39.100(-5)? Exception while invoking method 'getLecturesCoursera' TypeError: Object function Spooky(options, callback) {
....
I20150224-21:16:39.281(-5)? } has no method 'start'
I20150224-21:16:39.281(-5)? at [object Object].Meteor.methods.getLecturesCoursera (app/moocis.js:72:14)
I am doing something completly wrong and I have no clue why it isn't working...
I tried to verify that spookyjs and phantomjs are installed correctly in my app, but that isn't as easy as it sounds for someone who uses them for the first time...
Like normally with spooky you have to create a new Spooky Object before you can start and run it...
if (Meteor.isServer) {
Meteor.startup( function () {
var Spooky = Meteor.npmRequire('spooky');
var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug',
verbose: true,
ignoreSslErrors: true,
sslProtocol: 'tlsv1'
}
}, function (err) {
if (err) {
e = new Error('Failed to initialize SpookyJS');
e.details = err;
throw e;
}
spooky.start(
'https://www.google.com');
spooky.then(function () {
this.emit('hello', 'Hello, from ' + this.evaluate(function () {
return document.title;
}));
});
spooky.run();
});
spooky.on('error', function (e, stack) {
console.error(e);
if (stack) {
console.log(stack);
}
});
spooky.on('hello', function (greeting) {
console.log(greeting);
});
spooky.on('log', function (log) {
if (log.space === 'remote') {
console.log(log.message.replace(/ \- .*/, ''));
}
});
})
}
I am using Yeoman to generate some projects and also grunt-tasks.
Now I would also like to test the generated grunt tasks using Mocha, but I only find some information how to use Mocha tests in Grunt ;-)
Can anybody help?
Not an elegant solution but I took the approach of installing my dependencies (npm install) and consequently running the corresponding grunt task (for e.g. grunt less) and then writing test logic post that operation. I've used nested exec calls for this.
describe('less grunt tasks tests', function () {
var prompts = {
workFolder: 'temp',
fiddleDesc: 'mocha test'
};
var testGlobal = {};
beforeEach(function(done) {
testGlobal.app = helpers.run(path.join(__dirname, '../app'))
.inTmpDir(function(dir, err) {
if(err) { done(err); return; }
testGlobal.dir = dir;
// console.log(dir);
})
.withArguments(['skip-install'])
.withOptions({ less: true })
.withPrompts(prompts)
.on('end', function(){
done();
});
});
it('should modify app/styles/style.css', function(done){
this.timeout(60000 * 10); //10 minutes - my network is f**ked up
var opts = {
cwd : testGlobal.dir,
env: process.env,
detached: true
};
var gen = testGlobal.app.generator;
var devdeps = gen.devDependencies.join(' ');
var rootPath = testGlobal.dir;
var getPath = function(fpath) {
var s = path.join(rootPath, fpath);
// console.log(s); ;
return s;
};
exec('npm install ' + devdeps, opts, function(err, stdout, stderr) {
if(err) {
done(err);
return;
}
var h1 = fs.readFileSync(getPath('app/less/h1.less'), 'utf8');
var css = fs.readFileSync(getPath('app/styles/style.css'), 'utf8');
// expect(css).to.not.contain(h1);
expect(css).to.not.contain('h1');
exec('grunt less', opts, function(e, out, serr){
if(e) {
done(e);
return;
}
// console.log(out);
var h1 = fs.readFileSync(getPath('app/less/h1.less'), 'utf8');
var css = fs.readFileSync(getPath('app/styles/style.css'), 'utf8');
// expect(css).to.contain(h1); //this expect fails since for some reason \r are stripped out
expect(css).to.contain('h1');
done();
});
});
});
});
For more reference you can see more test code in the repo I contribute against.
Ps: I'd appreciate your comments on the approach I've taken.
I am trying to elegantly run five git commands one after the other while maintaining the ability to catch error and progress:
git status
git pull
git add .
git commit -am "commit message"
git push
Open source note: I've studied different node-git libraries and decided for different reasons to implement it on my own.
Using Q, I've created a deferred method to run child processes:
var exec = require('child_process').exec,
path = require('path'),
Q = require('q'),
gitPath = path.resolve(__dirname + "/../projects/langs");
function run(command) {
var deferred = Q.defer();
exec(command, {cwd: gitPath}, function puts(error, stdout, stderr) {
if (error) {
deferred.reject(new Error(error));
} else {
deferred.resolve(stdout);
}
});
return deferred.promise;
}
However, I want to avoid the Pyramid of doom:
function option1() {
// Pyramid of doom
run("git status").then(function (output) {
console.log(output);
run("git pull").then(function (output) {
console.log(output);
run("git add .").then(function (output) {
console.log(output);
// etc.
});
});
});
}
And doesn't feel quite elegant:
function options1a() {
// Pyramid of doom
run("git status").then(function (output) {
console.log(output);
run("git pull");
}).then(function (output) {
console.log(output);
run("git add .")
}).then(function (output) {
console.log(output);
});
}
I saw a third option but can't seem to get it to work:
function promiseWaterfall(tasks) {
var resolvedPromise = Q(undefined);
var finalTaskPromise = tasks.reduce(function (prevTaskPromise, task) {
return prevTaskPromise.then(task);
}, resolvedPromise); // initial value
return finalTaskPromise;
}
promiseWaterfall([
run("git status"),
run("git pull"),
run("git add .")
]).then(function () {
console.log(arguments);
});
And I'm playing with a fourth option of using the async library:
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
But this seems to take me towards a non-promises path.
How do I get it to work elegantly? any best practices?
I'm familiar with when.js promises so I will answer your question with that promise library. It provides helper functions for this sort of thing similar to the callback based async lib. Check out their API documentation for more examples.
In the following code I am using the when/sequence module to perform what you are looking for. I've also modified your code organization a little to keep things somewhat modular (e.g. not embedding the git cwd inside of the run function in your example).
Here is a fully working implementation. Make sure to change out the git cwd to your own git repository as it's currently pointing to one of my own.
var exec = require('child_process').exec
, when = require('when')
, sequence = require('when/sequence');
// simple promise wrapper for exec
function exec_p(command, options) {
options = options || {};
var defer = when.defer();
exec(command, options, function(error, stdout, stderr) {
return error
? defer.reject(stderr + new Error(error.stack || error))
: defer.resolve(stdout);
});
return defer.promise;
}
// Some simple git wrapper
function Git(config) {
var self = this;
self.config = config;
return function(gitCommand) {
return exec_p('git ' + gitCommand, self.config);
};
}
// create a new instnace of git and specify our options
var git = new Git({ cwd: "/home/trev/git/tsenior" });
// we can now use sequence & our newly created git wrapper to easily
// can things in order one after another
sequence([
function() { return git('status'); },
function() { return git('status'); },
function() { return git('status'); },
function() { return git('status'); }
]).then(function(results) { // handle the results here
console.log(results);
}).otherwise(function(error) { // handle any errors here
console.error(error.stack || error);
process.exit(1);
});
The code provided doesn't console.log after every step (it just logs out the results at the end), but it can be easily modified to do so.