import { EventEmitter as Emitter } from 'events' import path from 'path' // import promisify from 'util' import { readFile as readConfig } from 'fs-read-data' import { exec as child } from 'child_process' // import ignoreFiles from './ignore' import logger from '@uci/logger' let log = {} // declare module wide log to be set during construction class Sync extends Emitter { constructor(opts = {}) { super() log = logger({ name: 'remote-code', file:'src/sync.js', class:'Sync'}) this.opts = opts // this.opts.more = this.opts.more || [] // this.opts.ssh = this.opts.ssh || [] this.jobOpts = {} } async configure(opts,overwrite) { if (typeof opts === 'string') opts = await readConfig(opts) // opts here is path to configuration file this.opts = overwrite ? Object.assign(this.opts,opts) : Object.assign(opts, this.opts) } addOpt(option,value) { this.opts.more = this.opts.more || [] this.opts.more.push(`${(option.length > 1)?'--':'-'}${option} ${value}`) log.info (this.opts.more) } addSshOpt(option,value) { this.opts.ssh = this.opts.ssh || [] this.opts.ssh.push(`${(option.length > 1)?'--':'-'}${option} ${value}`) log.info (this.opts.ssh) } command(src, dest) { this.opts.src = src || this.opts.src || process.cwd() this.opts.dest = dest || this.opts.dest || `/opt/${process.cwd().match(/([^/]*)\/*$/)[1]}` this.cmd=`rsync ${this._opts} ${this._ssh()} ${this.opts.src}/ ${this.opts.dest}` // this.cmd = JSON.stringify(this.opts) return this.cmd } _ssh() { if (!(this.opts.username && this.opts.host)) return '' this.opts.dest = `${this.opts.username}@${this.opts.host}:${this.opts.dest}` if (this.opts.keyFilePath) this.addSshOpt('i',this.opts.keyFilePath) if (this.opts.port) this.addSshOpt('p',this.opts.port) if (!this.opts.ssh) return '' return `-e "ssh ${this.opts.ssh.join(' ')}"` } _opts() { if (typeof opts.mirror==='boolean' && !opts.mirror) // if ssh agent is not available in child process can make it so _agent(path) { // this.jobOpts.env } // returns a promise when job is done execute(src,dest) { const sync = this let reply=[] let error=[] sync.emit('busy') return new Promise((resolve, reject) => { // may need to be sure that child has agent socket envirnoment variable if (this.opts.agent) this._agent(this.opts.agent) const job = child(this.command(src,dest), this.jobOpts) job.on('error', (err) => { log.fatal({err:err, opts:sync.opts, msg:'failed to spawn rsync job'}) reject(err) }) job.on('close', (code) => { log.info(`sync job exited with code ${code}`) if (error.length!==0) sync.emit('remote:error',error) sync.emit('done',{command:sync.cmd, reply:reply}) resolve({command:sync.cmd,reply:reply,error:error}) }) job.stdout.on('data', (data) => { log.info({remote:sync.opts.host, console:data, msg:'console data from remote'}) reply.push(data) }) job.stderr.on('data', (data) => { log.warn({remote:sync.opts.host, error:data, msg:'error from remote'}) error.push(data) }) }) } }// end Class Sync export default Sync function isPlainObject (obj) { return Object.prototype.toString.call(obj) === '[object Object]' } function escapeSpaces (str) { if (typeof str === 'string') { return str.replace(/\b\s/g, '\\ ') } else { return path } }