uci-remote-code/src/sync.js

114 lines
3.4 KiB
JavaScript

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
}
}