mecano
Version:
Common functions for system deployment.
475 lines (463 loc) • 15.8 kB
JavaScript
// Generated by CoffeeScript 1.9.1
var diff, eco, fs, misc, nunjucks, path, quote, string, uid_gid;
module.exports = function(options, callback) {
var append, between, content, destination, destinationHash, destinationStat, do_backup, do_chown_chmod, do_diff, do_end, do_eof, do_read_destination, do_read_source, do_render, do_replace_partial, do_skip_empty_lines, do_write, from, j, len, modified, ref, to, w, write;
modified = false;
if (!((options.source || (options.content != null)) || options.replace || ((ref = options.write) != null ? ref.length : void 0))) {
return callback(new Error('Missing source or content'));
}
if (options.source && options.content) {
return callback(new Error('Define either source or content'));
}
if (!options.destination) {
return callback(new Error('Missing destination'));
}
if (options.content && Buffer.isBuffer(options.content)) {
options.content = options.content.toString();
}
if (options.diff == null) {
options.diff = options.diff || !!options.stdout;
}
if (options.engine == null) {
options.engine = 'eco';
}
switch (options.eof) {
case 'unix':
options.eof = "\n";
break;
case 'mac':
options.eof = "\r";
break;
case 'windows':
options.eof = "\r\n";
break;
case 'unicode':
options.eof = "\u2028";
}
destination = null;
destinationHash = null;
content = null;
from = to = between = null;
append = options.append;
write = options.write;
if (write == null) {
write = [];
}
if ((options.from != null) || (options.to != null) || (options.match != null) || (options.replace != null) || (options.before != null)) {
write.push({
from: options.from,
to: options.to,
match: options.match,
replace: options.replace,
append: options.append,
before: options.before
});
}
for (j = 0, len = write.length; j < len; j++) {
w = write[j];
if ((w.from == null) && (w.to == null) && (w.match == null) && (w.replace != null)) {
w.match = w.replace;
}
}
do_read_source = function() {
var source, ssh;
if (options.content != null) {
content = options.content;
if (typeof content === 'number') {
content = "" + content;
}
return do_read_destination();
}
source = options.source || options.destination;
if (typeof options.log === "function") {
options.log("Mecano `write`: force local source is \"" + (options.local_source ? 'true' : 'false') + "\" [DEBUG]");
}
if (typeof options.log === "function") {
options.log("Mecano `write`: source is \"" + options.source + "\" [DEBUG]");
}
ssh = options.local_source ? null : options.ssh;
return fs.exists(ssh, source, function(err, exists) {
if (err) {
return callback(err);
}
if (!exists) {
if (options.source) {
return callback(new Error("Source does not exist: " + (JSON.stringify(options.source))));
}
content = '';
return do_read_destination();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: read source [DEBUG]");
}
return fs.readFile(ssh, source, 'utf8', function(err, src) {
if (err) {
return callback(err);
}
content = src;
return do_read_destination();
});
});
};
destinationStat = null;
do_read_destination = (function(_this) {
return function() {
var do_mkdir, do_read, exists;
if (typeof options.destination === 'function') {
return do_render();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: destination is \"" + options.destination + "\" [DEBUG]");
}
exists = function() {
if (typeof options.log === "function") {
options.log("Mecano `write`: stat destination [DEBUG]");
}
return fs.stat(options.ssh, options.destination, function(err, stat) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
return do_mkdir();
}
if (err) {
return callback(err);
}
destinationStat = stat;
if (stat.isDirectory()) {
options.destination = options.destination + "/" + (path.basename(options.source));
return fs.stat(options.ssh, options.destination, function(err, stat) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
return do_render();
}
if (err) {
return callback(err);
}
if (!stat.isFile()) {
return callback(new Error("Destination is not a file: " + options.destination));
}
destinationStat = stat;
return do_read();
});
} else {
return do_read();
}
});
};
do_mkdir = function() {
return _this.mkdir({
destination: path.dirname(options.destination),
uid: options.uid,
gid: options.gid,
mode: options.mode,
not_if_exists: path.dirname(options.destination)
}, function(err, created) {
if (err) {
return callback(err);
}
return do_render();
});
};
do_read = function() {
if (typeof options.log === "function") {
options.log("Mecano `write`: read destination [DEBUG]");
}
return fs.readFile(options.ssh, options.destination, 'utf8', function(err, dest) {
if (err) {
return callback(err);
}
if (options.diff) {
destination = dest;
}
destinationHash = string.hash(dest);
return do_render();
});
};
return exists();
};
})(this);
do_render = function() {
var err;
if (options.context == null) {
return do_skip_empty_lines();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: rendering with " + options.engine + " [DEBUG]");
}
try {
switch (options.engine) {
case 'nunjunks':
content = (new nunjucks.Environment()).renderString(content.toString(), options.context);
break;
case 'eco':
content = eco.render(content.toString(), options.context);
break;
default:
return callback(Error("Invalid engine: " + options.engine));
}
} catch (_error) {
err = _error;
return callback(typeof err === 'string' ? Error(err) : err);
}
return do_skip_empty_lines();
};
do_skip_empty_lines = function() {
if (options.skip_empty_lines == null) {
return do_replace_partial();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: skip empty lines [DEBUG]");
}
content = content.replace(/(\r\n|[\n\r\u0085\u2028\u2029])\s*(\r\n|[\n\r\u0085\u2028\u2029])/g, "$1");
return do_replace_partial();
};
do_replace_partial = function() {
var before, from_index, k, len1, linebreak, opts, orgContent, pos, posoffset, res;
if (!write.length) {
return do_eof();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: replace [DEBUG]");
}
for (k = 0, len1 = write.length; k < len1; k++) {
opts = write[k];
if (opts.match) {
if (opts.match == null) {
opts.match = opts.replace;
}
if (typeof opts.match === 'string') {
opts.match = RegExp(quote(opts.match), 'mg');
}
if (!(opts.match instanceof RegExp)) {
return Error("Invalid match option");
}
if (opts.match.test(content)) {
content = content.replace(opts.match, opts.replace);
append = false;
} else if (opts.before && typeof opts.replace === 'string') {
if (typeof opts.before === "string") {
opts.before = new RegExp("^.*" + opts.before + ".*$", 'mg');
}
if (opts.before instanceof RegExp) {
posoffset = 0;
orgContent = content;
while ((res = opts.before.exec(orgContent)) !== null) {
pos = posoffset + res.index;
content = content.slice(0, pos) + opts.replace + '\n' + content.slice(pos);
posoffset += opts.replace.length + 1;
if (!opts.before.global) {
break;
}
}
before = false;
} else {
linebreak = content.length === 0 || content.substr(content.length - 1) === '\n' ? '' : '\n';
content = opts.replace + linebreak + content;
append = false;
}
} else if (opts.append && typeof opts.replace === 'string') {
if (typeof opts.append === "string") {
opts.append = new RegExp("^.*" + (quote(opts.append)) + ".*$", 'mg');
}
if (opts.append instanceof RegExp) {
posoffset = 0;
orgContent = content;
while ((res = opts.append.exec(orgContent)) !== null) {
pos = posoffset + res.index + res[0].length;
content = content.slice(0, pos) + '\n' + opts.replace + content.slice(pos);
posoffset += opts.replace.length + 1;
if (!opts.append.global) {
break;
}
}
append = false;
} else {
linebreak = content.length === 0 || content.substr(content.length - 1) === '\n' ? '' : '\n';
content = content + linebreak + opts.replace;
append = false;
}
} else {
continue;
}
} else if (opts.before === true) {
} else if (opts.from || opts.to) {
if (opts.from && opts.to) {
from = RegExp("(^" + (quote(opts.from)) + "$)", "m").exec(content);
to = RegExp("(^" + (quote(opts.to)) + "$)", "m").exec(content);
if ((from != null) && (to == null)) {
if (typeof options.log === "function") {
options.log("Mecano `write`: found 'from' but missing 'to', skip writing [WARN]");
}
} else if ((from == null) && (to != null)) {
if (typeof options.log === "function") {
options.log("Mecano `write`: missing 'from' but found 'to', skip writing [WARN]");
}
} else if ((from == null) && (to == null)) {
if (opts.append) {
content += '\n' + opts.from + '\n' + opts.replace + '\n' + opts.to;
append = false;
} else {
if (typeof options.log === "function") {
options.log("Mecano `write`: missing 'from' and 'to' without append, skip writing [WARN]");
}
}
} else {
content = content.substr(0, from.index + from[1].length + 1) + opts.replace + '\n' + content.substr(to.index);
append = false;
}
} else if (opts.from && !opts.to) {
from = RegExp("(^" + (quote(opts.from)) + "$)", "m").exec(content);
if (from != null) {
content = content.substr(0, from.index + from[1].length) + '\n' + opts.replace;
} else {
if (typeof options.log === "function") {
options.log("Mecano `write`: missing 'from', skip writing [WARN]");
}
}
} else if (!opts.from && opts.to) {
from_index = 0;
to = RegExp("(^" + (quote(opts.to)) + "$)", "m").exec(content);
if (to != null) {
content = opts.replace + '\n' + content.substr(to.index);
} else {
if (typeof options.log === "function") {
options.log("Mecano `write`: missing 'to', skip writing [WARN]");
}
}
}
}
}
return do_eof();
};
do_eof = function() {
var char, i, k, len1;
if (options.eof == null) {
return do_diff();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: eof [DEBUG]");
}
if (options.eof === true) {
for (i = k = 0, len1 = content.length; k < len1; i = ++k) {
char = content[i];
if (char === '\r') {
options.eof = content[i + 1] === '\n' ? '\r\n' : char;
break;
}
if (char === '\n' || char === '\u2028') {
options.eof = char;
break;
}
}
if (options.eof === true) {
options.eof = '\n';
}
}
if (!string.endsWith(content, options.eof)) {
if (typeof options.log === "function") {
options.log("Mecano `write`: add eof [INFO]");
}
content += options.eof;
}
return do_diff();
};
do_diff = function() {
if (destinationHash === string.hash(content)) {
return do_chown_chmod();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: file content has changed [WARN]");
}
diff(content, destination, options);
return do_backup();
};
do_backup = (function(_this) {
return function() {
var backup;
if (!(options.backup && destinationHash)) {
return do_write();
}
if (typeof options.log === "function") {
options.log("Mecano `write`: create backup [WARN]");
}
backup = typeof options.backup === 'string' ? options.backup : "." + (Date.now());
return _this.copy({
ssh: options.ssh,
source: options.destination,
destination: "" + options.destination + backup
}, function(err) {
if (err) {
return callback(err);
}
return do_write();
});
};
})(this);
do_write = function() {
if (typeof options.destination === 'function') {
if (typeof options.log === "function") {
options.log("Mecano `write`: write destination with user function [INFO]");
}
options.destination(content);
return do_end();
} else {
if (typeof options.log === "function") {
options.log("Mecano `write`: write destination [INFO]");
}
if (append) {
if (options.flags == null) {
options.flags = 'a';
}
}
return uid_gid(options, function(err) {
if (err) {
return callback(err);
}
return fs.writeFile(options.ssh, options.destination, content, options, function(err) {
if (err) {
return callback(err);
}
if (typeof options.log === "function") {
options.log("Mecano `write`: content has changed [INFO]");
}
modified = true;
return do_end();
});
});
}
};
do_chown_chmod = (function(_this) {
return function() {
return _this.chown({
destination: options.destination,
stat: destinationStat,
uid: options.uid,
gid: options.gid,
"if": (options.uid != null) || (options.gid != null)
}).chmod({
destination: options.destination,
stat: destinationStat,
mode: options.mode,
"if": options.mode != null
}).then(function(err, status) {
if (err) {
return callback(err);
}
if (status) {
modified = true;
}
return do_end();
});
};
})(this);
do_end = function() {
return callback(null, modified);
};
return do_read_source();
};
fs = require('ssh2-fs');
path = require('path');
eco = require('eco');
nunjucks = require('nunjucks/src/environment');
quote = require('regexp-quote');
misc = require('./misc');
diff = require('./misc/diff');
string = require('./misc/string');
uid_gid = require('./misc/uid_gid');