-
Notifications
You must be signed in to change notification settings - Fork 2
/
SyncMate.js
102 lines (90 loc) · 2.61 KB
/
SyncMate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
const exec = require('child_process').exec;
const p = require('./utils/pluralize');
function getSyncCommand(sources) {
sources = sources.map((source) => {
return `"${source}"`;
}).join(' ');
return [
`export SSH_AUTH_SOCK=$(find /tmp/*launch*/Listeners -user "${this.user}" -type s | head -1)`,
`rsync -zarR ${this.verbose ? '-v': ''} ${this.flags} -e "ssh -p ${this.port}" ${sources} "${this.user}@${this.host}:${this.dest}"`
].join('; ');
}
function SyncMate(options, cwd, log, excludes) {
if (!options.host) {
throw new Error('No host was provided');
}
Object.assign(this, options);
this.cwd = cwd || process.cwd();
this.log = log;
this.inProgress = 0;
this.tasks = [];
// if no destination was provided...
if (!this.dest) {
// assume cwd on remote machine
this.dest = cwd;
}
// move excludes to flags
if (this.exclude) {
const flags = Object.keys(this.exclude).reduce((excludes, key) => {
if (this.exclude[key]) {
excludes.push(key);
// exclude nested files
// prevents an error when saving a saved file in a directory that should be ignored
excludes.push(`${key}/**/*`);
}
return excludes;
}, []).map((key) => {
return `--exclude="${key}"`;
}).join(' ');
if (flags) {
this.flags = `${this.flags} ${flags}`;
}
}
}
SyncMate.prototype.sync = function(sources) {
this.log.info(`Syncing ${p(sources)}...`);
sources.forEach((source) => {
this.log.info(` ${sources}`);
});
const command = getSyncCommand.call(this, sources);
this.inProgress++;
const promise = new Promise((resolve, reject) => {
this.log.info('Executing rsync command...');
this.log.info(` ${command}`);
exec(command, {
cwd: this.cwd
}, (error, stdout, stderr) => {
this.inProgress--;
this.log.info(stdout);
this.log.error(stderr);
if (error) {
this.log.error(error.message);
reject(error);
} else {
this.log.info('SUCCESS!');
resolve();
}
});
});
this.tasks.push(promise);
this.done();
return promise;
};
SyncMate.prototype.done = function() {
return new Promise((resolve, reject) => {
// if nothing in progress, we're done...
if (!this.inProgress) {
resolve();
this.tasks = [];
} else {
this.log.info(`Waiting for ${p(this.inProgress, 'task')} to complete`);
const retry = () => {
// try again...
this.done().then(resolve, reject);
};
// when all the known tasks complete...
Promise.all(this.tasks).then(retry).catch(retry);
}
});
};
module.exports = SyncMate;