mecano
Version:
Common functions for system deployment.
414 lines (404 loc) • 11.5 kB
JavaScript
// Generated by CoffeeScript 1.11.1
var diff, eco, fs, misc, nunjucks, path, string, uid_gid;
module.exports = function(options, callback) {
var j, len, ref, target, targetHash, targetStat, w;
options.log({
message: "Entering file",
level: 'DEBUG',
module: 'mecano/lib/file'
});
if (!((options.source || (options.content != null)) || options.replace || (options.write != null))) {
return callback(Error('Missing source or content'));
}
if (options.source && options.content) {
return callback(Error('Define either source or content'));
}
if (!options.target) {
return callback(Error('Missing target'));
}
options.log({
message: "Source is \"" + options.source + "\"",
level: 'DEBUG',
module: 'mecano/lib/file'
});
options.log({
message: "Destination is \"" + options.target + "\"",
level: 'DEBUG',
module: 'mecano/lib/file'
});
if (options.content && Buffer.isBuffer(options.content)) {
options.content = options.content.toString();
}
if (typeof options.content === 'function') {
options.content = options.content(options);
}
if (options.diff == null) {
options.diff = options.diff || !!options.stdout;
}
if (options.engine == null) {
options.engine = 'nunjunks';
}
if (options.unlink == null) {
options.unlink = false;
}
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";
}
target = null;
targetHash = null;
if (options.write == null) {
options.write = [];
}
if ((options.from != null) || (options.to != null) || (options.match != null) || (options.replace != null) || (options.place_before != null)) {
options.write.push({
from: options.from,
to: options.to,
match: options.match,
replace: options.replace,
append: options.append,
place_before: options.place_before
});
options.append = false;
}
ref = options.write;
for (j = 0, len = ref.length; j < len; j++) {
w = ref[j];
if ((w.from == null) && (w.to == null) && (w.match == null) && (w.replace != null)) {
w.match = w.replace;
}
}
this.call(function(_, callback) {
var source, ssh;
if (options.content != null) {
if (typeof options.content === 'number') {
options.content = "" + options.content;
}
return callback();
}
source = options.source || options.target;
options.log({
message: "Force local source is \"" + (options.local ? 'true' : 'false') + "\"",
level: 'DEBUG',
module: 'mecano/lib/file'
});
ssh = options.local ? 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))));
}
options.content = '';
return callback();
}
options.log({
message: "Reading source",
level: 'DEBUG',
module: 'mecano/lib/file'
});
return fs.readFile(ssh, source, 'utf8', function(err, src) {
if (err) {
return callback(err);
}
options.content = src;
return callback();
});
});
});
targetStat = null;
this.call(function(_, callback) {
var do_mkdir, do_read, exists;
if (typeof options.target === 'function') {
return callback();
}
exists = function() {
options.log({
message: "Stat target",
level: 'DEBUG',
module: 'mecano/lib/file'
});
return fs.lstat(options.ssh, options.target, function(err, stat) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
return do_mkdir();
}
if (err) {
return callback(err);
}
targetStat = stat;
if (stat.isDirectory()) {
options.target = options.target + "/" + (path.basename(options.source));
options.log({
message: "Destination is a directory and is now \"options.target\"",
level: 'INFO',
module: 'mecano/lib/file'
});
return fs.stat(options.ssh, options.target, function(err, stat) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
options.log({
message: "New target does not exist",
level: 'INFO',
module: 'mecano/lib/file'
});
return callback();
}
if (err) {
return callback(err);
}
if (!stat.isFile()) {
return callback(new Error("Destination is not a file: " + options.target));
}
options.log({
message: "New target exist",
level: 'INFO',
module: 'mecano/lib/file'
});
targetStat = stat;
return do_read();
});
} else if (stat.isSymbolicLink()) {
options.log({
message: "Destination is a symlink",
level: 'INFO',
module: 'mecano/lib/file'
});
if (!options.unlink) {
return do_read();
}
return fs.unlink(options.ssh, options.target, function(err, stat) {
if (err) {
return callback(err);
}
return callback();
});
} else if (stat.isFile()) {
options.log({
message: "Destination is a file",
level: 'INFO',
module: 'mecano/lib/file'
});
return do_read();
} else {
return callback(Error("Invalid File Type Destination: " + options.target));
}
});
};
do_mkdir = (function(_this) {
return function() {
if (typeof options.mode === 'string') {
options.mode = parseInt(options.mode, 8);
}
return _this.mkdir({
target: path.dirname(options.target),
uid: options.uid,
gid: options.gid,
mode: options.mode ? options.mode | 0x49 : 0x1ed,
unless_exists: path.dirname(options.target)
}, function(err, created) {
if (err) {
return callback(err);
}
return callback();
});
};
})(this);
do_read = function() {
options.log({
message: "Reading target",
level: 'DEBUG',
module: 'mecano/lib/file'
});
return fs.readFile(options.ssh, options.target, 'utf8', function(err, dest) {
if (err) {
return callback(err);
}
target = dest;
targetHash = string.hash(dest);
return callback();
});
};
return exists();
});
this.call(function() {
if (options.context != null) {
return string.render(options);
}
});
this.call(function() {
if (options.skip_empty_lines == null) {
return;
}
options.log({
message: "Skip empty lines",
level: 'DEBUG',
module: 'mecano/lib/file'
});
return options.content = options.content.replace(/(\r\n|[\n\r\u0085\u2028\u2029])\s*(\r\n|[\n\r\u0085\u2028\u2029])/g, "$1");
});
this.call(function() {
if (options.write.length) {
return string.replace_partial(options);
}
});
this.call(function() {
var char, i, k, len1, ref1;
if (options.eof == null) {
return;
}
options.log({
message: "Checking option eof",
level: 'DEBUG',
module: 'mecano/lib/file'
});
if (options.eof === true) {
ref1 = options.content;
for (i = k = 0, len1 = ref1.length; k < len1; i = ++k) {
char = ref1[i];
if (char === '\r') {
options.eof = options.content[i + 1] === '\n' ? '\r\n' : char;
break;
}
if (char === '\n' || char === '\u2028') {
options.eof = char;
break;
}
}
if (options.eof === true) {
options.eof = '\n';
}
options.log({
message: "Option eof is true, gessing as " + (JSON.stringify(options.eof)),
level: 'INFO',
module: 'mecano/lib/file'
});
}
if (!string.endsWith(options.content, options.eof)) {
options.log({
message: "Add eof",
level: 'WARN',
module: 'mecano/lib/file'
});
return options.content += options.eof;
}
});
this.call(function(_, callback) {
var raw, ref1, text;
if (targetHash === string.hash(options.content)) {
return callback();
}
options.log({
message: "File content has changed: " + options.target,
level: 'WARN',
module: 'mecano/lib/file'
});
ref1 = diff(target, options.content, options), raw = ref1.raw, text = ref1.text;
if (typeof options.diff === 'function') {
options.diff(text, raw);
}
options.log({
message: text,
type: 'diff',
level: 'INFO',
module: 'mecano/lib/file'
});
return callback(null, true);
});
this.call(function() {
var backup;
if (!this.status()) {
return;
}
if (!(options.backup && targetHash)) {
return;
}
options.log({
message: "Create backup",
level: 'INFO',
module: 'mecano/lib/file'
});
backup = typeof options.backup === 'string' ? options.backup : "." + (Date.now());
return this.copy({
ssh: options.ssh,
source: options.target,
target: "" + options.target + backup
});
});
this.call(function(_, callback) {
if (!this.status()) {
return callback();
}
if (typeof options.target === 'function') {
options.log({
message: "Write target with user function",
level: 'INFO',
module: 'mecano/lib/file'
});
options.target(options.content);
return callback();
}
options.log({
message: "Write target",
level: 'INFO',
module: 'mecano/lib/file'
});
if (options.append) {
if (options.flags == null) {
options.flags = 'a';
}
}
if (options.mode == null) {
options.mode = 0x1a4;
}
return uid_gid(options, function(err) {
if (err) {
return callback(err);
}
return fs.writeFile(options.ssh, options.target, options.content, options, function(err) {
if (err) {
return callback(err);
}
options.log({
message: "File written",
level: 'INFO',
module: 'mecano/lib/file'
});
return callback();
});
});
});
this.chown({
target: options.target,
stat: targetStat,
uid: options.uid,
gid: options.gid,
"if": (options.uid != null) || (options.gid != null),
unless: options.target === 'function'
});
this.chmod({
target: options.target,
stat: targetStat,
mode: options.mode,
"if": options.mode != null,
unless: options.target === 'function'
});
return this.then(callback);
};
fs = require('ssh2-fs');
path = require('path');
eco = require('eco');
nunjucks = require('nunjucks/src/environment');
misc = require('../misc');
diff = require('../misc/diff');
string = require('../misc/string');
uid_gid = require('../misc/uid_gid');