UNPKG

mecano

Version:

Common functions for system deployment.

475 lines (463 loc) 15.8 kB
// 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');