Parcourir la source

Options for promts and subcommands

master
chess il y a 3 ans
Parent
révision
8d81122480
2 fichiers modifiés avec 164 ajouts et 39 suppressions
  1. 3
    3
      cliverse.js
  2. 161
    36
      index.js

+ 3
- 3
cliverse.js Voir le fichier

@@ -62,11 +62,11 @@ function nodeShellExec() {
return p;
}

var prompt = function(choices, label){
var prompt = function(choices, label, defaultchoice){
return this.prompter.ask(
`${label} \n ` + Object.keys(choices).map(choice => { return ` ${(+choice) + 1}) ${choices[choice]} `}).join('\n') + `\n default ( <= ${choices[0]} ) : `
`${label} \n ` + Object.keys(choices).map(choice => { return ` ${(+choice) + 1}) ${choices[choice]} `}).join('\n') + `\n default ( <= ${ defaultchoice || choices[0]} ) : `
).then(choice => {
if(!choice) return choices[0];
if(!choice) return defaultchoice || choices[0];
if(choice && isNaN(+choice)) return choice;
return choices[choice + 1];
})

+ 161
- 36
index.js Voir le fichier

@@ -84,7 +84,12 @@ var getTaskCheckExists = (command, options) => {
var getTaskWithElevation = function(tasdef){
return ()=>{
if (__isElevated) {
return tasdef.elevatedpulltasks();
return tasdef.elevatedpulltasks().then(()=>{
// PB : TODO -- Every elevation should have its own messaging file. Async writes from multiple processes are a problem here...
fs.writeFileSync('run.done', 'success')
}).catch(e=>{
fs.writeFileSync('run.done', 'failure')
});
}
else {
// PB : TODO -- Rename op['runas'] to 'elevate'
@@ -93,6 +98,7 @@ var getTaskWithElevation = function(tasdef){
console.error(e)
})
.finally(() => {
fs.unlinkSync('run.done')
if (!processedArgs.runas) { return tasdef.regularpulltasks(); }
})
}
@@ -519,11 +525,20 @@ var op = {
// git remote equivalents...
// git branch --set-upstream-to=elixir-unc/master master
// git push --set-upstream elixir-unc branch..
, 'set-url': (remotename, url) => {
, 'remote set-url': (args) => {
// git remote set-url elixir-unc //10.10.5.60/gitrepo/chess/bbhverse
var __args = {
remotename : args.remotename || processedArgs._[2]
, url : args.url || processedArgs._[3]
}

// pushable doesn't mean the remote doesn't support being pushed to.
// Over here it just means we are disabling pushing to that remote by setting the push portion of the url the a junk remote called no-pushing.
// PB : TODO -- change this to enablepushing.
// By default pushing should be disabled. Also developers permissions on the remote is a secondary check for pushing.
var pushable = processedArgs.pushable || false;
remotename = remotename || processedArgs._[1]
url = url || processedArgs._[2]
remotename = __args.remotename
url = __args.url
var serial_perform_git_seturl = (repo) => {
var options = { cwd: instanceroot + '/' + repo }
// console.log(repo)
@@ -534,17 +549,26 @@ var op = {
]
}
else {
console.error('not supported for non-pushable')
return [
['git', ['remote', 'set-url', remotename, url + '/' + repo], { cwd: instanceroot + '/' + repo }]
, ['git', ['remote', `set-url`, '--push', remotename, 'no-pushing'], { cwd: instanceroot + '/' + repo }]
]
}

}

var x = (args) => {
return () => {
var tasq = () => {
// console.log(args)
return nodeShellExec.apply(null, args)
return nodeShellExec.apply(null, args).catch(e => {
// We continue on failure.
console.error(tasq.toString())
})
}
// return Promise.resolve(true)
tasq.toString = function(){
return JSON.stringify(args)
}
return tasq;
}
var perform_git_seturl = (dir) => {
op['is-git-repo'](dir).then((logEntry) => {
@@ -610,8 +634,12 @@ var op = {

dirs(perform_git_add)
}
, 'remove': (remotename) => {
remotename = remotename || processedArgs._[1]
, 'remote remove': (args) => {
var __args = {
remotename : args.remotename|| processedArgs._[2]
}
var remotename = __args.remotename
var serial_perform_git_remove = (repo) => {
var options = { cwd: instanceroot + '/' + repo }
// console.log(repo)
@@ -634,7 +662,7 @@ var op = {
any(serial_perform_git_remove(dir.name).map(x))
})
.catch((e) => {
console.log('skipped : ' + dir.name + ', reason : No remote named origin')
console.log('skipped : ' + dir.name + `, reason : No remote named ${remotename}`)
})
}
// else console.log('Skipped : Not a Git Repo : ' + dir.name)
@@ -1553,7 +1581,36 @@ var op = {
var util = require('util');
var cliname = 'elxr';
var ver = '1.1';
var help = `# list of commands... please refer dveloper documentation for ${cliname}
var readme = `
Command syntax examples

elxr
Displays help. Same as elxr h

elxr i
Runs interactive propmts to help choose installation options.

NODE_ENV={{yourenvironment}} elxr {{cmd}} {{instancename}} {{otheroptions}}
General command syntax.
Eg: NODE_ENV=development elxr pull elixir

Note : Although {{instancename}} is generally a positional parameter supplied immediately after the cmd for most cmd's.
The specific cmd defines what the value is interpreted as.
There are several cmds that do not require an {{instancename}} parameter.
Eg: elxr remote remove origin
Git operations or passthrough external commands on all repository folders in you working directory.

Main objectives.
elxr cli is a wrapper around other shell commands and api based operations.
One of the main objetives it achives is ease of use of repeating an individual cmd multiple times on all targets.
Eg: A git operation like pull can be repeated consistently on a set of git repositories.
`;
var help = `
${readme}


# list of commands... please refer dveloper documentation for ${cliname}
${
// util.inspect(
[Object.keys(op)]
@@ -1612,11 +1669,9 @@ var elxr = {
if(def.elevatedRepos){
elevatedpulltasks = function() {
return any(def.elevatedRepos.map((def) => performPull(def.repo))).then(() => {
// fs.writeFileSync('run.done', 'success')
return true;
}).catch((e) => {
console.error(e)
fs.writeFileSync('run.done', 'error')
})
}
}
@@ -1632,27 +1687,35 @@ var elxr = {
}
}

function preworkerconfig(){
// Everything runs after this check is completed. Elevation occurs out of process when needed.
gitRepos = repomanifest.repos
// gitRepos = ['chess-server-lib'];
// 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

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


// The main elxr cli process
function elxrworker() {
// Everything runs after this check is completed. Elevation occurs out of process when needed.
gitRepos = repomanifest.repos
// gitRepos = ['chess-server-lib'];

// 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 subcommandlabels = {
remote : `remote ${processedArgs._[1]}`
}

var env = Object.assign({}, process.env); // Shallow clone it.
var __runcmd = function (label) {
return op[label] ? op[label]() : null;
var distinquishedlabel = subcommandlabels[label] || label
return op[distinquishedlabel] ? op[distinquishedlabel](processedArgs) : 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) {
@@ -2116,6 +2179,29 @@ function verifyAndInstallPrerequisites() {
return Promise.all(downloadtasks).then(() => { return any(installtasks) })
}

var getPromptableAsyncPropDescriptor = function(propName, choices, defaultchoise){
return {
get (){
return cli.prompt( choices, propName, defaultchoise).then(propValue => {
Object.defineProperty(this, propName, {
value: propValue,
writable: false,
configurable : true
});
return propValue
})
}
// , set (propValue){
// Object.defineProperty(this, propName, {
// value: propValue,
// writable: false,
// configurable : true
// })
// return propValue;
// }
, configurable : true
}
}

// function updateselection(selected) { selectedinstance = utils.assign(selectedinstance, selected) }
var selectedinstance = null;
@@ -2147,6 +2233,7 @@ acquireElevationState().then(() => {

var noprerequisites = {
add : true, 'set-url' : true, 'repo-relocate' : true
, remote : true
}
var skipprereqs = {}
var maintask = () => {
@@ -2164,7 +2251,7 @@ acquireElevationState().then(() => {
e = err;
console.error('Chosen cofiguraton failed or not found. Fix config and rerun or chose another.')
console.error(err)
}).then(() => { return elxrworker(true) })
}).then(() => { preworkerconfig(); return elxrworker(true) })
// .finally(()=>{
// fs.writeFileSync('run.log', ', ' + JSON.stringify({ error: e.message }), { 'flag': 'a+' })
// if(!e.success) fs.writeFileSync('run.done', 'error');
@@ -2174,6 +2261,7 @@ acquireElevationState().then(() => {
}
else {
console.log('cmd has no preqs or has been configured to skip preqs')
preworkerconfig()
return elxrworker()
}

@@ -2186,7 +2274,7 @@ acquireElevationState().then(() => {
if(noprerequisites[processedArgs._[0]]
|| skipprereqs[processedArgs._[0]]
) {
return op[processedArgs._[0]]()
return elxrworker()
}

return detectInstance().then((detectedInstance)=>{
@@ -2203,18 +2291,52 @@ acquireElevationState().then(() => {
}
catch (e) {
console.error(e)
// No local instances config found. We acquire user choices and proceed to reattempt.
// Basic keys that must be prompted and confirmed if not supplied as cmd line args.
// PB: TODO --- This should be cmd specific interpretation of location parameters !!!
var promptkeys = {
'instanceName' : processedArgs._[1]
}

var reconfirm = {
'instanceName' : true
}

// No local instances config found. We use a default initialized instance available in selectedinstance
// Confirm those that were not supplied as user choices in runtime args and proceed to reattempt.
initinstances(chessinstances, selectedinstance)

var instanceNameChoices = new Set(Object.keys( chessinstances) )
instanceNameChoices.delete('current_run')
instanceNameChoices.add(selectedinstance['instanceName'])
instanceNameChoices.add(promptkeys['instanceName'])

var choices = {
'instanceName' : Array.from(instanceNameChoices)
, 'reposerver' : selectedinstance['reposervers']
}

var prompts = [];
Object.keys(__interactve_promts).forEach(k => {
if(!selectedinstance[k]) {
var eachPrompt = function(k, i, a){
if(selectedinstance[k] !== promptkeys[k] || promptkeys[k] === undefined || reconfirm[k]) {
prompts.push(async ()=>{
Object.defineProperty(selectedinstance, k, Object.getOwnPropertyDescriptor(__interactve_promts, k));
// PB : NOTE -- Important in async cases when this needs to be in the same state as when it was invoked.
// We take a snapshot... Shallow.. !! If required deep should be used based on use case.
// If latest altered state is required we can reerence this directly.
// var asyncthis = Object.assign(this);
Object.defineProperty(selectedinstance, k, getPromptableAsyncPropDescriptor(k, choices[k], promptkeys[k] || selectedinstance[k] ));
return await selectedinstance[k]
})
}
})
delete promptkeys[k]
}

Object.keys(__interactve_promts).forEach(eachPrompt, __interactve_promts)
Object.keys(promptkeys).forEach(eachPrompt, promptkeys)
todo = any(prompts).then(()=>{ return selectedinstance })
@@ -2310,6 +2432,9 @@ function downloadandinstall(items) {
return Promise.all(tasks)
}




// Sample instances config.
// var instances = {
// "elixir": {

Chargement…
Annuler
Enregistrer