gistmailer

job mailer / jenkins mailer / ...

gist mailer

This set of scripts was developped to provide an easy way to post process build results for Jenkins or other continuous integration system.

It relies on the excellent node-email-templates to render HTML formatted emails, and nodemailer for sending.

Templates are fetched from git/gist repository, and are executed in the context of the build data passed to downstream Job.

Note email-templates is required but not installed by this package. If you wish to get full featureset from email-templates, you need the package locally by running npm install email-templates. The system will fallback to this implementation. May help on systems where installing juice is a problem.

Module:

var Mailer = require('gistmailer');
var mailer = new Mailer();
 
mailer
  .service('Gmail')
  .auth({ name: 'username@gmail.com', pass: 'password' })
  .url('https://gist.github.com/mklabs/eb28e58ac28a8d3ab845')
  .to('example@example.com')
  .file('./build.json')
  .run(function(err) {
    if (!err) return;
 
    console.error('Error running mailer', err);
    process.exit(1);
  });

Command:

 
 Usage: gistmailer [options] <file>
 
 Options:
 
   -h, --help            output usage information
   -V, --version         output the version number
   -u, --user [user]     SMTP auth user configuration
   -p, --pass [pass]     SMTP auth password configuration
   -s, --service [type]  One of the nodemailer service
   -H, --host [host]     SMTP hostname (not needed with service)
   -P, --port [port]     SMTP port (not needed with service)
   -u, --url [url]       Full GIT clone url, or a gist ID
   -t, --to [to]         Destination email
 

Scripts

 
# Iterates over JOB_MAILS
$ npm run gistmailer send
 
# Send email based on environment variables
$ MAIL_TO=example@example.com npm run gistmailer asserts
 

Use env vars, useful for CI based run. They'll overwrite any CLI arguments / flags.

  • UPSTREAM_DATA - Absolute path to build data
  • MAIL_USER - The email auth credential for username
  • MAIL_PASSWORD - The email auth credential for password
  • MAIL_SERVICE - One of the well known smtp services for nodemailer
  • MAIL_HOST - SMTP hostname server (not needed with service)
  • MAIL_PORT - SMTP port (defaults to 25, not needed with service)

See https://github.com/andris9/Nodemailer#setting-up-smtp

job/jenkins.xml file is a Jenkins Job template that can be used to quickly setup a Job to automatically send mail based on upstream data.

# Install plugins. Change name=mailer to change the job name
$ curl -X POST -H 'Content-Type: application/xml' $JEKINS_URL/createItem?name=mailer --data-binary @scripts/jenkins.xml

It has the following job parameters (then available as environment variable to shell scripts):

  • JOB_MAILS - List of destination emails for any failed asserts, space separated.
  • MAIL_SERVICE - https://github.com/andris9/Nodemailer#well-known-services-for-smtp
  • MAIL_USER - SMTP auth user
  • MAIL_PASSWORD - SMTP auth password
  • MAIL_SUBJECT - Email subject
  • UPSTREAM_DATA - Just for test the default value. Passed from upstream. Absolute path to the build data (including asserts)
  • TEMPLATE_URL - Full Git clone URL, or a gist id (Default: eb28e58ac28a8d3ab845)
  • DEBUG - Debug lvl option

The build.json file used is following the above structure. The relevant build properties used here is asserts.

[{
  url: "Relevant URL (can be the Job URL)"
  asserts: {
    rules: {
      assertKey: value
    },
 
    failedCount: 0,
    failedAsserts: []
  }
}, {
  url: "Relevant URL (can be the Job URL)"
  asserts: {
    rules: {
      assertKey: value,
      anotherOne: value
    },
 
    failedCount: 0,
    failedAsserts: []
  }
}]

Git

Git#init.

this.git = new Git();

Git#directory.

this.git = new Git();
assert.equal(this.git.directory(), path.join('./tmp/templates/default'));
assert.equal(this.git.directory('foo'), path.join('./tmp/templates/foo'));
assert.equal(this.git.directory(url), path.join('./tmp/templates/eb28e58ac28a8d3ab845'));

Clones url.

this.git = this.git || new Git();
this.git.clone(url, done);

and init template.

fs.stat(this.git.directory(url), done);

Mailer

Mailer#init.

var mailer = new Mailer();
// Config 
mailer
  .file(path.join(__dirname, 'fixtures/build.json'))
  .url(url)
  .to('foo@bar.com')
  .service('Gmail')
  .auth({ name: 'foo', pass: 'bar' });
assert.deepEqual(mailer.config(), {
  service: 'Gmail',
  auth: {
    name: 'foo',
    pass: 'bar'
  }
});

Prop

props well.

var obj = Object.create({});
props.forEach(prop(obj));
props.forEach(assertProp(obj));

Object.create.

var obj = Object.create({});
prop(obj)('src');
prop(obj)('dest');
prop(obj)('output');
obj
  .src('file.json')
  .dest('tmp/output.json')
  .output('log/stdout.log')
assert.equal(obj.src(), 'file.json');
assert.equal(obj.dest(), 'tmp/output.json');
assert.equal(obj.output(), 'log/stdout.log');

new Stuff().

function Stuff() {}
prop(Stuff.prototype)('src');
prop(Stuff.prototype)('dest');
prop(Stuff.prototype)('output');
var stuff = new Stuff();
stuff
  .src('file.json')
  .dest('tmp/output.json')
  .output('log/stdout.log')
assert.equal(stuff.src(), 'file.json');
assert.equal(stuff.dest(), 'tmp/output.json');
assert.equal(stuff.output(), 'log/stdout.log');

Hash.

var obj = {};
prop(obj)('src');
prop(obj)('dest');
prop(obj)('output');
obj
  .src('file.json')
  .dest('tmp/output.json')
  .output('log/stdout.log')
assert.equal(obj.src(), 'file.json');
assert.equal(obj.dest(), 'tmp/output.json');
assert.equal(obj.output(), 'log/stdout.log');

_prop - default.

var obj = {};
prop(obj)('file');
obj.file('file.json');
assert.equal(obj.file(), 'file.json');
assert.equal(obj._file, 'file.json');

attr - use this.attributes hash.

var obj = {};
prop(obj, { strategy: 'attr' })('file');
obj.file('file.json');
assert.equal(obj.file(), 'file.json');
assert.equal(obj.attributes.file, 'file.json');
obj = {};
prop(obj, { strategy: prop.attr })('file');
obj.file('file.json');
assert.equal(obj.file(), 'file.json');
assert.equal(obj.attributes.file, 'file.json');

custom.

// Using this.options object 
function opts(namevalue) {
  this.options = this.options || {};
  if (!value) return this.options[name];
  this.options[name] = value;
  return this;
}
var obj = {};
prop(obj, { strategy: opts })('file');
obj.file('file.json');
assert.equal(obj.file(), 'file.json');
assert.equal(obj.options.file, 'file.json');

Validate inputs.

var obj = {};
prop(obj, {
  validatefunction (namevalue) {
    if (!value) return;
    if (!value.name) return;
    if (!value.pass) return;
    return true;
  }
})('auth');
obj.auth({ foo: 'bar' });
assert.ok(typeof obj.auth() === '');
obj.auth({
  name: 'foo',
  pass: 'bar'
});
assert.deepEqual(obj.auth(), { name: 'foo', pass: 'bar' });

  • [] rename into notif-mailer, build notif module

...