Browse Source

Prompt choices

master
pb 3 years ago
parent
commit
87b7ab7e4d
2 changed files with 233 additions and 136 deletions
  1. 2
    1
      cliverse.js
  2. 231
    135
      index.js

+ 2
- 1
cliverse.js View File

@@ -90,8 +90,9 @@ function nodeShellExec() {
var prompt = function(choices, label, defaultchoice){
var choices = choices || [];
return this.prompter.ask(
`${label} \n ` + Object.keys(choices).map(choice => { return ` ${(+choice) + 1}) ${choices[choice]} `}).join('\n') + `\n default ( <= ${ defaultchoice || choices[0]} ) : `
`${label} \n` + Object.keys(choices).map(choice => { return ` ${(+choice) + 1}) ${choices[choice]} `}).join('\n') + `\n default ( <= ${ defaultchoice || choices[0]} ) : `
).then(choice => {
// propName = promptable.interpret(propValue)
if(!choice) return defaultchoice || choices[0];
if(choice && isNaN(+choice)) return choice;
return choices[(+choice) - 1];

+ 231
- 135
index.js View File

@@ -2069,12 +2069,14 @@ var hasElxr = function(path, options, cb) {
var hasElxrSync = function(path){ return hasElxr(path, { sync :true}); }

var detectfromroot = function(root){
return { root, node_env : path.basename(path.dirname(root)), instanceName : path.basename( path.dirname(path.dirname(root)) ) }
return { root, node_env : path.basename(root), instanceName : path.basename( path.dirname(root) ) }
}
var detectinstances = function () {
console.log(`launchpath = ${launchpath}`)
console.log(`thisscriptdir = ${thisscriptdir}`)

// PB : TODO -- !Postpone this.
console.log(`instanceroot = ${instanceroot}`) // Not yet confirmed...

// Note : Paths should already be normalized fefore this.
@@ -2088,25 +2090,41 @@ var detectinstances = function () {
// We need a reference to the root director for elxr cli to be properly oriented.
if (( elxrCliExists && path.normalize(launchpath + '/elxr')) === thisscriptdir) {
// We were run from the proper root with elxr cli in the subfolder.
console.log(`Instance Path : ${root}`)
instanceroot = root = launchpath;
instanceoptions.splice( 0, 0, dedetected = { root })
instanceoptions.splice( 0, 0, detected = { root })
instanceoptions.splice( 0, 0, detectfromroot(root))
}
else {
if (launchpath === thisscriptdir) {
if(path.normalize(launchpath + '/elxr') === thisscriptdir) {
// elxrCliExists is false -- and yet thiscriptdir is still proper.
// PB : TODO -- Maybe a warning / abort if for some reason this scriptdir should not be taken over...
console.error('Warning : detected thisscriptdir as elxr subfolder but not recognized as elixir. git updates might fail.')
instanceroot = root = launchpath;
instanceoptions.splice( 0, 0, detected = { root })
instanceoptions.splice( 0, 0, detectfromroot(root))
}
else if (launchpath === thisscriptdir) {
var parentHasElxr = hasElxrSync(launchpath + '/..')
// PB : TODO -- verify if we have .elxr folder in the parent...
if(!parentHasElxr) {
// ! thisscriptdir is not elxr.
console.error('Invalid run location in subfolder that looks like elxr. We should probably abort as elxr will not sync.')
}

// Same directory doesn't mean we are being run from elxr sub directory.
// In standalone build script we may or not be in the same location.
if (BUILD_VERSION.indexOf('Version: {version} - built on {date}') > -1) {
// Unbuilt therefore we are in the elxr directory.
// Unbuilt therefore we are in the elxr sub directory.

instanceroot = root = path.normalize(launchpath + '/..');
instanceoptions.splice( 0, 0, dedetected = { root })
instanceoptions.splice( 0, 0, detectfromroot(root))
instanceoptions.splice( 0, 0, detected = { root });
instanceoptions.splice( 0, 0, detectfromroot(root));
}
else {
// Built version.
// We could have been run from the elxr subfolder. Highly likely that the built version isn't the full elxr.
if(hasElxrSync(launchpath + '/..')) {
if(parentHasElxr) {
// Built version was run from the full elxr subfolder. Should work
// PB : TODO -- but we should switch to the full version...
instanceroot = root = path.normalize(launchpath + '/..');
@@ -2114,99 +2132,120 @@ var detectinstances = function () {
instanceoptions.splice( 0, 0, detectfromroot(root))
}
else {
instanceroot = root = launchpath;
instanceoptions.splice( 0, 0, detected = { root })
instanceoptions.splice( 0, 0, detectfromroot(root))
// Assume current launchpath is a new instance and create.
// Figure out the instnace name and environment from parent folders as an alternative option with confirmation if not provided in the arguments.

if(clioverrides.instanceName) {
if(clioverrides.node_env) {
instanceroot = root = path.normalize(launchpath + '/' + clioverrides.instanceName + '/' + clioverrides.node_env)
instanceoptions.splice( 0, 0, detected = { root, instanceName : clioverrides.instanceName, node_env : clioverrides.node_env })
// instanceoptions.splice( 0, 0, detectfromroot(root)) // This can be an option but is unnecessary unless a confirmation is provided.
// also folder names may have no relation to the actual instanceName and instanceType coz we need to have many
// eg : floder name can be elixir01 but instance name is elixr
}
else {
instanceroot = root = path.normalize(launchpath + '/' + clioverrides.instanceName + '/' + 'development')
instanceoptions.splice( 0, 0, detected = { root, instanceName : clioverrides.instanceName, node_env : 'development' })
instanceoptions.splice( 0, 0, detectfromroot(root)) // A recessive option only.
}
}
else {
instanceroot = root = launchpath;
if(clioverrides.node_env) {
instanceoptions.splice( 0, 0, detected = { root, node_env : clioverrides.node_env })
instanceoptions.splice( 0, 0, detectfromroot(root))
}
else {
// Nothing was specified... We only have one option from root.
instanceoptions.splice( 0, 0, detected = detectfromroot(launcpath))
}
}
// if(clioverrides.instanceName) {
// if(clioverrides.node_env) {
// instanceroot = root = path.normalize(launchpath + '/' + clioverrides.instanceName + '/' + clioverrides.node_env)
// instanceoptions.splice( 0, 0, detected = { root, instanceName : clioverrides.instanceName, node_env : clioverrides.node_env })
// // instanceoptions.splice( 0, 0, detectfromroot(root)) // This can be an option but is unnecessary unless a confirmation is provided.
// // also folder names may have no relation to the actual instanceName and instanceType coz we need to have many
// // eg : floder name can be elixir01 but instance name is elixr
// }
// else {
// instanceroot = root = path.normalize(launchpath + '/' + clioverrides.instanceName + '/' + 'development')
// instanceoptions.splice( 0, 0, detected = { root, instanceName : clioverrides.instanceName, node_env : 'development' })
// instanceoptions.splice( 0, 0, detectfromroot(root)) // A recessive option only.
// }
// }
// else {
// instanceroot = root = launchpath;
// if(clioverrides.node_env) {
// instanceoptions.splice( 0, 0, detected = { root, node_env : clioverrides.node_env })
// instanceoptions.splice( 0, 0, detectfromroot(root))
// }
// else {
// // Nothing was specified... We only have one option from root.
// instanceoptions.splice( 0, 0, detected = detectfromroot(launcpath))
// }
// }
}
}
}
else {
if(elxrCliExists) {
instanceroot = root = launchpath;
instanceoptions.splice( 0, 0, detected = { root })
instanceoptions.splice( 0, 0, detectfromroot(root))
}
}
}
instanceroot = detected.root
__default.root = root;
clioverrides.root = clioverrides.root || root;
// We can expect a .elxr at each level.
['' /* instanceroot */, '../' /* instanceTypes or node_env */, '../..' /* instanceNames */].
earlyreduce( ( value, p, i, a )=>{
var localinstancesPath = `${instanceroot}/${p}.elxr`;
if(existsSync( localinstancesPath )) {
try {
var chessinstances = acquirelocalinstances( { localinstancesPath } )
return Object.keys(chessinstances).earlyreduce( ( value, instanceName) => {
return Object.keys(chessinstances[instanceName]).earlyreduce( (value, instanceType) => {
if( path.normalize(chessinstances[instanceName][instanceType].root) === path.normalize( instanceroot) ) {
instanceoptions.splice( 0, 0, chessinstances[instanceName][instanceType])
return {
value : chessinstances[instanceName][instanceType]
, done : true
};
}
})
})
}
catch(e){
return { }
}
}
else return { }
}
)

// Resolves empty array when No known instances detected.
return Promise.resolve(instanceoptions)
})

}

var __interactve_promts = {
get reposerver(){
return cli.prompt(this.reposervers, 'git default repo').then(reposerver => {
Object.defineProperty(this, 'reposerver', {
value: reposerver,
writable: false,
configurable : true,
enumerable : true
});
return reposerver
})
}
, set reposerver(reposerver){
Object.defineProperty(this, 'reposerver', {
value: reposerver,
writable: false,
configurable : true,
enumerable : true
});
return reposerver
var __interactve_promts = function( target ){

return {
runchoice : {
label :
`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
: `
, choices : []
, defaultchoice : 'c'
, interpret : function(choice){
var interpret_map = {
d : function(){
processedArgs._[0] = 'i'
target.instanceName = processedArgs._[1] = processedArgs._[1] || 'chess'
target.node_env = processedArgs.node_env = (process.env.NODE_ENV && process.env.NODE_ENV.trim()) || processedArgs.node_env || 'development'
target.reposerver = 'https://git.bbh.org.in'
}
, n : function() { processedArgs._[0] = 'i' }
, i : function() { processedArgs._[0] = 'i' }
, c : async function() {
Object.defineProperty(this, 'cmd', getPromptableAsyncPropDescriptor('cmd', {
label : `Enter cmd :
p) pull
Default <= p
: `
, defaultchoice : 'pull'
}
));

var cmd = await target['cmd'];
if (!cmd || cmd === 'p') { target['cmd'] = processedArgs._[0] = 'pull' }
else target['cmd'] = processedArgs._[0] = cmd
return cmd;
}
, h : function() { console.log(elxr.help()); process.exit() } // PB : TODO -- Why do we need log.
, q : function() { process.exit() }
}
var __interpreter = interpret_map['c']
// if(!choice) return interpret_map['c']() // This should not happen prompter should always give us a default choice.
if(interpret_map[choice]) __interpreter = interpret_map[choice];
return __interpreter.call(target)
}
}
, instanceName : { label : `Enter Instance Name ( <= ${target.instanceName || 'chess'} ) : `, choices : [], defaultchoice : 'chess'}
, instanceType : { label : `Enter Instance Type ( <= ${target.instanceType || 'development'} ) : `, choices : [], defaultchoice : 'development'}
, reposerver : { label : `Enter Instance Name ( <= ${target.reposerver || 'https://git.bbh.org.in'} ) : `, choices : [], defaultchoice : 'https://git.bbh.org.in'}
}
}


var downloadsdir = '../Downloads';
var prerequisites = [
{
@@ -2442,28 +2481,35 @@ function verifyAndInstallPrerequisites() {
return Promise.all(downloadtasks).then(() => { return any(installtasks) })
}

var getPromptableAsyncPropDescriptor = function(propName, choices, defaultchoise){
var getPromptableAsyncPropDescriptor = function(propName, promptable){
return {
get (){
return cli.prompt( choices, propName, defaultchoise).then(propValue => {
Object.defineProperty(this, propName, {
value: propValue,
writable: false,
configurable : true,
enumerable : true
});
return propValue
return cli.prompt( promptable.choices, promptable.label, promptable.defaultchoice ).then(propValue => {
if(promptable.interpret){
return propName = promptable.interpret(propValue).then(
()=>{
Object.defineProperty(this, propName, {
value: propValue,
writable: true,
configurable : true,
enumerable : true
});
return propValue
}
)
}
else return propValue
})
}
// , set (propValue){
// Object.defineProperty(this, propName, {
// value: propValue,
// writable: false,
// configurable : true,
// enumerable : true
// })
// return propValue;
// }
, set (propValue){
Object.defineProperty(this, propName, {
value: propValue,
writable: true, // PB : TODO -- Use this to fix value permanently until run is over.
configurable : true,
enumerable : true
})
return propValue;
}
, configurable : true
, enumerable : true
}
@@ -2473,10 +2519,40 @@ var getPromptableAsyncPropDescriptor = function(propName, choices, defaultchoise


function acquirelocalinstances(selected){
// utils.assign is used to cleanup duplicates...
var chessinstances = utils.assign(require(path.normalize(selected.root + '/chessinstances.js')));
return chessinstances
}

function findlocalinstances(chessinstances, instanceoptions){
// We can expect a .elxr at each level.
['' /* instanceroot */, '../' /* instanceTypes or node_env */, '../..' /* instanceNames */].
earlyreduce( ( value, p, i, a )=>{
var localinstancesPath = `${instanceroot}/${p}.elxr`;
if(existsSync( localinstancesPath )) {
try {
var chessinstances = acquirelocalinstances( { localinstancesPath } )
return Object.keys(chessinstances).earlyreduce( ( value, instanceName) => {
return Object.keys(chessinstances[instanceName]).earlyreduce( (value, instanceType) => {
if( path.normalize(chessinstances[instanceName][instanceType].root) === path.normalize( instanceroot) ) {
instanceoptions.splice( 0, 0, chessinstances[instanceName][instanceType])
return {
value : chessinstances[instanceName][instanceType]
, done : true
};
}
})
})
}
catch(e){
return { }
}
}
else return { }
}
)
}


// function updateselection(selected) { selectedinstance = utils.assign(selectedinstance, selected) }
var selectedinstance = null;
@@ -2609,17 +2685,50 @@ acquireElevationState().then(() => {
}

return detectinstances().then((detectedinstanceoptions)=>{

var getchoices = function(){
detectedinstanceoptions.splice(0,0, __default)

var instances = []
var reposervers = [];
var instnaceNames = []
var instanceTypes = ['development', 'production'];
Object.keys( chessinstances).forEach(instanceName => {
if(instanceName === 'current_run') return;
Object.keys( chessinstances[instanceName] ).forEach(node_env=>{
var instance = chessinstances[instanceName][node_env];
reposervers = reposervers.concat(instance.reposervers)
if(instance.reposerver) reposervers.push(instance.reposerver)
instances.push(instance)
instanceTypes.push(instance.node_env)
instnaceNames.push(instance.instanceName)
})
})
instances = instances.concat(detectedinstanceoptions)

if(promptkeys['instanceName']) instnaceNames.push(selectedinstance['instanceName'])
if(promptkeys['instanceName']) instnaceNames.push(promptkeys['instanceName'])
if(selectedinstance['reposervers']) reposervers = reposervers.concat(selectedinstance['reposervers'])
var choices = {
'instanceName' : Array.from( new Set(instnaceNames) )
, 'reposerver' : Array.from( new Set(reposervers) )
, 'instanceType' : Array.from( new Set(instanceTypes) )
}

return choices;
}

// PB : TODO -- Most recent should be at the tip ! at index 0 so utils.reverseassign is required !!!
selectedinstance = utils.assign( ...detectedinstanceoptions.slice(-2) )
promptkeys = utils.assign(promptkeys, { 'instanceName' : clioverrides.instanceName, 'node_env' : clioverrides.node_env })
promptkeys = utils.assign(promptkeys, clioverrides)
if(clioverrides.reconfirm) {
var reconfirm = { 'instanceName' : selectedinstance['instanceName'] === 'chess' }
}
else { var reconfirm = {}; }

var prompts = [];

var eachPrompt = function(k, i, a){
// No local instances config found. We use a default initialized instance available in selectedinstance
@@ -2633,61 +2742,48 @@ acquireElevationState().then(() => {
// 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] ));
promptables[k].choices = choices[k]
Object.defineProperty(selectedinstance, k, getPromptableAsyncPropDescriptor(k, promptables[k]));
return await selectedinstance[k]
})
}
delete promptkeys[k]
}
try {
chessinstances = acquirelocalinstances(selectedinstance);
initinstances(selectedinstance)
findlocalinstances(chessinstances, detectedinstanceoptions)
var todo = Promise.resolve(true);
var instanceNameChoices = new Set(Object.keys( chessinstances) )
instanceNameChoices.delete('current_run')
instanceNameChoices.add(selectedinstance['instanceName'])
if(promptkeys['instanceName']) instanceNameChoices.add(promptkeys['instanceName'])
var choices = {
'instanceName' : Array.from(instanceNameChoices)
, 'reposerver' : selectedinstance['reposervers']
}
Object.keys(__interactve_promts).forEach(eachPrompt, __interactve_promts)
Object.keys(promptkeys).forEach(eachPrompt, promptkeys)
var choices = getchoices()

var promptables = __interactve_promts(selectedinstance)
Object.keys(promptables).forEach(eachPrompt, selectedinstance)
todo = any(prompts).then(()=>{ return selectedinstance })
var todo = Promise.resolve(true);

todo = any(prompts).then(()=>{
return initinstances(selectedinstance)
})
}
catch (e) {
// PB : TODO -- verbose mode warning.. console.warn(e) // Missing chessinstances is not an error...
initinstances(selectedinstance)
var instanceNameChoices = new Set(Object.keys( chessinstances) )
instanceNameChoices.delete('current_run')
instanceNameChoices.add(selectedinstance['instanceName'])
if(promptkeys['instanceName']) instanceNameChoices.add(promptkeys['instanceName'])
var choices = {
'instanceName' : Array.from(instanceNameChoices)
, 'reposerver' : selectedinstance['reposervers']
, 'instanceType' : ['development', 'production']
}
Object.keys(__interactve_promts).forEach(eachPrompt, __interactve_promts)
Object.keys(promptkeys).forEach(eachPrompt, promptkeys)
var choices = getchoices()

var promptables = __interactve_promts(selectedinstance)
Object.keys(promptables).forEach(eachPrompt, selectedinstance)
todo = any(prompts).then(()=>{ return selectedinstance })
todo = any(prompts).then(()=>{ return initinstances(selectedinstance) })
if(!processedArgs._[0] || !selectedinstance.node_env || !selectedinstance.instanceName){
// Weve been told what to do.
// Weve not been told what to do.
todo = todo.then(() => { return acquireChoices(selectedinstance) })
}
todo = todo.then(() => {
try {
chessinstances = acquirelocalinstances(selectedinstance)
findlocalinstances(chessinstances, detectedinstanceoptions)
detectedinstanceoptions.splice(0,0, __default)
initinstances(selectedinstance)
}
catch (e) {

Loading…
Cancel
Save