var path = require('path'); var utils = require('bbhverse'); var any = utils.any; var Tasq = utils.Tasq var statuslog = utils.Traq var Traq = utils.Traq Tasq.addlistener(statuslog.statuslog) var cli = require('./cliverse') var nodeShellExec = cli.nodeShellExec; var chalk = require('chalk') const BUILD_VERSION = '[VI]Version: {version} - built on {date}[/VI]'; function getVersion() { return BUILD_VERSION; } console.log(getVersion()) // 'use strict'; // PB : TODO -- make sure folder context is proper coz we can now run elxr from anywhere. // PB : TODO -- // runas bypass non elevated tasks when we request privelege // runas message is always error needs to be fixed. // runas wait needs to be parallelized. // suppress elevation check error messages // support runas lauched directly from shell. // pass in environment in hta to shellexecute. const { existsSync } = require('fs'); const fs = require('fs') const cliargs = utils.cliargs; const processedArgs = cliargs(process.argv.slice(2)); console.dir(processedArgs) var globSync = require('glob').sync; const { isMaster } = require('cluster'); var ENV = Object.assign({}, process.env); // Shallow clone it. var getShellTask = (command, args, options)=>{ return ()=>{ var p = nodeShellExec.apply(null, [command, args, Object.assign({ inherit : true, shell: true, env : ENV, title : `${command} ${args}` }, options) ]) if(options.ignorefailures){ return p.catch(e=>{ // Ignore. Not a major error. }) } else return p; } } var getPullCmd = (repo)=>{ // console.log(useGitPull) var pullCmd = [ gitInstallDir , ['-c', 'branch=`git rev-parse --abbrev-ref HEAD`;for i in `git remote`; do git pull $i $branch; done;'] , { cwd : repo, title : 'pull all origins for ' + repo }] // var pullCmd = ['pullall', [], { cwd : repo }] if(useGitPull) pullCmd = ['git', ['pull'], { inherit : true, shell: true, cwd : instanceroot + '/' + repo // , env: process.env , runas : processedArgs.runas , title : `git pull ${repo}` }] return pullCmd } var performPull = (repo) => { if(existsSync(instanceroot + '/' + repo)) { console.log('pulling ' + instanceroot + '/' + repo) return nodeShellExec.apply(null, getPullCmd(repo)).then(()=>{ if(__isElevated) { fs.writeFileSync('run.log', ', ' + JSON.stringify({ repo, success:true}), {'flag':'a+'} ) } else statuslog.statuslog(null, repo) return true; }) .catch((e)=>{ e.repo = repo; if(__isElevated) { fs.writeFileSync('run.log', ', ' + JSON.stringify(e), {'flag':'a+'} ) } else statuslog.statuslog(e); console.error(e) }) } else { console.log('cloning ' + repo) // PB : TODO -- detect if a clonable repo exists in currentGitAuthUser return nodeShellExec('git', ['clone', '-c', 'core.symlinks=true', defaultRepoServer + `/${defaultRepoOwner}/` + repo + '.git'], { inherit : true, shell: true, env: process.env , runas : processedArgs.runas }).catch((e)=>{ throw e; }).then(()=>{ return nodeShellExec('git', ['config', '--replace-all' , 'core.symlinks', true], { inherit : true, shell: true, env: process.env , cwd : repo , runas : processedArgs.runas , title : `git core.symlinks --replace-all true for ${defaultRepoServer + `/${defaultRepoOwner}/` + repo + '.git'}` }) .then(()=>{ if(__isElevated) { fs.writeFileSync('run.log', ', ' + JSON.stringify({ repo, success:true}), {'flag':'a+'} ) } else statuslog.statuslog(null, repo) }) .catch((e)=>{ e.repo = repo; if(__isElevated) { fs.writeFileSync('run.log', ', ' + JSON.stringify(e), {'flag':'a+'} ) } else statuslog.statuslog(e); }) }) .catch(e=>{ e.repo = repo; if(__isElevated) { fs.writeFileSync('run.log', ', ' + JSON.stringify(e), {'flag':'a+'} ) } else statuslog.statuslog(e); }) } } // PB : TODO -- If we are run from an elevated shell it never moves forward and simply exits. // -- Currently workaround is to always run from a non-elevated shell. var __isElevated = null; // We assume non-Elevated until someone evaluates and sets this variable. var acquireElevationState = ()=>{ if(__isElevated === null) { return nodeShellExec( "fsutil", ["dirty", "query", "C:"], { inherit : true // , shell: true , stdio: 'ignore' , env: process.env , title : `check privileged execution mode using "fsutil dirty query C:"` }).then((exitcode)=>{ console.log('Elevated') __isElevated = true; return true; }).catch(()=>{ __isElevated = false; console.log('Not Elevated'); return false; }); } else return Promise.resolve(__isElevated); } var repomanifest = null; var currentGitAuthUser ; // nodeShellExec('git', ['config', 'user.email']) ... PB : TODO-- get the current gittea username var defaultRepoOwner = 'chess'; var elevatedRunasRepos = null var gitRepos = null var defaultRepoServer = null // grep -qxF 'alias elxr="node elxr/index.js"' ~/.bash_profile || echo 'alias elxr="node elxr/index.js"' >> ~/.bash_profile // nodeShellExec('echo', ['elxr'], { inherit : true}) //, {stdio: "inherit"} var dbForLabel = function(label){ var dbsForLabel = { devmysql : 'mysql' , development : 'mssql' , production : 'mssql' } return dbsForLabel[label] || 'mysql' } // SAM : TODO Use nodeshellexec where to detect git installation dir var gitInstallDir = "C:\\Program Files\\Git\\bin\\sh.exe" // var gitInstallDir = "G:\\Installed\\Git\\bin\\sh.exe" // Relevant git repos // var repomanifest = require('../'+repomanifest.instanceName+'-config-'+ nodeenv +'/repo-manifest')() var exludeMergeRepos = []; var useGitPull = processedArgs.useGitPull || false; var configPromise = null // elxr cli operations var op = { 'h' : ()=>{ console.log(elxr.info()); return '-h' } , 'undefined' : ()=>{ return op.h(); } , 'reset' : ()=>{ // Reset NPM packages semver so major versions can be updated. const fs = require('fs') const wipeDependencies = (__package) => { const file = fs.readFileSync(__package + '/package.json') const content = JSON.parse(file) for (var devDep in content.devDependencies) { if (content.devDependencies[devDep].match(/\W+\d+.\d+.\d+-?((alpha|beta|rc)?.\d+)?/g)) { content.devDependencies[devDep] = '*'; } } for (var dep in content.dependencies) { if (content.dependencies[dep].match(/\W+\d+.\d+.\d+-?((alpha|beta|rc)?.\d+)?/g)) { content.dependencies[dep] = '*'; } } fs.writeFileSync(__package + '/package.json', JSON.stringify(content)) } var repos = ['client']; // repos = gitRepos; repos.forEach(wipeDependencies) // if (require.main === module) { // } else { // module.exports = wipeDependencies // } } , 'upgrade' : ()=>{ console.log('upgrade.......') var tasks = [ ()=>{ var p = nodeShellExec('npm', ['i', '-g', 'npm-upgrade'], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) p.position = 1; console.log('One') return p; } , ()=>{ var p = nodeShellExec('npm', ['cache', 'clean', '-f'], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) p.position = 2; console.log('Two') return p; } , ()=>{ var p = nodeShellExec('npm', ['install', '-g', 'n'], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) p.position = 3; console.log('Three') return p; } , ()=>{ var p = nodeShellExec('n', ['latest'], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) p.position = 4; console.log('Four') return p; } ] any(tasks) console.log('.......done') console.log('Running exlr upgrade in : ' + path.dirname(__dirname)) console.log('Currently only upgrades ember : ' + path.dirname(__dirname)); console.info('Uninstalling existing ember globally') ; var step1 = nodeShellExec('cmd', ['/c', 'npm', 'uninstall', '-g', 'ember-cli'], { stdio: ['pipe', process.stdout, process.stderr], inherit : true, shell: true, cwd : path.dirname(__dirname), env: env }) step1.on('close', ()=>{ console.info('Installing ember globally') ; var step2 = nodeShellExec('cmd', ['/c', 'npm', 'install', '-g', 'ember-cli'], { stdio: ['pipe', process.stdout, process.stderr], inherit : true, shell: true, cwd : path.dirname(__dirname), env: env }) step2.on('close', ()=>{ nodeShellExec('cmd', ['/c', 'ember', '--version'], { stdio: ['pipe', process.stdout, process.stderr], inherit : true, shell: true, cwd : path.dirname(__dirname), env: env }) }) }) } , 'runas' : ()=>{ console.log('Testing Elevation') if(__isElevated){ try { op[ processedArgs.label || processedArgs._[0] || 'h']() } catch(e){ console.error('Error Invalid command : ' + e) fs.writeFileSync('run.done', 'error') } finally { } } else { console.log('Requesting Elevated Privileges'); // Wait for the runas to complete before we read it. try { fs.unlinkSync('run.done') fs.unlinkSync('run.log') } catch(e) { } //Ignore // Find node path to send to hta. return nodeShellExec('where', ['node']).then(r => { var namedArgs = []; console.log('result : ' + JSON.stringify(r)) Object.keys(processedArgs).forEach((v)=>{ v!='_' ? namedArgs.push('--'+v+'='+processedArgs[v]) : null; }) // PB : TODO -- Convert all the cli args back to string. var args = [__dirname + '/windowselevate.hta'].concat(processedArgs._).concat(namedArgs.join(' ')); args.push('--runas=self'); args.push('--nodepath='+r[r.length-1]) if(!processedArgs.node_env) args.push('--node_env='+ENV.NODE_ENV) if(processedArgs.debug) args.push('--debug=true') // Enable to debug elevated.. return nodeShellExec('MSHTA', [`"${args.join('" "')}"`] , { inherit : true , shell: true , env: ENV , runas : 'self' , title : `runas` } ).then(()=>{ // runas returned. try { // PB : TODO -- Log is comma prefixed. Needs to be proper JSON. var runaslog = JSON.parse('[ { "success" : true, "result" : "started"}' + fs.readFileSync('run.log', { flags : 'a+'}) + ']'); runaslog.forEach((logEntry)=>{ statuslog.statuslog(logEntry.success ? null : logEntry, logEntry) logEntry.success ? (console.log(['success :' + logEntry.result]), console.log((logEntry.messages || []).join(' '))) : (console.error(['error :' + logEntry.result]), console.error((logEntry.messages || []).join(' '))) }) } catch(e){ // We must have a runas log statuslog.statuslog(e) console.error('Run log error probably was not created by runas : ' + e) } }) .catch(err => console.error('Elevation failed : ' + err)); }) } } , 'push' : ()=>{ if(!processedArgs._[1]) { console.error('push all not supported. Specify repo name'); return } // init remote bare from local // pushandinitremotebare // https://www.jeffgeerling.com/blogs/jeff-geerling/push-your-git-repositories // connect to repo server -- net use 172.16.0.27 // cd 172.16.0.27/repos/ // mkdir repo.git // cd repo.git // git init --bare // cd localrepo // git remote rename origin githubclone // git remote add origin //172.16.0.27/repos/repo.git // git push origin master var repo = processedArgs._[1]; var sequentialTaskShellCommands = []; if(!existsSync(`Z:/${repo}.git`)){ sequentialTaskShellCommands = [ // ['net', ['use', 'Z:', defaultRepoServer.replace('/','\\')], { // inherit : true, shell: true // , env: process.env // }] ['pwd', { cwd : 'Z:', inherit : true}] , ['mkdir', [`${repo}.git`], { cwd : `Z:` , inherit : true, shell: true , env: process.env }] , ['pwd', { cwd : `Z:/${repo}.git`, inherit : true}] , ['git', ['init', '--bare'], { cwd : `Z:/${repo}.git` , inherit : true, shell: true , env: process.env }] // PB : TODO -- Do this conditionally only... , ['git', ['remote', 'rename', 'origin', 'githubclone'], { cwd : `${repo}`}, (err)=>{ console.log('Ignoring origin rename error : ' + err); return true; //return true to continue. } ] // PB ; Todo -- new repositories created locally will not have origin. Handle this failure. , ['git', ['remote', 'add', 'origin', `${defaultRepoServer}/${repo}.git`], { cwd : `${repo}`}] // PB : TODO -- If threre is a gitbubclone origin // Set the master to pull from the local repo. ] if(!existsSync(`Z:`)){ sequentialTaskShellCommands.splice(0,0, ['net', ['use', 'Z:', defaultRepoServer.replace(/\//gm,'\\')], { inherit : true, shell: true , env: process.env }]) console.warn('Adding network drive z: for repo server. ' + sequentialTaskShellCommands[0]) // throw 'done' } } sequentialTaskShellCommands.push(['git', ['push', 'origin', 'master'], { cwd : `${repo}`}]) // console.dir(sequentialTaskShellCommands); var tasks = []; sequentialTaskShellCommands.forEach(shellcmd => { // console.log(shellcmd) tasks.push(()=>{ var p = nodeShellExec.apply(null, shellcmd.slice(0,3)).catch((e)=>{ if(shellcmd[3]) { return shellcmd[3]() } else { console.error(e);} }) return p; }) }) any(tasks); } , 'is-git-repo' : (dir)=>{ return nodeShellExec('git', ['-C', dir.name, 'rev-parse'], { stdio : 'ignore'}) } , 'set-url' : (remotename, url) => { var pushable = processedArgs.pushable || false; remotename = remotename || processedArgs._[1] url = url || processedArgs._[2] var serial_perform_git_seturl = (repo)=>{ var options = { cwd : repo } // console.log(repo) if(pushable) { return [ ['git', ['remote', 'set-url', remotename, url + '/' + repo], { cwd : repo }] ] } else { console.error('not supported for non-pushable') } } var x = (args)=>{ return ()=>{ // console.log(args) return nodeShellExec.apply(null, args) } // return Promise.resolve(true) } var perform_git_seturl = (dir)=>{ op['is-git-repo'](dir).then((code)=>{ any( serial_perform_git_seturl(dir.name).map(x) ) }).catch((e)=>{ // console.log('Failed : ' + dir.name) }) } const { readdir } = require("fs").promises const dirs = async (perform, path) => { for (const dir of await readdir(path || process.cwd(), { withFileTypes: true })) { if (dir.isDirectory()) perform(dir) } } dirs( perform_git_seturl) } , 'add' : (remotename, url, branch) => { var pushable = processedArgs.pushable || false; remotename = remotename || processedArgs._[1] url = url || processedArgs._[2] branch = branch || processedArgs._[3] var serial_perform_git_add = (repo)=>{ var options = { cwd : repo } // console.log(repo) if(pushable) { return [ ['git', ['remote', 'add', remotename, url + '/' + repo], { cwd : repo }] , ['git', ['pull', remotename, branch], { cwd : repo }] , ['git', ['branch', `--set-upstream-to=${remotename}/${branch}`, branch], { cwd : repo }] ] } else { return [ ['git', ['remote', 'add', remotename, url + '/' + repo], { cwd : repo }] , ['git', ['remote', `set-url`, '--push', remotename, 'no-pushing'], { cwd : repo }] , ['git', ['pull', remotename, branch], { cwd : repo }] , ['git', ['branch', `--set-upstream-to=${remotename}/${branch}`, branch], { cwd : repo }] ] } } var x = (args)=>{ return ()=>{ // console.log(args) return nodeShellExec.apply(null, args) } // return Promise.resolve(true) } var perform_git_add = (dir)=>{ op['is-git-repo'](dir).then((code)=>{ // console.log(code) if(code) { nodeShellExec('git',['remote', 'get-url', remotename], { cwd : dir.name, stdio : 'ignore' }).then(()=>{ console.log('skipped : ' + dir.name + ', reason : A remote with same name already exists.') }) .catch((e)=>{ any( serial_perform_git_add(dir.name).map(x) ) }) } // else console.log('Skipped : Not a Git Repo : ' + dir.name) }).catch((e)=>{ // console.log('Failed : ' + dir.name) }) } const { readdir } = require("fs").promises const dirs = async (perform, path) => { for (const dir of await readdir(path || process.cwd(), { withFileTypes: true })) { if (dir.isDirectory()) perform(dir) } } dirs(perform_git_add) } , 'remove' : (remotename) => { remotename = remotename || processedArgs._[1] var serial_perform_git_remove = (repo)=>{ var options = { cwd : repo } // console.log(repo) return [ ['git', ['remote', 'remove', remotename], { cwd : repo }] ] } var x = (args)=>{ return ()=>{ // console.log(args) return nodeShellExec.apply(null, args) } // return Promise.resolve(true) } var perform_git_remove = (dir)=>{ op['is-git-repo'](dir).then((code)=>{ // console.log(code) if(code) { nodeShellExec('git',['remote', 'get-url', remotename], { cwd : dir.name, stdio : 'ignore' }).then(()=>{ any( serial_perform_git_remove(dir.name).map(x) ) }) .catch((e)=>{ console.log('skipped : ' + dir.name + ', reason : No remote named origin') }) } // else console.log('Skipped : Not a Git Repo : ' + dir.name) }).catch((e)=>{ // console.log('Failed : ' + dir.name) }) } const { readdir } = require("fs").promises const dirs = async (perform, path) => { for (const dir of await readdir(path || process.cwd(), { withFileTypes: true })) { if (dir.isDirectory()) perform(dir) } } dirs(perform_git_remove) } , 'init-gitea' : (user) => { user = user || processedArgs._[1] if(!user) throw 'User name required' var serial_perform_init_gitea = (repo)=>{ var options = { cwd : repo } // console.log(repo) return [ ['git', ['remote', 'add', 'chess', `${defaultRepoServer}/${user}/${repo}.git`], { cwd : repo }] , ['git', ['remote', 'set-url', '--push', 'chess', 'no-pushing'], { cwd : repo }] , ['git', ['remote', 'set-url', 'origin', `${defaultRepoServer}/${user}/${repo}.git`], { cwd : repo }] ]} var x = (args)=>{ return ()=>{ // console.log(args) return nodeShellExec.apply(null, args) } // return Promise.resolve(true) } var perform_init_gitea = (dir)=>{ op['is-git-repo'](dir).then((code)=>{ // console.log(code) if(code) { nodeShellExec('git',['remote', 'get-url', 'chess'], { cwd : dir.name, stdio : 'ignore' }).then(()=>{ console.log('skipped : ' + dir.name + ', reason : Already has remote chess ') }) .catch((e)=>{ any( serial_perform_init_gitea(dir.name).map(x) ) }) } // else console.log('Skipped : Not a Git Repo : ' + dir.name) }).catch((e)=>{ // console.log('Failed : ' + dir.name) }) } const { readdir } = require("fs").promises const dirs = async (perform, path) => { for (const dir of await readdir(path || process.cwd(), { withFileTypes: true })) { if (dir.isDirectory()) perform(dir) } } dirs(perform_init_gitea) } , 'syncmaster' : (label) => { // Usage : // elxr pull -- Defaults to run config var env = Object.assign({}, process.env); // Shallow clone it. // console.dir(env) console.log('Running exlr pull : ' + path.dirname(__dirname)) if(!processedArgs.runas) gitRepos.forEach(performPull) if(__isElevated){ return any(elevatedRunasRepos.map((repo)=>performPull(repo))).then(()=>{ fs.writeFileSync('run.done', 'success') }).catch(()=>{ fs.writeFileSync('run.done', 'error') }) } else return op['runas']() } , 'pull' : (label) => { // Usage : // elxr pull -- Defaults to run config var env = Object.assign({}, process.env); // Shallow clone it. console.log('Running exlr pull : ' + path.dirname(__dirname)) var useGitPull = processedArgs.useGitPull || false; if(__isElevated){ return any(elevatedRunasRepos.map((repo)=>performPull(repo))).then(()=>{ fs.writeFileSync('run.done', 'success') return true; }).catch(()=>{ fs.writeFileSync('run.done', 'error') }) } else { // PB : TODO -- Rename op['runas'] to 'elevate' return op['runas']().then(()=>{ return true; }) .catch((e)=>{ console.error(e) }) .finally(()=>{ if(!processedArgs.runas) { var pendingpulls = []; gitRepos.forEach( (r)=>{ pendingpulls.push(performPull(r)) } ) return Promise.all(pendingpulls).then(results =>{ return true; }).finally(Traq.finally) } }) } } , 'isInstalled' : ()=>{ return nodeShellExec('where', [processedArgs._[1]], { inherit : true} ).then(()=>{ console.log(processedArgs._[1] + ' exists.') return true; }); } , 'i' : ()=>{ var tasks = [] tasks.push(op['pull']); tasks.push( getShellTask.apply(null, ['rm', [instanceroot + '/run.js'], { ignorefailures : true} ] ) ) tasks.push(op['use']) if(!__isElevated){ tasks.push(op['npmi']) } // var tasksdefs = [ // ['elxr', ['pull']] // , ['elxr', ['use', 'elixir']] // , ['elxr', ['npmi']] // ] // tasksdefs.forEach(tasksdef=>{ // tasks.push( ()=> nodeShellExec.apply(null, tasksdef) ) // }) return any(tasks); } , 'npmi' : ()=>{ var tasks = []; var npmbuildrepos = ['loopback-jsonapi-model-serializer'] npmbuildrepos.forEach(repo => { tasks.push(()=>{ return nodeShellExec('npm', ['i --force'], { inherit : true, shell: true , cwd : repo , env: process.env , title : `npm i for ${repo}` }).catch((e)=>{ console.error('Ignoring Benign Error'); console.error(e); }).then(()=>{ console.log(`--------------------npm run build for ${repo}--------------------`) return nodeShellExec('npm', ['run build'], { inherit : true, shell: true , cwd : repo , env: process.env , title : `npm run build for ${repo}` }).then(Tasq.then).catch(Tasq.catch) }) }) }) any(tasks).then(()=>{ gitRepos.push('client/server'); gitRepos = gitRepos.concat(elevatedRunasRepos); var rmtasks = [] var repotasks = [] gitRepos.forEach(repo => { rmtasks.push(()=>{ console.log(`--------------------rm package-lock.json for ${repo}--------------------`) return nodeShellExec('rm', ['package-lock.json'], { inherit : true, shell: true , cwd : repo , env: process.env , title : `rm 'package-lock.json' for ${repo}` }).catch((e)=>{console.error(e)}) }) if( npmbuildrepos.indexOf(repo) < 0) { repotasks.push( ()=>{ console.log(`--------------------npm i for ${repo}--------------------`) var p = nodeShellExec('npm', ['i --force'], { inherit : true, shell: true , cwd : repo , env: process.env , title : `npm i for ${repo}` }).then(Tasq.then).catch(Tasq.catch) return p; }) } }) var bowerRepos = ['client'] bowerRepos.forEach(repo => { repotasks.push(()=>{ var p = nodeShellExec('bower', ['install'], { inherit : true, shell: true , cwd : repo , env: process.env , title : `bower i for ${repo}` }).then(Tasq.then).catch(Tasq.catch) return p; }) }) return Promise.all(rmtasks).then(()=>any(repotasks)); }).catch(e=>{ }).finally(statuslog.finally) } , 'start' : (label)=>{ console.log('Starting Elixir Server.'); var env = Object.assign({}, process.env); // Shallow clone it. // console.dir(env) env.NODE_ENV = process.env.NODE_ENV || 'development'; env.DEBUG = 'loopback:connector:' + dbForLabel(label) var cmd = env.NODE_ENV === 'development' ? 'nodemon' : 'node'; // cmd = 'node' cmd = [cmd, ['--inspect=9228', 'elixir/server.js']] var childPromise = nodeShellExec(...cmd, { // inherit : true, shell: true, detached: true, stdio: 'ignore', cwd : 'elixir-server' , env: env }) var child = childPromise.process; if (typeof child.pid !== 'undefined') { console.log(`started Elixir Server PID(${child.pid}) : NODE_ENV=${process.NODE_ENV} ${cmd}`); fs.writeFileSync('.elixir-server.elixir.server.pid', child.pid, { encoding: 'utf8' }) } // nodeShellExec('node', ['--inspect=9226', ' bin/www'], { // inherit : true, // shell: true, detached: true, // cwd : 'qms/server', // env: env, // shell : true // }) // nodeShellExec('ember', ['s'], { // // inherit : true, // shell: true, detached: true, // cwd : 'client/', // env: env // }) console.log('Starting Elixir Client Host.'); var cmd = ['ember', ['s']] var childPromise = nodeShellExec(...cmd, { // var childPromise = nodeShellExec('node', ['--inspect=9227', './node_modules/.bin/ember', 's'], { // PB : TODO -- ember debugging. // inherit : true, shell: true, detached: true, stdio: 'ignore', cwd : 'client' , env: env }) // .catch(e=>console.error(e)) child = childPromise.process; if (typeof child.pid !== 'undefined') { console.log(`started Elixir Client Host PID(${child.pid}) : NODE_ENV=${process.NODE_ENV} ${cmd}`); fs.writeFileSync('.client.server.pid', child.pid, { encoding: 'utf8' }) } } , 'stop' : (label)=>{ const kill = require('tree-kill'); var serverPid = fs.readFileSync('.elixir-server.elixir.server.pid', { encoding: 'utf8' }) fs.unlinkSync('.elixir-server.elixir.server.pid') console.log(serverPid) kill(serverPid) serverPid = fs.readFileSync('.client.server.pid', { encoding: 'utf8' }) fs.unlinkSync('.client.server.pid') console.log(serverPid) kill(serverPid) } , 'use' : ()=>{ // use a certain named instance. // Eg : // 1) elxr use elixir // 2) elxr use cihsr // If environment is not specified defaults to development. // 1) NODE=test elxr use elixir /*// Steps 1) Delete Config and Data symlinks 2) Make Links for config ({{name}}-config-{{node_env}}) and data with the NODE_ENV specified or default to dev 3) Iterates all repos and pull all. 'git', ['pull', '--all']. 4) Iterates all repos and checkout to the ENV specified. 'git', ['checkout', checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV] 5) Iterates all repos and merge from source configured in mergeSource. 'git', ['merge', mergeSource], */ var runconfig = { NODE_ENV : repomanifest.node_env } try { runconfig = Object.assign(runconfig, require(instanceroot + '/run.js')) } catch(e) { } if((!processedArgs.runas || processedArgs.runas !== 'self') && runconfig.NODE_ENV && runconfig.NODE_ENV === (repomanifest.node_env || runconfig.NODE_ENV) && repomanifest.instanceName && runconfig.use === repomanifest.instanceName) { console.log(`No change detected. Already using requested specs : ${runconfig.NODE_ENV} ${runconfig.use}`) if(processedArgs.runas) { fs.writeFileSync('run.done', 'success') } return } var tasks = [ ()=>{ if(existsSync('config')) { var p = nodeShellExec('rmdir', ['config'], {inherit : true, shell: true, env: process.env } ).catch((err)=>{ console.log('Ignoring benign error : ' + err); return true; }) return p; } else return Promise.resolve(true); }, ()=>{ if(existsSync('data')) { var p = nodeShellExec('rmdir', ['data'], { inherit : true, shell: true, env: process.env } ).catch((err)=>{ console.log('Ignoring benign error : ' + err); return true; }) return p; } else return Promise.resolve(true); }, ]; runconfig.NODE_ENV = process.env.NODE_ENV = process.env.NODE_ENV || runconfig.NODE_ENV || 'development'; if(processedArgs._[1] && runconfig.use !== processedArgs._[1]) runconfig.use = processedArgs._[1]; if(!runconfig.use) { throw 'unspecifed use not allowed. Please specify chess instance name.' } // console.log(process.env.cwd) fs.writeFileSync(instanceroot + '/run.js', 'module.exports = ' + JSON.stringify(runconfig)) // Maps an environment to a branch. Not required if the branch is appropriately named. var checkoutMap = { 'development' : 'master'} // cant use git checkout -b it fails with branch already exists. var performCheckout = (repo, branch)=>{ if(!branch) return Promise.resolve({ 'skipped' : true }) if(excludeCheckouts[repo]) return Promise.resolve({ 'skipped' : true }) return nodeShellExec('git', ['checkout', branch || checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV], { // return nodeShellExec('git', ['switch', '-m', '-C', checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV], { // inherit : true, shell: true, cwd : repo // , stdio : ignore // Use when we want to silcence output completely. , runas : processedArgs.runas , title : `git checkout ${branch ||checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV} for ${repo}` }).catch((e)=>{ console.error(e); return { error : true, message : repo} }) } if(runconfig.NODE_ENV === 'development') performCheckout = ()=>{ return Promise.resolve(true) } var performPullAll = (repo)=>{ if(excludeCheckouts[repo]) return Promise.resolve({ 'skipped' : true }) return nodeShellExec('git', ['pull', '--all'], { // inherit : true, shell: true, cwd : repo , runas : processedArgs.runas , title : `git pull -all for ${checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV} ${repo}` }).catch((e)=>{ console.error(e); return { error : true, message : repo} }) } var mergeSources = { 'development' : null, 'test' : 'master', 'production' : 'master' } var excludeCheckouts = Object.assign(exludeMergeRepos) delete excludeCheckouts[`elixir-config-${runconfig.NODE_ENV}`] delete excludeCheckouts[`cihsr-config-${runconfig.NODE_ENV}`] var mergeSource = mergeSources[checkoutMap[runconfig.NODE_ENV] || runconfig.NODE_ENV] var performMerge = (repo)=>{ if(exludeMergeRepos[repo]) return Promise.resolve({ 'skipped' : true }) return nodeShellExec('git', ['merge', mergeSource], { inherit : true, shell: true, cwd : repo , runas : processedArgs.runas }).catch((e)=>{ console.error(e) }) } if(runconfig.NODE_ENV === 'development') performMerge = ()=>{ return Promise.resolve(true) } any(tasks).then(()=>{ if(!processedArgs.runas) return op['runas']() tasks = [ ()=>{ // Use junctions to avoid npm package issues var p = nodeShellExec('mklink', ['/J', 'config', runconfig.use + '-config' + '-' + process.env.NODE_ENV ], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) return p; } ]; if(processedArgs._[1]) { tasks = tasks.concat( [ ()=>{ var p = nodeShellExec('mklink', ['/J', 'data', runconfig.use + '-data'], { inherit : true, shell: true , env: process.env }).catch((e)=>{ console.error(e) }) return p; } ] ) } return any(tasks) //target is the env is we specify in elxr use command. Default is dev .then( //Switch to target branch () => any([ any(gitRepos.map((repo)=>performCheckout(repo, process.env.NODE_ENV || 'development'))) , any(elevatedRunasRepos.map((repo)=>performCheckout(repo, process.env.NODE_ENV || 'development')))]) ) .then( //PULL from target branch () => any([ any(gitRepos.map((repo)=>performPullAll(repo))), any(elevatedRunasRepos.map((repo)=>performPullAll(repo)))]) ) .then( //Switch to merge source branch () => any([ any(gitRepos.map((repo)=>performCheckout(repo, mergeSources[process.env.NODE_ENV || 'development'] ))) , any(elevatedRunasRepos.map((repo)=>performCheckout(repo, mergeSources[process.env.NODE_ENV || 'development'])))]) ) .then( //Pull on merge source branch () => any([ any(gitRepos.map((repo)=>performPullAll(repo))), any(elevatedRunasRepos.map((repo)=>performPullAll(repo)))]) ) .then( //Switch to target branch () => any([ any(gitRepos.map((repo)=>performCheckout(repo, process.env.NODE_ENV || 'development'))) , any(elevatedRunasRepos.map((repo)=>performCheckout(repo, process.env.NODE_ENV || 'development')))]) ) .then( //Merge source branch to target branch () => any([ any(gitRepos.map((repo)=>performMerge(repo))).catch(err=>{ console.error('error in performMerge ' + err)}) , any(elevatedRunasRepos.map((repo)=>performMerge(repo))).catch(err=>{ console.error('error in performMerge ' + err)})]) ) .then( () => { // Move test config from dev. // if(process.env.NODE_ENV === 'test'){ // var devcfgreponame = runconfig.use + '-config' + '-development'; // var testcfgreponame = runconfig.use + '-config' + '-test'; // var testcfgdir = path.dirname(__dirname) + '/' + testcfgreponame + '/' // var devcfgdir = path.dirname(__dirname) + '/' + devcfgreponame + '/' //eg (elxr/../elixir-config.development) // return any([ // ()=>{ // return nodeShellExec('git', ['checkout', 'test'], { // inherit : true, shell: true, // cwd : testcfgdir // // , env: process.env // , runas : processedArgs.runas // , title : `git checkout test for ${testcfgreponame}` // }).catch((e)=>{ console.error(e) }) // } // , ()=> { // return nodeShellExec('git', ['checkout', 'master'], { // inherit : true, shell: true, // cwd : devcfgdir // // , env: process.env // , runas : processedArgs.runas // , title : `git checkout master for ${devcfgreponame}` // }).catch((e)=>{ console.error(e) }) // } // , ()=> { // globSync( '**/*.test.js', {cwd : devcfgdir}).map((filename) => { // console.log('File found : ' + devcfgdir + filename) // fs.copyFileSync(devcfgdir + filename, testcfgdir+ filename); // }) // return nodeShellExec('git', ['checkout', 'test'], { // inherit : true, shell: true, // cwd : devcfgdir // // , env: process.env // , runas : processedArgs.runas // , title : `git checkout test for ${devcfgreponame}` // }).catch((e)=>{ console.error(e) }) // } // ]) // } // else { return Promise.resolve(true) // } }) .then(()=>{ fs.writeFileSync('run.done', 'success') }).catch(()=>{ fs.writeFileSync('run.done', 'error') }) }).catch(()=>{ fs.writeFileSync('run.done', 'error') }) // Antibiotic stewardship program. // 1st use is fine. // Max vials dispense // 2nd use Pharmacy needs justification Form. // Approval after a certain period of time. } , 'g' : ()=>{ if(processedArgs.h) { console.log('elxr g [modelname] => generate a model named [modelname]'); console.log('elxr g => regenerate all known models'); return } // var child = nodeShellExec('mkdir', ['-p', label], { inherit : true} ); // console.log('Starting directory: ' + process.cwd()); // try { // child = child.on('close', () => { process.chdir(label) } ); // console.log('New directory: ' + process.cwd()); // } // catch (err) { // console.log('chdir: ' + err); // } // child.on('close', function(){ // var options = { // shell : true // , inherit : true // // , cwd : '' + process.cwd // // , env : process.env // }; // nodeShellExec('git', ['init'], { inherit : true}); if(0){ // PB : TODO -- Special google chrome profile for tests etc. nodeShellExec('pwd', { inherit : true}); // /c/Program\ Files\ \(x86\)/Google/Chrome/Application/chrome.exe --user-data-dir=/c/chess/instances/elixir_01/data/Google/Chrome/User\ Data --profile-directory="chess" // "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --user-data-dir="C:\chess\instances\elixir_01\data\Google\Chrome\User Data" --profile-directory="chess" http://localhost:4200/admin/crud/create/item // "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --user-data-dir="C:\chess\instances\elixir_01\data\Google\Chrome\User Data" --profile-directory="chess" http://localhost:4200/tests/index.html?grep=loopback nodeShellExec("C:/Program Files (x86)/Google/Chrome/Application/chrome.exe", [ "--profile-directory=Profile 1" , 'http://localhost:4200/tests/index.html?grep=model convert ember to loopback' + '&filter=none' /*+ '&filter=model convert ember to loopback'*/]); } // nodeShellExec('npm', ['init', '-y'], options); // nodeShellExec('npm', ['init', '-y'], options); // }) var g = { 'client' : ()=>{ console.info('Creating new ember client named : ' + processedArgs._[2] ) ; var step1 = nodeShellExec('cmd', ['/c', 'ember', 'new', processedArgs._[2]], { stdio: ['pipe', process.stdout, process.stderr], inherit : true, shell: true, cwd : path.dirname(__dirname), env: env }) }, 'modelr' : ()=>{ var tasks = [ ()=>{ var p = nodeShellExec('"ember"', [ 'g' , 'modelr' , processedArgs._[2]], { inherit : true, shell: true, env: process.env }).then(()=>{ console.log('Blueprint generation complete for : ' + processedArgs._[2]) return true; }).catch((e)=>{ console.error(e) }) return p; }, ()=>{ var chromePrefsFile = "C:\\chess\\instances\\elixir_01\\data\\Google\\Chrome\\User Data\\chess\\Preferences"; var chromeprefs = fs.readFileSync(chromePrefsFile, { encoding: 'utf8' }) chromeprefs = JSON.parse(chromeprefs) var previous = chromeprefs.download.default_directory; var parentDir = path.dirname(__dirname); chromeprefs.download.default_directory = parentDir + "\\client\\app\\templates\\components\\resource"; fs.writeFileSync(chromePrefsFile, JSON.stringify(chromeprefs)) // PB : TODO -- detect where chrome is installed. // PB : TODO -- set the download dir to the place where files are needed. var p = nodeShellExec('"C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"', [ '--user-data-dir="C:\\chess\\instances\\elixir_01\\data\\Google\\Chrome\\User Data"' , '--profile-directory="chess"' , 'http://localhost:4200/admin/crud/create/' + processedArgs._[2]], { inherit : true, shell: true , env: process.env }).then(()=>{ chromeprefs.download.default_directory = previous; fs.writeFileSync(chromePrefsFile, JSON.stringify(chromeprefs)) return true; }).catch((e)=>{ console.error(e) }) return p; } , ()=>{ console.log('Browser process should have closed here....') return true; } ] any(tasks) } } g[processedArgs._[1]](); } } var util = require('util') var cliname = 'elxr'; var ver = '1.1'; var help = `# list of commands... please refer dveloper documentation for ${cliname} ${ // util.inspect( [Object.keys(op)] // ) } `; var elxr = { help(){ return chalk.cyanBright(` ------------------------------------------------------------------------------- *** BBH Elixir *** ------------------------------------------------------------------------------- elxr ${ver} A cli tool for chess ${help} ------------------------------------------------------------------------------- `)} , info(){ return chalk.cyanBright(` ------------------------------------------------------------------------------- *** BBH Elixir *** ------------------------------------------------------------------------------- elxr ${ver} A cli tool for chess ------------------------------------------------------------------------------- `)} } // The main elxr cli process function elxrworker(hasconfig){ return acquireElevationState().then(()=>{ // Everything runs after this check is completed. Elevation occurs out of process when needed. gitRepos = repomanifest.repos // Repositiories that have symlinks that require elevated priviletes in windows to create symlinks elevatedRunasRepos = repomanifest.elevated // Repos that should excluded from merge for releases... exludeMergeRepos = repomanifest.exludeMergeRepos var env = Object.assign({}, process.env); // Shallow clone it. var __runcmd = function(label){ return op[label] ? op[label]() : null; } // mysqldump --add-drop-table --no-data -u root -p db_name | grep 'DROP TABLE' ) > drop_all_tables.sql // mysql -u root -p db_name < drop_all_tables.sql var mysql = '../xampp/mysql/bin/mysql' var mysqldump = '../xampp/mysql/bin/mysqldump' // --runas if(processedArgs.runas) { // Weve been asked to run in priviledged mode. Check if we already are privileged. __runcmd('runas') } else __runcmd(processedArgs.label || processedArgs._[0] || 'h'); }) } var getManifest = function(){ // Once choices are made we need to load config according to those choices. // No trace of a previous run... // Default Config... return repomanifest = { // Default is public server only. reposervers : [ 'https://git.bbh.org.in' ] // All public repos are by default available. , repos : [ 'setup' , 'elxr' , 'loopback-connector-mysql' , 'loopback-jsonapi-model-serializer' , 'loopback-component-jsonapi' , 'ember-service-worker' , 'ember-service-worker-asset-cache' , 'ember-service-worker-cache-fallback' , 'ember-service-worker-index' , 'ember-sw-client-route' ] , elevated : [ ] , exludeMergeRepos : {} , instanceName : processedArgs._[1] || 'chess' // Runas windowshta clobbers and removes the NODE_ENV !!! We therefore pass it in. , node_env : (process.env.NODE_ENV && process.env.NODE_ENV.trim()) || processedArgs.node_env || 'development' }; } function acquireChoices(){ var hasconfig = false; console.warn(chalk.yellow(` ------------------------------------------------------------------------------- Warning : Cannot locate your preferred configuration since it was not specified You should fork the default chess configuration to customize and make it your own instance with a named config as {{yourowninstancename}}-config-{{yourcurrentenvironment}} And then run this tool as follows NODE_ENV={{yourenvironment}} elxr i {{yourowninstancename}} OR Run this tool with the following command to use a quick start default. node elxr --default OR Choose the the option to create a new instance for you interactively. We will run your choice of default or create your own at the next prompt. ------------------------------------------------------------------------------- `)) var prompt = cli.prompt; return prompt.ask(`Choose an option : d) Install the default chess instance. => elxr i chess node_env=development --default n) Create your custom new instance interactively => elxr i {{instanceName}} node_env={{environment}} i) Choose an instance and environment to install => elxr i {{instanceName}} node_env={{environment}} c) Choose a command to run ( pull, use, i, npmi ... ) <= pull => elxr {{cmd}} {{instanceName}} node_env={{environment}} h) Help q) Quit Default <= d : `).then((choice)=>{ prompt.close(); if(choice && choice==='d' || !choice) { processedArgs._[0] = 'i' processedArgs._[1] = processedArgs._[1] || 'chess' processedArgs.node_env = (process.env.NODE_ENV && process.env.NODE_ENV.trim()) || processedArgs.node_env || 'development' defaultRepoServer = 'https://git.bbh.org.in' getManifest() // PB : TODO -- acquire the manifest directly from http url instead of clone before cloning the config. Since // This is because the manifest at any server location can redirect to the preferred server.. } else if(choice === 'h'){ processedArgs._[0] = 'h' fs.writeFileSync('run.done', 'noop help'); console.log(elxr.help()); process.exit() } else if(choice === 'n' || choice === 'i'){ var p1 = cli.prompt; return p1.ask(`Enter Instance Name ( <= elixir ) : `).then(function(instanceName){ processedArgs._[0] = 'i' processedArgs._[1] = instanceName ? instanceName : 'elixir' return p1.ask(`Enter Environment ( <= development ) : `).then(function(node_env){ processedArgs.node_env = node_env ? node_env : 'development' if(choice === 'n') { defaultRepoServer = 'https://git.bbh.org.in' console.warn( chalk.magenta('No Option Available. Your account may not have privileges. You can request here http://git.bbh.org.in/chess')) process.exit(); } return p1.ask(`Enter preferred repo server ( <= https://git.bbh.org.in ) : `).then(function(reposerver){ p1.close() if(!reposerver) defaultRepoServer = 'https://git.bbh.org.in' }) }) }) } else if(choice === 'c'){ var p1 = cli.prompt; return p1.ask(`Enter Instance Name ( <= elixir ) : `).then(function(instanceName){ processedArgs._[1] = instanceName ? instanceName : 'elixir' return p1.ask(`Enter Environment ( <= development ) : `).then(function(node_env){ processedArgs.node_env = node_env ? node_env : 'development' return p1.ask(`Enter cmd : p) pull Default <= p : `).then(function(cmd){ if(!cmd || cmd === 'p') { processedArgs._[0] = 'pull' } else processedArgs._[0] = cmd return p1.ask(`Enter preferred repo server ( <= https://git.bbh.org.in ) : `).then(function(reposerver){ if(!reposerver) defaultRepoServer = 'https://git.bbh.org.in' p1.close() }) }) }) }) } else { console.log(chalk.gray(`Default option not exercised. Please follow manual instructions to customize your instance here http://git.bbh.org.in/chess and try again.`)); fs.writeFileSync('run.log', ', ' + JSON.stringify({ success : 'quit without execution' }), {'flag':'a+'} ) fs.writeFileSync('run.done', 'noop quit'); process.exit() } }) } var acquireConfig = function(){ var instance = getInstance(); var configrepo = processedArgs._[1]+'-config-'+processedArgs.node_env; return performPull(configrepo).then(()=>{ var manifestpath = path.normalize(instance.root + '/' +processedArgs._[1]+'-config-'+processedArgs.node_env+'/repo-manifest'); repomanifest = require(manifestpath)() var chessinstances = {} try { chessinstances = require(path.normalize(instance.root + '/chessinstances.js')); chessinstances[processedArgs._[1]][processedArgs.node_env] = repomanifest; } catch(e) { chessinstances[processedArgs._[1]] = {} chessinstances[processedArgs._[1]][processedArgs.node_env] = repomanifest; } chessinstances['current_run'] = { instanceName : processedArgs._[1], node_env : processedArgs.node_env } fs.writeFileSync( instanceroot + '/chessinstances.js', 'module.exports = ' + JSON.stringify(chessinstances) + '', {'flag':'w'} ) defaultRepoServer = repomanifest.reposervers[0] // PB : TODO -- Attempt first one that is available and online... ENV.NODE_ENV = repomanifest.node_env; }) .catch((e)=>{ console.error(e) console.error('Config acquisition failed.') }) } var launchpath = process.cwd() var thisscriptdir = __dirname var instanceroot = launchpath; var getInstance = function(){ console.log(`launchpath = ${launchpath}`) console.log(`thisscriptdir = ${thisscriptdir}`) var root = launchpath; // We need a reference to the root director for elxr cli to be properly oriented. if((launchpath + path.normalize('/elxr')) === thisscriptdir ) { // We ran unbuilt from the proper root with elxr subfolder. console.log(`Instance Path : ${root}`) } else { if(launchpath === thisscriptdir){ // Same directory doesn't mean we are being run from elxr directory or the root directory. // It could be a standalone elxr build which may or maynot be in the proper location. if(BUILD_VERSION.indexOf('Version: {version} - built on {date}') > -1) { // Unbuilt therefore we are in the elxr directory. root = path.normalize(launchpath + '../'); } else { // Built version. // check if we have a elxr subfolder. if(fs.existsSync(launchpath + '/..' + path.normalize('/elxr'))) { // Probably in the right place. root = path.normalize(launchpath + '/..'); } else { // Assume launchpath is meaningless. // Figure it out from the input instance name and environment parameters if we are in the right location. root = path.normalize(launchpath + '/' + processedArgs._[1] + '/' + processedArgs.node_env) } } } } instanceroot = root; return { root }; } try { var detectedInstance = getInstance(); // From launch location etc. var chessinstances = require(path.normalize(detectedInstance.root + '/chessinstances.js')); processedArgs._[1] = chessinstances.current_run.instanceName; processedArgs.node_env = chessinstances.current_run.node_env; repomanifest = chessinstances[chessinstances.current_run.instanceName][chessinstances.current_run.node_env] // defaultRepoServer = repomanifest.reposervers[0] // PB : TODO -- Attempt first one that is available and online from all that are available... return acquireConfig().catch((e)=>{ console.error('Exisitng config Failed. Fix config and rerun or chose another.') console.error(e); }).then(()=>{ return elxrworker(true) }) } catch(e) { console.error(e) return acquireChoices().then(()=>{ return acquireConfig().catch((e)=>{ console.error('Chosen cofiguraton failed or not found') console.error(e) fs.writeFileSync('run.log', ', ' + JSON.stringify({ error : e.message }), {'flag':'a+'} ) fs.writeFileSync('run.done', 'error'); return process.exit() }) .then(()=>{ return elxrworker(true) }) }) }