mecano
Version:
Common functions for system deployment.
420 lines (406 loc) • 13.6 kB
JavaScript
// Generated by CoffeeScript 1.7.1
var child, chmod, chown, conditions, diff, each, eco, fs, misc, mkdir, pad, path;
fs = require('ssh2-fs');
path = require('path');
each = require('each');
eco = require('eco');
pad = require('pad');
diff = require('diff');
misc = require('./misc');
conditions = require('./misc/conditions');
child = require('./misc/child');
mkdir = require('./mkdir');
chown = require('./chown');
chmod = require('./chmod');
module.exports = function(goptions, options, callback) {
var finish, result, _ref;
_ref = misc.args(arguments, {
parallel: 1
}), goptions = _ref[0], options = _ref[1], callback = _ref[2];
result = child();
finish = function(err, written) {
if (callback) {
callback(err, written);
}
return result.end(err, written);
};
misc.options(options, function(err, options) {
var written;
if (err) {
return finish(err);
}
written = 0;
return each(options).parallel(goptions.parallel).on('item', function(options, next) {
var append, between, content, destination, destinationHash, do_backup, do_diff, do_end, do_eof, do_ownership, do_permissions, do_read_destination, do_read_source, do_render, do_replace_partial, do_write, from, modified, to, write, _ref1;
modified = false;
if (!((options.source || (options.content != null)) || options.replace || ((_ref1 = options.write) != null ? _ref1.length : void 0))) {
return next(new Error('Missing source or content'));
}
if (options.source && options.content) {
return next(new Error('Define either source or content'));
}
if (!options.destination) {
return next(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;
}
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)) {
write.push({
from: options.from,
to: options.to,
match: options.match,
replace: options.replace,
append: options.append
});
}
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("Read source: " + source + (options.local_source ? ' (local)' : ''));
}
ssh = options.local_source ? null : options.ssh;
return fs.exists(ssh, source, function(err, exists) {
if (err) {
return next(err);
}
if (!exists) {
if (options.source) {
return next(new Error("Source does not exist: \"" + options.source + "\""));
}
content = '';
return do_read_destination();
}
return fs.readFile(ssh, source, 'utf8', function(err, src) {
if (err) {
return next(err);
}
content = src;
return do_read_destination();
});
});
};
do_read_destination = function() {
var do_mkdir, do_read, exists;
if (typeof options.destination === 'function') {
return do_render();
}
if (typeof options.log === "function") {
options.log("Read destination: " + options.destination);
}
exists = function() {
return fs.stat(options.ssh, options.destination, function(err, stat) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
return do_mkdir();
}
if (err) {
return next(err);
}
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 next(err);
}
if (!stat.isFile()) {
return next(new Error("Destination is not a file: " + options.destination));
}
return do_read();
});
} else {
return do_read();
}
});
};
do_mkdir = function() {
return mkdir({
ssh: options.ssh,
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 next(err);
}
return do_render();
});
};
do_read = function() {
return fs.readFile(options.ssh, options.destination, 'utf8', function(err, dest) {
if (err) {
return next(err);
}
if (options.diff) {
destination = dest;
}
destinationHash = misc.string.hash(dest);
return do_render();
});
};
return exists();
};
do_render = function() {
if (options.context == null) {
return do_replace_partial();
}
try {
content = eco.render(content.toString(), options.context);
} catch (_error) {
err = _error;
if (typeof err === 'string') {
err = new Error(err);
}
return next(err);
}
return do_replace_partial();
};
do_replace_partial = function() {
var linebreak, opts, orgContent, pos, posoffset, res, _i, _len;
if (!write.length) {
return do_eof();
}
for (_i = 0, _len = write.length; _i < _len; _i++) {
opts = write[_i];
if (opts.match) {
if (opts.match instanceof RegExp) {
if (opts.match.test(content)) {
content = content.replace(opts.match, opts.replace);
append = false;
} else if (opts.append && typeof opts.replace === 'string') {
if (typeof opts.append === "string") {
opts.append = new RegExp("^.*" + 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 {
from = content.indexOf(opts.match);
to = from + opts.match.length;
content = content.substr(0, from) + opts.replace + content.substr(to);
}
} else {
from = opts.from ? content.indexOf(opts.from) + opts.from.length : 0;
to = opts.to ? content.indexOf(opts.to) : content.length;
content = content.substr(0, from) + opts.replace + content.substr(to);
}
}
return do_eof();
};
do_eof = function() {
var char, i, _i, _len;
if (options.eof == null) {
return do_diff();
}
if (options.eof === true) {
for (i = _i = 0, _len = content.length; _i < _len; i = ++_i) {
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 (!misc.string.endsWith(content, options.eof)) {
content += options.eof;
}
return do_diff();
};
do_diff = function() {
var count_added, count_removed, line, lines, ls, padsize, _i, _j, _k, _len, _len1, _len2;
if (destinationHash === misc.string.hash(content)) {
return do_ownership();
}
if (typeof options.log === "function") {
options.log("File content has changed");
}
if (options.diff) {
lines = diff.diffLines(destination, content);
if (typeof options.diff === 'function') {
options.diff(lines);
}
if (options.stdout) {
count_added = count_removed = 0;
padsize = Math.ceil(lines.length / 10);
for (_i = 0, _len = lines.length; _i < _len; _i++) {
line = lines[_i];
if (line.value === null) {
continue;
}
if (!line.added && !line.removed) {
count_added++;
count_removed++;
continue;
}
ls = line.value.split(/\r\n|[\n\r\u0085\u2028\u2029]/g);
if (line.added) {
for (_j = 0, _len1 = ls.length; _j < _len1; _j++) {
line = ls[_j];
count_added++;
options.stdout.write("" + (pad(padsize, '' + count_added)) + " + " + line + "\n");
}
} else {
for (_k = 0, _len2 = ls.length; _k < _len2; _k++) {
line = ls[_k];
count_removed++;
options.stdout.write("" + (pad(padsize, '' + count_removed)) + " - " + line + "\n");
}
}
}
}
}
return do_write();
};
do_write = function() {
if (typeof options.destination === 'function') {
options.destination(content);
return do_end();
} else {
if (append) {
if (options.flags == null) {
options.flags = 'a';
}
}
return fs.writeFile(options.ssh, options.destination, content, options, function(err) {
if (err) {
return next(err);
}
modified = true;
return do_backup();
});
}
};
do_backup = function() {
var backup;
if (!options.backup) {
return do_end();
}
backup = options.backup;
if (backup === true) {
backup = "." + (Date.now());
}
backup = "" + options.destination + backup;
return fs.writeFile(options.ssh, backup, content, function(err) {
if (err) {
return next(err);
}
return do_end();
});
};
do_ownership = function() {
if (!((options.uid != null) && (options.gid != null))) {
return do_permissions();
}
return chown({
ssh: options.ssh,
destination: options.destination,
uid: options.uid,
gid: options.gid,
log: options.log,
stdout: options.stdout,
stderr: options.stderr
}, function(err, chowned) {
if (err) {
return next(err);
}
if (chowned) {
modified = true;
}
return do_permissions();
});
};
do_permissions = function() {
if (options.mode == null) {
return do_end();
}
return chmod({
ssh: options.ssh,
destination: options.destination,
mode: options.mode,
log: options.log,
stdout: options.stdout,
stderr: options.stderr
}, function(err, chmoded) {
if (err) {
return next(err);
}
if (chmoded) {
modified = true;
}
return do_end();
});
};
do_end = function() {
if (modified) {
written++;
}
return next();
};
return conditions.all(options, next, do_read_source);
}).on('both', function(err) {
return finish(err, written);
});
});
return result;
};